"Fossies" - the Fresh Open Source Software Archive

Member "install-tl-20200916/tlpkg/TeXLive/TLConfFile.pm" (4 Jun 2017, 21326 Bytes) of package /windows/misc/install-tl.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Perl source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 # $Id: TLConfFile.pm 44447 2017-06-04 02:03:31Z preining $
    2 # TeXLive::TLConfFile.pm - reading and writing conf files
    3 # Copyright 2010-2017 Norbert Preining
    4 # This file is licensed under the GNU General Public License version 2
    5 # or any later version.
    6 
    7 package TeXLive::TLConfFile;
    8 
    9 use TeXLive::TLUtils;
   10 
   11 my $svnrev = '$Revision: 44447 $';
   12 my $_modulerevision;
   13 if ($svnrev =~ m/: ([0-9]+) /) {
   14   $_modulerevision = $1;
   15 } else {
   16   $_modulerevision = "unknown";
   17 }
   18 sub module_revision {
   19   return $_modulerevision;
   20 }
   21 
   22 sub new
   23 {
   24   my $class = shift;
   25   my ($fn, $cc, $sep, $typ) = @_;
   26   my $self = {} ;
   27   $self{'file'} = $fn;
   28   $self{'cc'} = $cc;
   29   $self{'sep'} = $sep;
   30   if (defined($typ)) {
   31     if ($typ eq 'last-win' || $typ eq 'first-win' || $typ eq 'multiple') {
   32       $self{'type'} = $typ;
   33     } else {
   34       printf STDERR "Unknown type of conffile: $typ\n";
   35       printf STDERR "Should be one of: last-win first-win multiple\n";
   36       return;
   37     }
   38   } else {
   39     # default type for backward compatibility is last-win
   40     $self{'type'} = 'last-win';
   41   }
   42   bless $self, $class;
   43   return $self->reparse;
   44 }
   45 
   46 sub reparse
   47 {
   48   my $self = shift;
   49   my %config = parse_config_file($self->file, $self->cc, $self->sep);
   50   my $lastkey = undef;
   51   my $lastkeyline = undef;
   52   $self{'keyvalue'} = ();
   53   $self{'confdata'} = \%config;
   54   $self{'changed'} = 0;
   55   my $in_postcomment = 0;
   56   for my $i (0..$config{'lines'}) {
   57     if ($config{$i}{'type'} eq 'comment') {
   58       $lastkey = undef;
   59       $lastkeyline = undef;
   60       $is_postcomment = 0;
   61     } elsif ($config{$i}{'type'} eq 'data') {
   62       $lastkey = $config{$i}{'key'};
   63       $lastkeyline = $i;
   64       $self{'keyvalue'}{$lastkey}{$i}{'value'} = $config{$i}{'value'};
   65       $self{'keyvalue'}{$lastkey}{$i}{'status'} = 'unchanged';
   66       if (defined($config{$i}{'postcomment'})) {
   67         $in_postcomment = 1;
   68       } else {
   69         $in_postcomment = 0;
   70       }
   71     } elsif ($config{$i}{'type'} eq 'empty') {
   72       $lastkey = undef;
   73       $lastkeyline = undef;
   74       $is_postcomment = 0;
   75     } elsif ($config{$i}{'type'} eq 'continuation') {
   76       if (defined($lastkey)) {
   77         if (!$in_postcomment) {
   78           $self{'keyvalue'}{$lastkey}{$lastkeyline}{'value'} .= 
   79             $config{$i}{'value'};
   80         }
   81       }
   82       # otherwise we are in a continuation of a comment!!! so nothing to do
   83     } else {
   84       print "-- UNKNOWN TYPE\n";
   85     }
   86   }
   87   return $self;
   88 }
   89 
   90 sub file
   91 {
   92   my $self = shift;
   93   return($self{'file'});
   94 }
   95 sub cc
   96 {
   97   my $self = shift;
   98   return($self{'cc'});
   99 }
  100 sub sep
  101 {
  102   my $self = shift;
  103   return($self{'sep'});
  104 }
  105 sub type
  106 {
  107   my $self = shift;
  108   return($self{'type'});
  109 }
  110 
  111 sub key_present
  112 {
  113   my ($self, $key) = @_;
  114   return defined($self{'keyvalue'}{$key});
  115 }
  116 
  117 sub keys
  118 {
  119   my $self = shift;
  120   return keys(%{$self{'keyvalue'}});
  121 }
  122 
  123 sub keyvaluehash
  124 {
  125   my $self = shift;
  126   return \%{$self{'keyvalue'}};
  127 }
  128 sub confdatahash
  129 {
  130   my $self = shift;
  131   return $self{'confdata'};
  132 }
  133 
  134 sub by_lnr
  135 {
  136   # order of lines
  137   # first all the line numbers >= 0,
  138   # then the negative line numbers in reverse order
  139   # (negative line numbers refer to new entries in the conffile)
  140   # example: 
  141   # line number in order: 0 3 6 7 9 -1 -2 -3
  142   return ($a >= 0 && $b >= 0 ? $a <=> $b : $b <=> $a);
  143 }
  144 
  145 sub value
  146 {
  147   my ($self, $key, $value, @restvals) = @_;
  148   my $t = $self->type;
  149   if (defined($value)) {
  150     if (defined($self{'keyvalue'}{$key})) {
  151       my @key_lines = sort by_lnr CORE::keys %{$self{'keyvalue'}{$key}};
  152       if ($t eq 'multiple') {
  153         my @newval = ( $value, @restvals );
  154         my $newlen = $#newval;
  155         # in case of assigning to a multiple value stuff,
  156         # we assign to the first n elements, delete superficial
  157         # or add new ones if necessary
  158         # $value should be a reference to an array of values
  159         my $listp = $self{'keyvalue'}{$key};
  160         my $oldlen = $#key_lines;
  161         my $minlen = ($newlen < $oldlen ? $newlen : $oldlen);
  162         for my $i (0..$minlen) {
  163           if ($listp->{$key_lines[$i]}{'value'} ne $newval[$i]) {
  164             $listp->{$key_lines[$i]}{'value'} = $newval[$i];
  165             if ($listp->{$key_lines[$i]}{'status'} ne 'new') {
  166               $listp->{$key_lines[$i]}{'status'} = 'changed';
  167             }
  168             $self{'changed'} = 1;
  169           }
  170         }
  171         if ($minlen < $oldlen) {
  172           # we are assigning less values to more lines, so we have to
  173           # remove the remaining ones
  174           for my $i (($minlen+1)..$oldlen) {
  175             $listp->{$key_lines[$i]}{'status'} = 'deleted';
  176           }
  177           $self{'changed'} = 1;
  178         }
  179         if ($minlen < $newlen) {
  180           # we have new values
  181           my $ll = $key_lines[$#key_lines];
  182           # if we are adding the first new entry, set line to -1,
  183           # otherwise decrease the line number (already negative
  184           # for new lines)
  185           $ll = ($ll >= 0 ? -1 : $ll-1);
  186           for my $i (($minlen+1)..$newlen) {
  187             $listp->{$ll}{'status'} = 'new';
  188             $listp->{$ll}{'value'} = $newval[$i];
  189             $ll--;
  190           }
  191           $self{'changed'} = 1;
  192         }
  193       } else {
  194         # select element based on first-win or last-win type
  195         my $ll = $key_lines[($t eq 'first-win' ? 0 : $#key_lines)];
  196         #print "lastwin = $ll\n";
  197         if ($self{'keyvalue'}{$key}{$ll}{'value'} ne $value) {
  198           $self{'keyvalue'}{$key}{$ll}{'value'} = $value;
  199           # as long as the key/value pair is not new,
  200           # we set its status to changed
  201           if ($self{'keyvalue'}{$key}{$ll}{'status'} ne 'new') {
  202             $self{'keyvalue'}{$key}{$ll}{'status'} = 'changed';
  203           }
  204           $self{'changed'} = 1;
  205         }
  206       }
  207     } else { # all new key
  208       my @newval = ( $value, @restvals );
  209       my $newlen = $#newval;
  210       for my $i (0..$newlen) {
  211         $self{'keyvalue'}{$key}{-($i+1)}{'value'} = $value;
  212         $self{'keyvalue'}{$key}{-($i+1)}{'status'} = 'new';
  213       }
  214       $self{'changed'} = 1;
  215     }
  216   }
  217   # $self->dump_myself();
  218   if (defined($self{'keyvalue'}{$key})) {
  219     my @key_lines = sort by_lnr CORE::keys %{$self{'keyvalue'}{$key}};
  220     if ($t eq 'first-win') {
  221       return $self{'keyvalue'}{$key}{$key_lines[0]}{'value'};
  222     } elsif ($t eq 'last-win') {
  223       return $self{'keyvalue'}{$key}{$key_lines[$#key_lines]}{'value'};
  224     } elsif ($t eq 'multiple') {
  225       return map { $self{'keyvalue'}{$key}{$_}{'value'} } @key_lines;
  226     } else {
  227       die "That should not happen: wrong type: $!";
  228     }
  229   }
  230   return;
  231 }
  232 
  233 sub delete_key
  234 {
  235   my ($self, $key) = @_;
  236   %config = %{$self{'confdata'}};
  237   if (defined($self{'keyvalue'}{$key})) {
  238     for my $l (CORE::keys %{$self{'keyvalue'}{$key}}) {
  239       $self{'keyvalue'}{$key}{$l}{'status'} = 'deleted';
  240     }
  241     $self{'changed'} = 1;
  242   }
  243 }
  244 
  245 sub rename_key
  246 {
  247   my ($self, $oldkey, $newkey) = @_;
  248   %config = %{$self{'confdata'}};
  249   for my $i (0..$config{'lines'}) {
  250     if (($config{$i}{'type'} eq 'data') &&
  251         ($config{$i}{'key'} eq $oldkey)) {
  252       $config{$i}{'key'} = $newkey;
  253       $self{'changed'} = 1;
  254     }
  255   }
  256   if (defined($self{'keyvalue'}{$oldkey})) {
  257     $self{'keyvalue'}{$newkey} = $self{'keyvalue'}{$oldkey};
  258     delete $self{'keyvalue'}{$oldkey};
  259     $self{'keyvalue'}{$newkey}{'status'} = 'changed';
  260     $self{'changed'} = 1;
  261   }
  262 }
  263 
  264 sub is_changed
  265 {
  266   my $self = shift;
  267   return $self{'changed'};
  268 }
  269 
  270 sub save
  271 {
  272   my $self = shift;
  273   my $outarg = shift;
  274   my $closeit = 0;
  275   # unless $outarg is defined or we are changed, return immediately
  276   return if (! ( defined($outarg) || $self->is_changed));
  277   #
  278   %config = %{$self{'confdata'}};
  279   #
  280   # determine where to write to
  281   my $out = $outarg;
  282   my $fhout;
  283   if (!defined($out)) {
  284     $out = $config{'file'};
  285     my $dn = TeXLive::TLUtils::dirname($out);
  286     TeXLive::TLUtils::mkdirhier($dn);
  287     if (!open(CFG, ">$out")) {
  288       tlwarn("Cannot write to $out: $!\n");
  289       return 0;
  290     }
  291     $closeit = 1;
  292     $fhout = \*CFG;
  293   } else {
  294     # check what we got there for $out
  295     if (ref($out) eq 'SCALAR') {
  296       # that is a file name
  297       my $dn = TeXLive::TLUtils::dirname($out);
  298       TeXLive::TLUtils::mkdirhier($dn);
  299       if (!open(CFG, ">$out")) {
  300         tlwarn("Cannot write to $out: $!\n");
  301         return 0;
  302       }
  303       $fhout = \*CFG;
  304       $closeit = 1;
  305     } elsif (ref($out) eq 'GLOB') {
  306       # that hopefully is a fh
  307       $fhout = $out;
  308     } else {
  309       tlwarn("Unknown out argument $out\n");
  310       return 0;
  311     }
  312   }
  313     
  314   #
  315   # first we write the config file as close as possible to orginal layout,
  316   # and after that we add new key/value pairs
  317   my $current_key_value_is_changed = 0;
  318   for my $i (0..$config{'lines'}) {
  319     if ($config{$i}{'type'} eq 'comment') {
  320       print $fhout "$config{$i}{'value'}";
  321       print $fhout ($config{$i}{'multiline'} ? "\\\n" : "\n");
  322     } elsif ($config{$i}{'type'} eq 'empty') {
  323       print $fhout ($config{$i}{'multiline'} ? "\\\n" : "\n");
  324     } elsif ($config{$i}{'type'} eq 'data') {
  325       $current_key_value_is_changed = 0;
  326       # we have to check whether the original data has been changed!!
  327       if ($self{'keyvalue'}{$config{$i}{'key'}}{$i}{'status'} eq 'changed') {
  328         $current_key_value_is_changed = 1;
  329         print $fhout "$config{$i}{'key'} $config{'sep'} $self{'keyvalue'}{$config{$i}{'key'}}{$i}{'value'}";
  330         if (defined($config{$i}{'postcomment'})) {
  331           print $fhout $config{$i}{'postcomment'};
  332         }
  333         # if a value is changed, we do not print out multiline stuff
  334         # as keys are not split
  335         print $fhout "\n";
  336       } elsif ($self{'keyvalue'}{$config{$i}{'key'}}{$i}{'status'} eq 'deleted') {
  337         $current_key_value_is_changed = 1;
  338       } else {
  339         $current_key_value_is_changed = 0;
  340         # the original already contains the final \, so only print new line
  341         print $fhout "$config{$i}{'original'}\n";
  342       }
  343     } elsif ($config{$i}{'type'} eq 'continuation') {
  344       if ($current_key_value_is_changed) {
  345         # ignore continuation lines if values are changed
  346       } else {
  347         print $fhout "$config{$i}{'value'}";
  348         print $fhout ($config{$i}{'multiline'} ? "\\\n" : "\n");
  349       }
  350     }
  351   }
  352   #
  353   # save new keys
  354   for my $k (CORE::keys %{$self{'keyvalue'}}) {
  355     for my $l (CORE::keys %{$self{'keyvalue'}{$k}}) {
  356       if ($self{'keyvalue'}{$k}{$l}{'status'} eq 'new') {
  357         print $fhout "$k $config{'sep'} $self{'keyvalue'}{$k}{$l}{'value'}\n";
  358       }
  359     }
  360   }
  361   close $fhout if $closeit;
  362   #
  363   # reparse myself
  364   if (!defined($outarg)) {
  365     $self->reparse;
  366   }
  367 }
  368 
  369 
  370 
  371 
  372 #
  373 # parse/write config file
  374 # these functions allow reading and writing of config files
  375 # that consists of comments (comment char/string is the second argument)
  376 # and pairs
  377 #   \s* key \s* SEP \s* value \s*
  378 # where SEP is the third argument,
  379 # and key does not contain neither white space nor SEP
  380 # and value can be arbitry
  381 #
  382 # continuation lines are allowed
  383 # Furthermore, at least the separator has to be on the same line as the key!!
  384 # Continuations followed by comment lines are invalid!
  385 #
  386 sub parse_config_file {
  387   my ($file, $cc, $sep) = @_;
  388   my @data;
  389   if (!open(CFG, "<$file")) {
  390     @data = ();
  391   } else {
  392     @data = <CFG>;
  393     chomp(@data);
  394     close(CFG);
  395   }
  396 
  397   my %config = ();
  398   $config{'file'} = $file;
  399   $config{'cc'} = $cc;
  400   $config{'sep'} = $sep;
  401 
  402   my $lines = $#data;
  403   my $cont_running = 0;
  404   for my $l (0..$lines) {
  405     $config{$l}{'original'} = $data[$l];
  406     if ($cont_running) {
  407       if ($data[$l] =~ m/^(.*)\\$/) {
  408         $config{$l}{'type'} = 'continuation';
  409         $config{$l}{'multiline'} = 1;
  410         $config{$l}{'value'} = $1;
  411         next;
  412       } else {
  413         # last line of a continuation
  414         # do nothing, we will finish here
  415         $config{$l}{'type'} = 'continuation';
  416         $config{$l}{'value'} = $data[$l];
  417         $cont_running = 0;
  418         next;
  419       }
  420     }
  421     # ignore continuation after comments, that is the behaviour the
  422     # kpathsea library is using, so we follow it here
  423     if ($data[$l] =~ m/$cc/) {
  424       $data[$l] =~ s/\\$//;
  425     }
  426     # continuation line
  427     if ($data[$l] =~ m/^(.*)\\$/) {
  428       $cont_running = 1;
  429       $config{$l}{'multiline'} = 1;
  430       # remove the continuation marker so that we can do everything
  431       # as normal below
  432       $data[$l] =~ s/\\$//;
  433       # we will continue below
  434     }
  435     # from now on, if $cont_running == 1, then it means that
  436     # we are in the FIRST line of a multi line setting, so evaluate
  437     # it accordingly to get the key if necessary
  438 
  439     # empty lines are treated as comments
  440     if ($data[$l] =~ m/^\s*$/) {
  441       $config{$l}{'type'} = 'empty';
  442       next;
  443     }
  444     if ($data[$l] =~ m/^\s*$cc/) {
  445       # save the full line as is into the config hash
  446       $config{$l}{'type'} = 'comment';
  447       $config{$l}{'value'} = $data[$l];
  448       next;
  449     }
  450     # mind that the .*? is making the .* NOT greedy, ie matching as few as
  451     # possible. That way we can get rid of the comments at the end of lines
  452     if ($data[$l] =~ m/^\s*([^\s$sep]+)\s*$sep\s*(.*?)(\s*)?($cc.*)?$/) {
  453       $config{$l}{'type'} = 'data';
  454       $config{$l}{'key'} = $1;
  455       $config{$l}{'value'} = $2;
  456       if (defined($3)) {
  457         my $postcomment = $3;
  458         if (defined($4)) {
  459           $postcomment .= $4;
  460         }
  461         # check that there is actually a comment in the second part of the
  462         # line. Otherwise we might add the continuation lines of that
  463         # line to the value
  464         if ($postcomment =~ m/$cc/) {
  465           $config{$l}{'postcomment'} = $postcomment;
  466         }
  467       }
  468       next;
  469     }
  470     # if we are still here, that means we cannot evaluate the config file
  471     # give a BIG FAT WARNING but save the line as comment and continue 
  472     # anyway
  473     warn("WARNING WARNING WARNING\n");
  474     warn("Cannot parse config file $file ($cc, $sep)\n");
  475     warn("The following line (l.$l) seems to be wrong:\n");
  476     warn(">>> $data[$l]\n");
  477     warn("We will treat this line as a comment!\n");
  478     $config{$l}{'type'} = 'comment';
  479     $config{$l}{'value'} = $data[$l];
  480   }
  481   # save the number of lines in the config hash
  482   $config{'lines'} = $lines;
  483   #print "====DEBUG dumping config ====\n";
  484   #dump_config_data(\%config);
  485   #print "====DEBUG writing config ====\n";
  486   #write_config_file(\%config);
  487   #print "=============================\n";
  488   return %config;
  489 }
  490 
  491 sub dump_myself {
  492   my $self = shift;
  493   print "======== DUMPING SELF =============\n";
  494   dump_config_data($self{'confdata'});
  495   print "DUMPING KEY VALUES\n";
  496   for my $k (CORE::keys %{$self{'keyvalue'}}) {
  497     print "key = $k\n";
  498     for my $l (sort CORE::keys %{$self{'keyvalue'}{$k}}) {
  499       print "  line =$l= value =", $self{'keyvalue'}{$k}{$l}{'value'}, "= status =", $self{'keyvalue'}{$k}{$l}{'status'}, "=\n";
  500     }
  501   }
  502   print "=========== END DUMP ==============\n";
  503 }
  504 
  505 sub dump_config_data {
  506   my $foo = shift;
  507   my %config = %{$foo};
  508   print "config file name: $config{'file'}\n";
  509   print "config comment char: $config{'cc'}\n";
  510   print "config separator: $config{'sep'}\n";
  511   print "config lines: $config{'lines'}\n";
  512   for my $i (0..$config{'lines'}) {
  513     print "line ", $i+1, ": $config{$i}{'type'}";
  514     if ($config{$i}{'type'} eq 'comment') {
  515       print "\nCOMMENT = $config{$i}{'value'}\n";
  516     } elsif ($config{$i}{'type'} eq 'data') {
  517       print "\nKEY = $config{$i}{'key'}\nVALUE = $config{$i}{'value'}\n";
  518       print "MULTLINE = ", ($config{$i}{'multiline'} ? "1" : "0"), "\n";
  519     } elsif ($config{$i}{'type'} eq 'empty') {
  520       print "\n";
  521       # do nothing
  522     } elsif ($config{$i}{'type'} eq 'continuation') {
  523       print "\nVALUE = $config{$i}{'value'}\n";
  524       print "MULTLINE = ", ($config{$i}{'multiline'} ? "1" : "0"), "\n";
  525     } else {
  526       print "-- UNKNOWN TYPE\n";
  527     }
  528   }
  529 }
  530       
  531 sub write_config_file {
  532   my $foo = shift;
  533   my %config = %{$foo};
  534   for my $i (0..$config{'lines'}) {
  535     if ($config{$i}{'type'} eq 'comment') {
  536       print "$config{$i}{'value'}";
  537       print ($config{$i}{'multiline'} ? "\\\n" : "\n");
  538     } elsif ($config{$i}{'type'} eq 'data') {
  539       print "$config{$i}{'key'} $config{'sep'} $config{$i}{'value'}";
  540       if ($config{$i}{'multiline'}) {
  541         print "\\";
  542       }
  543       print "\n";
  544     } elsif ($config{$i}{'type'} eq 'empty') {
  545       print ($config{$i}{'multiline'} ? "\\\n" : "\n");
  546     } elsif ($config{$i}{'type'} eq 'continuation') {
  547       print "$config{$i}{'value'}";
  548       print ($config{$i}{'multiline'} ? "\\\n" : "\n");
  549     } else {
  550       print STDERR "-- UNKNOWN TYPE\n";
  551     }
  552   }
  553 }
  554 
  555 
  556 1;
  557 __END__
  558 
  559 
  560 =head1 NAME
  561 
  562 C<TeXLive::TLConfFile> -- TeX Live Config File Access Module
  563 
  564 =head1 SYNOPSIS
  565 
  566   use TeXLive::TLConfFile;
  567 
  568   $conffile = TeXLive::TLConfFile->new($file_name, $comment_char, $separator, $type);
  569   $conffile->file;
  570   $conffile->cc;
  571   $conffile->sep;
  572   $conffile->type
  573   $conffile->key_present($key);
  574   $conffile->keys;
  575   $conffile->value($key [, $value, ...]);
  576   $conffile->is_changed;
  577   $conffile->save;
  578   $conffile->reparse;
  579 
  580 =head1 DESCRIPTION
  581 
  582 This module allows parsing, changing, saving of configuration files
  583 of a general style. It also supports three different paradigma 
  584 with respect to multiple occurrences of keys: C<first-win> specifies
  585 a configuration file where the first occurrence of a key specifies
  586 the value, C<last-win> specifies that the last wins, and
  587 C<multiple> that all keys are kept.
  588 
  589 The configuration files (henceforth conffiles) can contain comments
  590 initiated by the $comment_char defined at instantiation time.
  591 Everything after a $comment_char, as well as empty lines, will be ignored.
  592 
  593 The rest should consists of key/value pairs separated by the separator,
  594 defined as well at instantiation time.
  595 
  596 Whitespace around the separator, and before and after key and value 
  597 are allowed.
  598 
  599 Comments can be on the same line as key/value pairs and are also preserved
  600 over changes.
  601 
  602 Continuation lines (i.e., lines with last character being a backslash)
  603 are allowed after key/value pairs, but the key and
  604 the separator has to be on the same line.
  605 
  606 Continuations are not possible in comments, so a terminal backslash in 
  607 a comment will be ignored, and in fact not written out on save.
  608 
  609 =head2 Methods
  610 
  611 =over 4
  612 
  613 =item B<< $conffile = TeXLive::TLConfFile->new($file_name, $comment_char, $separator [, $type]) >>
  614 
  615 instantiates a new TLConfFile and returns the object. The file specified
  616 by C<$file_name> does not have to exist, it will be created at save time.
  617 
  618 The C<$comment_char> can actually be any regular expression, but 
  619 embedding grouping is a bad idea as it will break parsing.
  620 
  621 The C<$separator> can also be any regular expression.
  622 
  623 The C<$type>, if present, has to be one of C<last-win> (the default),
  624 C<first-win>, or C<multiple>.
  625 
  626 =item B<< $conffile->file >>
  627 
  628 Returns the location of the configuration file. Not changeable (at the moment).
  629 
  630 =item B<< $conffile->cc >>
  631 
  632 Returns the comment character.
  633 
  634 =item B<< $conffile->sep >>
  635 
  636 Returns the separator.
  637 
  638 =item B<< $conffile->type >>
  639 
  640 Returns the type.
  641 
  642 =item B<< $conffile->key_present($key) >>
  643 
  644 Returns true (1) if the given key is present in the config file, otherwise
  645 returns false (0).
  646 
  647 =item B<< $conffile->keys >>
  648 
  649 Returns the list of keys currently set in the config file.
  650 
  651 =item B<< $conffile->value($key [, $value, ...]) >>
  652 
  653 With one argument, returns the current setting of C<$key>, or undefined
  654 if the key is not set. If the configuration file is of C<multiple>
  655 type a list of keys ordered by occurrence in the file is returned.
  656 
  657 With two (or more) arguments changes (or adds) the key/value pair to 
  658 the config file and returns the I<new> value.
  659 In case of C<first-win> or C<last-win>, the respective occurrence
  660 of the key is changed, and the others left intact. In this case only
  661 the first C<$value> is used.
  662 
  663 In case of C<multiple> the C<$values> are assigned to the keys in the 
  664 order of occurrence in the file. If extra values are present, they
  665 are added. If on the contrary less values then already existing
  666 keys are passed, the remaining keys are deleted.
  667 
  668 =item B<< $conffile->rename_key($oldkey, $newkey) >>
  669 
  670 Renames a key from C<$oldkey> to C<$newkey>. It does not automatically
  671 save the new config file.
  672 
  673 =item B<< $conffile->is_changed >>
  674 
  675 Returns true (1) if some real change has happened in the configuration file,
  676 that is a value has been changed to something different, or a new
  677 setting has been added.
  678 
  679 Note that changing a setting back to the original one will not reset
  680 the changed flag.
  681 
  682 =item B<< $conffile->save >>
  683 
  684 Saves the config file, preserving as much structure and comments of 
  685 the original file as possible.
  686 
  687 =item B<< $conffile->reparse >>
  688 
  689 Reparses the configuration file.
  690 
  691 
  692 =back
  693 
  694 =head1 EXAMPLES
  695 
  696 For parsing a C<texmf.cnf> file you can use
  697 
  698   $tmfcnf = TeXLive::TLConfFile->new(".../texmf-dist/web2c", "[#%]", "=");
  699 
  700 since the allowed comment characters for texmf.cnf files are # and %.
  701 After that you can query keys:
  702 
  703   $tmfcnf->value("TEXMFMAIN");
  704   $tmfcnf->value("trie_size", 900000);
  705  
  706 =head1 AUTHORS AND COPYRIGHT
  707 
  708 This script and its documentation were written for the TeX Live
  709 distribution (L<http://tug.org/texlive>) and both are licensed under the
  710 GNU General Public License Version 2 or later.
  711 
  712 =cut
  713 
  714 ### Local Variables:
  715 ### perl-indent-level: 2
  716 ### tab-width: 2
  717 ### indent-tabs-mode: nil
  718 ### End:
  719 # vim:set tabstop=2 expandtab: #