File::Value - routines to manipulate file name or content as a single value


 use File::Value;              # to import routines into a Perl script

 ($msg = file_value(">pid_file", $pid))  # Example: store a file value
         and die "pid_file: $msg\n";

 ($msg = file_value("<pid_file", $pid))  # Example: read a file value
         and die "pid_file: $msg\n";

 snag_dir($dirname);           # attempt to create directory $dirname
 snag_file($filename);         # attempt to create file $filename

 ($num, $name) =               # return number and name of node created
  snag_version(                # with lowest numbered available version
   $template, {                # base name with possible terminal digits
   as_dir => undef,            # create as a directory (default file)
   no_type_mismatch => 0,      # complain if mismatch (default don't)
   mknextcopy => 0 });         # copy unnumbered file (default don't)

 list_high_version($template); # report highest version found
 list_low_version($template);  # report lowest version found


These are general purpose routines that support the treatment of a file's contents or a file's name as a single data value.


file_value( $file, $value, $how, $maxlen )

Copy contents of file $file to or from a string $value, returning the empty string on success or an error message on failure. To copy $value to $file, start $file with ">" or ">>". To copy $file to $value, start $file with "<". The optional $how parameter qualifies how the copy will be done: "trim" (default) trims whitespace from either end, "untaint" removes various suspicious characters that might cause security problems (no guarantees), and "raw" does no processing at all. The optional parameter $maxlen dictates the maximum number of octets that will be copied. It defaults for sanity to 4GB; use a value of 0 (zero) to remove even that limit.

snag_file( $filename ), snag_dir( $dirname )

The snag_file() and snag_dir() routines try to create directory or file $node. Return "" on successful creation, "1" if $node already exists, else an error message. We check existence only if the creation attempt fails, which permits efficient handling of race conditions. For example,

        $msg = -e $node ? "1"           # either branch can return "1"
                : snag_file($node);     # race lost if this returns "1"
        if ($msg eq "1") {
                $fname = snag_version("$node.v1");

checks existence before calling us, avoiding subroutine and system call overhead, and it can rely on the "1" return to detect a lost race and take appropriate action. Generally returns strings, so the caller must check with string comparison (ne, eq instead of !=, ==).

snag_version( $template, $options )

Create the lowest numbered available version of a file or directory that is higher than the highest existing version, and return a two-element array containing the number and name of the node that was obtained, or (-1, $error_msg) on failure. The $options argument is an optional hash reference in which the following keys are recognized:

  as_dir           => undef,    # create node as dir (default is to
                                # infer it from $template if possible)
  no_type_mismatch => 0,        # complain if mismatch (default don't)
  mknextcopy       => 0,        # copy unnumbered file (default don't)

If no numbered version exists, the version that will be created will be a file, or a directory if as_dir is true (default is false unless the original node was a directory). If no_type_mismatch is true (default is false), it is an error if the type of the highest version (file or directory) is different from the requested type. If a race condition is detected, snag_version will try again with the next higher version number, but won't do this more than a few times. If mknextcopy is true (default false), the new node will receive a copy of the unnumbered file corresponding to $template (it is an error if it does not exist already as an unnumbered file).

The $template parameter is used to search for and create numbered versions. If it doesn't end in a digit substring ($digits), the digit "1" is assumed. The length of $digits determines the minimum width of the version number, zero-padded if necessary, and the value of $digits is the first version number to use if no numbered versions exist. Examples:

  $template="x";        # x1, x2, ..., x9, x10, ..., x101, ...
  $template="x.3";      # x.3, x.4, ..., x.101, x.102, ...
  $template="v001";     # v001, v002, ..., v101, ..., v1001, ...

One common use case calls for version numbers only when the file or directory that you want to create already exists. For $name="foo",

   if (($msg = snag_file($name)) eq 1)
        { ($n, $name) = snag_version("$name-"); }

creates foo, foo-1, foo-2, foo-3 etc. Another common use case calls for version numbers on every version. For example, if versions v996, v997, v998 currently exist, running

   ($n, $name) = snag_version("v001");

will then create v999, v1000, v1001, etc.

list_high_version( $template )

Return the number and name of the highest numbered existing version of $template (defaults to ""), where numbered versions have the form /$template\d+$/, eg, "v" for v001, v02, v3, or "foo.v" for foo.v1, ..., foo.v129 . Return (-1, undef) if no numbered versions exist.

list_low_version( $template )

Similar to list_high_version(), but return the number and name of the lowest numbered existing version.




John A. Kunze, jak at


Copyright 2009-2010 UC Regents. Open source BSD license.