From 9d4173f2716c2f9a2d26f8f9ab0f47b351b87de7 Mon Sep 17 00:00:00 2001 From: Lorry Tar Creator Date: Tue, 7 Jun 2011 08:06:16 +0000 Subject: File-Slurp-9999.19 --- Changes | 167 ++++++ MANIFEST | 39 ++ META.yml | 17 + Makefile.PL | 22 + README | 37 ++ TODO | 33 ++ extras/FileSlurp_12.pm | 260 ++++++++++ extras/slurp_article.pod | 743 +++++++++++++++++++++++++++ extras/slurp_bench.pl | 587 +++++++++++++++++++++ lib/File/Slurp.pm | 1261 ++++++++++++++++++++++++++++++++++++++++++++++ t/TestDriver.pm | 91 ++++ t/append_null.t | 24 + t/binmode.t | 50 ++ t/chomp.t | 53 ++ t/data_list.t | 62 +++ t/data_scalar.t | 62 +++ t/edit_file.t | 107 ++++ t/error.t | 125 +++++ t/error_mode.t | 59 +++ t/file_object.t | 75 +++ t/handle.t | 222 ++++++++ t/inode.t | 44 ++ t/large.t | 175 +++++++ t/newline.t | 52 ++ t/no_clobber.t | 26 + t/original.t | 55 ++ t/paragraph.t | 64 +++ t/perms.t | 31 ++ t/pod.t | 13 + t/pod_coverage.t | 24 + t/prepend_file.t | 74 +++ t/pseudo.t | 34 ++ t/read_dir.t | 66 +++ t/signal.t | 34 ++ t/slurp.t | 19 + t/stdin.t | 23 + t/stringify.t | 45 ++ t/tainted.t | 69 +++ t/write_file_win32.t | 29 ++ 39 files changed, 4973 insertions(+) create mode 100644 Changes create mode 100644 MANIFEST create mode 100644 META.yml create mode 100644 Makefile.PL create mode 100644 README create mode 100644 TODO create mode 100644 extras/FileSlurp_12.pm create mode 100644 extras/slurp_article.pod create mode 100755 extras/slurp_bench.pl create mode 100755 lib/File/Slurp.pm create mode 100644 t/TestDriver.pm create mode 100644 t/append_null.t create mode 100644 t/binmode.t create mode 100644 t/chomp.t create mode 100644 t/data_list.t create mode 100644 t/data_scalar.t create mode 100644 t/edit_file.t create mode 100644 t/error.t create mode 100644 t/error_mode.t create mode 100644 t/file_object.t create mode 100644 t/handle.t create mode 100644 t/inode.t create mode 100644 t/large.t create mode 100644 t/newline.t create mode 100644 t/no_clobber.t create mode 100644 t/original.t create mode 100644 t/paragraph.t create mode 100644 t/perms.t create mode 100644 t/pod.t create mode 100644 t/pod_coverage.t create mode 100644 t/prepend_file.t create mode 100644 t/pseudo.t create mode 100644 t/read_dir.t create mode 100644 t/signal.t create mode 100644 t/slurp.t create mode 100644 t/stdin.t create mode 100644 t/stringify.t create mode 100644 t/tainted.t create mode 100644 t/write_file_win32.t diff --git a/Changes b/Changes new file mode 100644 index 0000000..379f9c9 --- /dev/null +++ b/Changes @@ -0,0 +1,167 @@ +Revision history File::Slurp + +9999.19 Tue Jun 7 04:06:06 EDT 2011 + - Fixed use line in t/edit_file.t to import :edit first + Thanks to paul + - read_file and write_file work even when interrupted by signals + this includes a test for read_file interrupt + Thanks to Andrew Danforth + - Fixed bugs in the config synopsis example + +9999.18 Fri May 13 02:30:05 EDT 2011 + - Added :std and :edit export tags + - Cleaned up EXPORT vars + - Documented importing edit_file and edit_file_lines + - Fixed some pod spelling + +9999.17 Wed Apr 27 02:20:03 EDT 2011 + - Requiring Perl 5.6.2 (first time older Perls were dropped) + This is because of use of the re 'taint' pragma + - Added major new features: edit_file and edit_file_lines + - Speed up of tainted slurp with return of lines + - Added chomp option to read_file + - Added prefix option to read_dir + - Fixed optimization of reading small files. + +9999.16 Wed Apr 13 03:47:26 EDT 2011 + - Added support for read_file options to be a hash reference. + - Added support for read_dir options to be a hash reference. + - Added new feature prepend_file + - Fixed bug with array_ref in list context. was introduced by .15/.14 + Thanks to Norbert Gruener + - Cleaned up some pod + +9999.15 Thu Mar 24 16:40:19 EDT 2011 + - Fixed error.t test so it works when run as root + - Removed skip lines from error.t + - Fixed pod about binmode option to reflect changes in .14 + +9999.14 Sun Mar 20 16:26:47 EDT 2011 + - Added LICENCE (same as perl) to POD + - Added special faster code to slurp in small text files which + is a common case + - Rewrote the extras/slurp_bench.pl script. It has a full + legend, better CLI options, size is selectable, benchmark + entries have more consistant names and it compares the new + fast slurp for small files to the general slurp code. + Thanks to Mark Friendlich + - Cleaned up pod + - Added more Synopsis examples + - Added t/error.t to actually test error conditions. Previous + error.t was renamed to error_mode.t which better reflects its + tests. + - t/error.t uses a new test driver module. this may get used by + other tests in the future. + - Fixed check for SEEK_SET and other constant subs being defined + - Added support for binmode other than :raw and binmode.t test + Thanks to Martin J. Evans, Peter Edwards, Bryce Nesbitt + - Added support for perms option in write_file and perms.t test + Thanks to Peter Corlett and Paul Miller + - Added check to the rename call in atomic mode. Tested in error.t. + Thanks to Daniel Scott Sterling + - Added POD to state that using scalar_ref or buf_ref will be faster + and save memory due to not making a copy + Thanks to Peter Edwards + - read_file in list mode keeps data tainted + Thanks to Sébastien Aperghis-Tramoni + - read_file checks for an overloaded object to get the file + name. + Thanks to Sébastien Aperghis-Tramoni + +9999.13 Tue Oct 10 02:04:51 EDT 2006 + - Refactored the extras/slurp_bench.pl script. It has options, + a key the benchmarks, help and more benchmarks. + - Reordered changes so recent entries are first + - Added error check on atomic rename and test for it + Thanks to Daniel Scott Sterling + +9999.12 Thu Feb 2 02:26:31 EST 2006 + - Fixed bug on windows with classic slurping and File::Slurp not + agreeing on newline conversion. + - Added t/newline.t test to check for that fix. + - When passing text data by scalar reference to write_file under + windows, the buffer is copied so the newline conversion won't + modify the caller's data. + - Thanks to Johan Lodin for a test script which + I modified into t/newline.t + +9999.11 Fri Jan 20 01:24:00 EDT 2005 + - Quick release to remove code that forced the faked SEEK_* + values to be used. Showed up when tested on OSX which doesn't + need that backport. + +9999.10 Thu Jan 19 11:38:00 EDT 2005 + - t/*.t modules don't use Fcntl.pm + - using POSIX qw( :fcntl_h ) instead of Fcntl qw( :seek ) for + backwards compatiblity to 5.00503 + - added conditional definitions of SEEK_* and O_* subs as they are not + defined in perl 5.004 + - File::Slurp now runs on perl 5.004 and newer (see BUGS section) + All of the above thanks to Smylers , + Piers Kent and + John Alden + - Added pod.t and pod_coverage.t tests. This is to pass all + the CPANTS tests. + +9999.09 Tue Apr 19 01:21:55 EDT 2005 + - t/original.t and read_dir.t no longer search for tempdirs. they just + use the current dir which should be in the build directory + - t/readdir.t renamed to read_dir.t for consistancy + - write_file return values are docuemented + Thanks to Adam Kennedy + - added no_clobber option to write_file and t/no_clobber.t test for it + Thanks to + - fixed bug when appending a null string to a file which then + truncates it. seems to be an odd way for linux and OS X to + handle O_APPEND mode on sysopen. they don't seek to the end of + the file so it gets truncated. fixed by adding a seek to the + end if in append mode.n + Thanks to Chris Dolan + +9999.08 Sat Apr 16 01:01:27 EDT 2005 + - read_dir returns an array ref in scalar context + - read_dir keeps . and .. if keep_dot_dot option is set. + Thanks to John Alden + - slurp() is an optional exported alias to read_file + Thanks to Damian Conway + +9999.07 Tue Jan 25 01:33:11 EST 2005 + - Slurping in pseudo files (as in /proc) which show a size of 0 + but actually have data works. This seems to be the case on + linux but on Solaris those files show their proper size. + Thanks to Juerd Waalboer + +9999.06 Mon Sep 20 01:57:00 EDT 2004 + - Slurping the DATA handle now works without the workaround. + tests are in t/data_scalar.t and t/data_list.t + - Paragraph mode in read_file is supported. As with <> when $/ + (input record separator) is set to '', then the input file is + split on multiple newlines (/\n\n+/). + Thanks to Geoffrey Leach + +9999.05 Tue Feb 24 21:14:55 EST 2004 + - skip handle tests where socketpair is not supported (pre 5.8 + on windows) + Thanks to Mike Arms + +9999.04 Mon Feb 23 14:20:52 EST 2004 + - fixed DATA handle bug in t/handle.t (not seen on most OS's) + Thanks to James Willmore + +9999.03 Mon Dec 22 01:44:43 EST 2003 + - fixed DATA handle bugs in t/handle.t on osx (should be fixed + on BSD as well) + - added more comments to code + +9999.02 Wed Dec 17 03:40:49 EST 2003 + - skip DATA test in handle.t on OSX (bug in perl with sysread on DATA) + - changed checking if file handle from fileno to ref + from Randal Schwartz + - added support for atomic spewing + - added new test stdin.t for the fileno/ref change + - added new test inode.t to test atomic spewing + +9999.01 Mon Sep 1 00:20:56 2003 + - original version; created by h2xs 1.21 with options + -AX -n File::FastSlurp + diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..f4c0506 --- /dev/null +++ b/MANIFEST @@ -0,0 +1,39 @@ +Changes +lib/File/Slurp.pm +Makefile.PL +MANIFEST +README +TODO +t/TestDriver.pm +t/append_null.t +t/binmode.t +t/chomp.t +t/data_list.t +t/data_scalar.t +t/edit_file.t +t/error.t +t/error_mode.t +t/file_object.t +t/handle.t +t/inode.t +t/large.t +t/newline.t +t/no_clobber.t +t/original.t +t/paragraph.t +t/perms.t +t/pod.t +t/pod_coverage.t +t/prepend_file.t +t/pseudo.t +t/read_dir.t +t/signal.t +t/slurp.t +t/stdin.t +t/stringify.t +t/tainted.t +t/write_file_win32.t +extras/slurp_bench.pl +extras/FileSlurp_12.pm +extras/slurp_article.pod +META.yml Module meta-data (added by MakeMaker) diff --git a/META.yml b/META.yml new file mode 100644 index 0000000..2ab5ccf --- /dev/null +++ b/META.yml @@ -0,0 +1,17 @@ +--- #YAML:1.0 +name: File-Slurp +version: 9999.19 +abstract: Simple and Efficient Reading/Writing/Modifying of Complete Files +license: perl +author: + - Uri Guttman +generated_by: ExtUtils::MakeMaker version 6.42 +distribution_type: module +requires: + Carp: 0 + Exporter: 0 + Fcntl: 0 + POSIX: 0 +meta-spec: + url: http://module-build.sourceforge.net/META-spec-v1.3.html + version: 1.3 diff --git a/Makefile.PL b/Makefile.PL new file mode 100644 index 0000000..364dc45 --- /dev/null +++ b/Makefile.PL @@ -0,0 +1,22 @@ +use strict ; +use ExtUtils::MakeMaker; +# See lib/ExtUtils/MakeMaker.pm for details of how to influence +# the contents of the Makefile that is written. +WriteMakefile( + 'NAME' => 'File::Slurp', + 'LICENSE' => 'perl', + 'AUTHOR' => 'Uri Guttman ', + 'VERSION_FROM' => 'lib/File/Slurp.pm', + 'ABSTRACT_FROM' => 'lib/File/Slurp.pm', + 'META_MERGE' => { + requires => { + perl => 5.004, + }, + }, + 'PREREQ_PM' => { + 'Carp' => 0, + 'Exporter' => 0, + 'Fcntl' => 0, + 'POSIX' => 0, + }, +); diff --git a/README b/README new file mode 100644 index 0000000..c499b17 --- /dev/null +++ b/README @@ -0,0 +1,37 @@ +File::Slurp.pm +=========================== + +This module provides subroutines to read or write entire files with a +simple call. It also has a subroutine for reading the list of filenames +in a directory. + +In the extras/ directory you can read an article (slurp_article.pod) +about file slurping and also run a benchmark (slurp_bench.pl) that +compares many ways of slurping/spewing files. This benchmark was +rewritten for .14 and is much better. + +This module was first written and owned by David Muir Sharnoff (MUIR on +CPAN). I checked out his module and decided to write a new version +which would be faster and with many more features. To that end, David +graciously transfered the namespace to me. + +There have been some comments about the somewhat unusual version number. +The problem was that David used a future date (2004.0904) in his version +number, and the only way I could get CPAN to index my new module was to +make it have a version number higher than the old one, so I chose the +9999 prefix and appended the real revision number to it. + +INSTALLATION + +To install this module type the following: + + perl Makefile.PL + make + make test + make install + +COPYRIGHT AND LICENCE + +Copyright (C) 2010 Uri Guttman + +Licensed the same as Perl. diff --git a/TODO b/TODO new file mode 100644 index 0000000..9af7980 --- /dev/null +++ b/TODO @@ -0,0 +1,33 @@ + +File::Slurp TODO + +NEW FEATURES + +prepend_file() -- prepend text to the front of a file + + options: lock file? enable atomic + +edit_file() -- slurp into $_, call edit code block, write out $_ + + options: lock file? + +edit_file_lines() -- slurp each line into $_, call edit code block, + write out $_ + + options: lock file? + +read_file_lines() + reads lines to array ref or list + same as $list = read_file( $file, { array_ref => 1 } + or @lines = read_file() + +new options for read_dir + prepend -- prepend the dir name to each dir entry. + filter -- grep dir entries with qr// or code ref. + +BUGS: + +restart sysread/write after a signal (or check i/o count) + +FEATURE REQUESTS + diff --git a/extras/FileSlurp_12.pm b/extras/FileSlurp_12.pm new file mode 100644 index 0000000..5f24792 --- /dev/null +++ b/extras/FileSlurp_12.pm @@ -0,0 +1,260 @@ +package FileSlurp_12; + +use strict; + +use Carp ; +use Fcntl qw( :DEFAULT ) ; +use POSIX qw( :fcntl_h ) ; +use Symbol ; + +use base 'Exporter' ; +use vars qw( %EXPORT_TAGS @EXPORT_OK $VERSION @EXPORT ) ; + +%EXPORT_TAGS = ( 'all' => [ + qw( read_file write_file overwrite_file append_file read_dir ) ] ) ; + +@EXPORT = ( @{ $EXPORT_TAGS{'all'} } ); +@EXPORT_OK = qw( slurp ) ; + +$VERSION = '9999.13'; + +my $is_win32 = $^O =~ /win32/i ; + +# Install subs for various constants that aren't set in older perls +# (< 5.005). Fcntl on old perls uses Exporter to define subs without a +# () prototype These can't be overridden with the constant pragma or +# we get a prototype mismatch. Hence this less than aesthetically +# appealing BEGIN block: + +BEGIN { + unless( eval { defined SEEK_SET() } ) { + *SEEK_SET = sub { 0 }; + *SEEK_CUR = sub { 1 }; + *SEEK_END = sub { 2 }; + } + + unless( eval { defined O_BINARY() } ) { + *O_BINARY = sub { 0 }; + *O_RDONLY = sub { 0 }; + *O_WRONLY = sub { 1 }; + } + + unless ( eval { defined O_APPEND() } ) { + + if ( $^O =~ /olaris/ ) { + *O_APPEND = sub { 8 }; + *O_CREAT = sub { 256 }; + *O_EXCL = sub { 1024 }; + } + elsif ( $^O =~ /inux/ ) { + *O_APPEND = sub { 1024 }; + *O_CREAT = sub { 64 }; + *O_EXCL = sub { 128 }; + } + elsif ( $^O =~ /BSD/i ) { + *O_APPEND = sub { 8 }; + *O_CREAT = sub { 512 }; + *O_EXCL = sub { 2048 }; + } + } +} + +# print "OS [$^O]\n" ; + +# print "O_BINARY = ", O_BINARY(), "\n" ; +# print "O_RDONLY = ", O_RDONLY(), "\n" ; +# print "O_WRONLY = ", O_WRONLY(), "\n" ; +# print "O_APPEND = ", O_APPEND(), "\n" ; +# print "O_CREAT ", O_CREAT(), "\n" ; +# print "O_EXCL ", O_EXCL(), "\n" ; + + +*slurp = \&read_file ; + +sub read_file { + + my( $file_name, %args ) = @_ ; + +# set the buffer to either the passed in one or ours and init it to the null +# string + + my $buf ; + my $buf_ref = $args{'buf_ref'} || \$buf ; + ${$buf_ref} = '' ; + + my( $read_fh, $size_left, $blk_size ) ; + +# check if we are reading from a handle (glob ref or IO:: object) + + if ( ref $file_name ) { + +# slurping a handle so use it and don't open anything. +# set the block size so we know it is a handle and read that amount + + $read_fh = $file_name ; + $blk_size = $args{'blk_size'} || 1024 * 1024 ; + $size_left = $blk_size ; + +# DEEP DARK MAGIC. this checks the UNTAINT IO flag of a +# glob/handle. only the DATA handle is untainted (since it is from +# trusted data in the source file). this allows us to test if this is +# the DATA handle and then to do a sysseek to make sure it gets +# slurped correctly. on some systems, the buffered i/o pointer is not +# left at the same place as the fd pointer. this sysseek makes them +# the same so slurping with sysread will work. + + eval{ require B } ; + + if ( $@ ) { + + @_ = ( \%args, <IO->IoFLAGS & 16 ) { + +# set the seek position to the current tell. + + sysseek( $read_fh, tell( $read_fh ), SEEK_SET ) || + croak "sysseek $!" ; + } + } + else { + +# a regular file. set the sysopen mode + + my $mode = O_RDONLY ; + $mode |= O_BINARY if $args{'binmode'} ; + +#printf "RD: BINARY %x MODE %x\n", O_BINARY, $mode ; + +# open the file and handle any error + + $read_fh = gensym ; + unless ( sysopen( $read_fh, $file_name, $mode ) ) { + @_ = ( \%args, "read_file '$file_name' - sysopen: $!"); + goto &_error ; + } + +# get the size of the file for use in the read loop + + $size_left = -s $read_fh ; + + unless( $size_left ) { + + $blk_size = $args{'blk_size'} || 1024 * 1024 ; + $size_left = $blk_size ; + } + } + +# infinite read loop. we exit when we are done slurping + + while( 1 ) { + +# do the read and see how much we got + + my $read_cnt = sysread( $read_fh, ${$buf_ref}, + $size_left, length ${$buf_ref} ) ; + + if ( defined $read_cnt ) { + +# good read. see if we hit EOF (nothing left to read) + + last if $read_cnt == 0 ; + +# loop if we are slurping a handle. we don't track $size_left then. + + next if $blk_size ; + +# count down how much we read and loop if we have more to read. + $size_left -= $read_cnt ; + last if $size_left <= 0 ; + next ; + } + +# handle the read error + + @_ = ( \%args, "read_file '$file_name' - sysread: $!"); + goto &_error ; + } + +# fix up cr/lf to be a newline if this is a windows text file + + ${$buf_ref} =~ s/\015\012/\n/g if $is_win32 && !$args{'binmode'} ; + +# this is the 5 returns in a row. each handles one possible +# combination of caller context and requested return type + + my $sep = $/ ; + $sep = '\n\n+' if defined $sep && $sep eq '' ; + +# caller wants to get an array ref of lines + +# this split doesn't work since it tries to use variable length lookbehind +# the m// line works. +# return [ split( m|(?<=$sep)|, ${$buf_ref} ) ] if $args{'array_ref'} ; + return [ length(${$buf_ref}) ? ${$buf_ref} =~ /(.*?$sep|.+)/sg : () ] + if $args{'array_ref'} ; + +# caller wants a list of lines (normal list context) + +# same problem with this split as before. +# return split( m|(?<=$sep)|, ${$buf_ref} ) if wantarray ; + return length(${$buf_ref}) ? ${$buf_ref} =~ /(.*?$sep|.+)/sg : () + if wantarray ; + +# caller wants a scalar ref to the slurped text + + return $buf_ref if $args{'scalar_ref'} ; + +# caller wants a scalar with the slurped text (normal scalar context) + + return ${$buf_ref} if defined wantarray ; + +# caller passed in an i/o buffer by reference (normal void context) + + return ; +} + + +# error handling section +# +# all the error handling uses magic goto so the caller will get the +# error message as if from their code and not this module. if we just +# did a call on the error code, the carp/croak would report it from +# this module since the error sub is one level down on the call stack +# from read_file/write_file/read_dir. + + +my %err_func = ( + 'carp' => \&carp, + 'croak' => \&croak, +) ; + +sub _error { + + my( $args, $err_msg ) = @_ ; + +# get the error function to use + + my $func = $err_func{ $args->{'err_mode'} || 'croak' } ; + +# if we didn't find it in our error function hash, they must have set +# it to quiet and we don't do anything. + + return unless $func ; + +# call the carp/croak function + + $func->($err_msg) ; + +# return a hard undef (in list context this will be a single value of +# undef which is not a legal in-band value) + + return undef ; +} + +1; diff --git a/extras/slurp_article.pod b/extras/slurp_article.pod new file mode 100644 index 0000000..8b000f7 --- /dev/null +++ b/extras/slurp_article.pod @@ -0,0 +1,743 @@ +=head1 Perl Slurp Ease + +=head2 Introduction + + +One of the common Perl idioms is processing text files line by line: + + while( ) { + do something with $_ + } + +This idiom has several variants, but the key point is that it reads in +only one line from the file in each loop iteration. This has several +advantages, including limiting memory use to one line, the ability to +handle any size file (including data piped in via STDIN), and it is +easily taught and understood to Perl newbies. In fact newbies are the +ones who do silly things like this: + + while( ) { + push @lines, $_ ; + } + + foreach ( @lines ) { + do something with $_ + } + +Line by line processing is fine, but it isn't the only way to deal with +reading files. The other common style is reading the entire file into a +scalar or array, and that is commonly known as slurping. Now, slurping has +somewhat of a poor reputation, and this article is an attempt at +rehabilitating it. Slurping files has advantages and limitations, and is +not something you should just do when line by line processing is fine. +It is best when you need the entire file in memory for processing all at +once. Slurping with in memory processing can be faster and lead to +simpler code than line by line if done properly. + +The biggest issue to watch for with slurping is file size. Slurping very +large files or unknown amounts of data from STDIN can be disastrous to +your memory usage and cause swap disk thrashing. You can slurp STDIN if +you know that you can handle the maximum size input without +detrimentally affecting your memory usage. So I advocate slurping only +disk files and only when you know their size is reasonable and you have +a real reason to process the file as a whole. Note that reasonable size +these days is larger than the bad old days of limited RAM. Slurping in a +megabyte is not an issue on most systems. But most of the +files I tend to slurp in are much smaller than that. Typical files that +work well with slurping are configuration files, (mini-)language scripts, +some data (especially binary) files, and other files of known sizes +which need fast processing. + +Another major win for slurping over line by line is speed. Perl's IO +system (like many others) is slow. Calling C<< <> >> for each line +requires a check for the end of line, checks for EOF, copying a line, +munging the internal handle structure, etc. Plenty of work for each line +read in. Whereas slurping, if done correctly, will usually involve only +one I/O call and no extra data copying. The same is true for writing +files to disk, and we will cover that as well (even though the term +slurping is traditionally a read operation, I use the term ``slurp'' for +the concept of doing I/O with an entire file in one operation). + +Finally, when you have slurped the entire file into memory, you can do +operations on the data that are not possible or easily done with line by +line processing. These include global search/replace (without regard for +newlines), grabbing all matches with one call of C, complex parsing +(which in many cases must ignore newlines), processing *ML (where line +endings are just white space) and performing complex transformations +such as template expansion. + +=head2 Global Operations + +Here are some simple global operations that can be done quickly and +easily on an entire file that has been slurped in. They could also be +done with line by line processing but that would be slower and require +more code. + +A common problem is reading in a file with key/value pairs. There are +modules which do this but who needs them for simple formats? Just slurp +in the file and do a single parse to grab all the key/value pairs. + + my $text = read_file( $file ) ; + my %config = $text =~ /^(\w+)=(.+)$/mg ; + +That matches a key which starts a line (anywhere inside the string +because of the C modifier), the '=' char and the text to the end of the +line (again, C makes that work). In fact the ending C<$> is not even needed +since C<.> will not normally match a newline. Since the key and value are +grabbed and the C is in list context with the C modifier, it will +grab all key/value pairs and return them. The C<%config>hash will be +assigned this list and now you have the file fully parsed into a hash. + +Various projects I have worked on needed some simple templating and I +wasn't in the mood to use a full module (please, no flames about your +favorite template module :-). So I rolled my own by slurping in the +template file, setting up a template hash and doing this one line: + + $text =~ s/<%(.+?)%>/$template{$1}/g ; + +That only works if the entire file was slurped in. With a little +extra work it can handle chunks of text to be expanded: + + $text =~ s/<%(\w+)_START%>(.+?)<%\1_END%>/ template($1, $2)/sge ; + +Just supply a C