home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / system-tools-backends-2.0 / scripts / Utils / Parse.pm < prev    next >
Encoding:
Perl POD Document  |  2009-04-09  |  27.6 KB  |  1,413 lines

  1. #!/usr/bin/perl
  2. #-*- Mode: perl; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  3.  
  4. # parse.pl: Common parsing stuff for the ximian-setup-tools backends.
  5. #
  6. # Copyright (C) 2000-2001 Ximian, Inc.
  7. #
  8. # Authors: Hans Petter Jansson <hpj@ximian.com>
  9. #          Arturo Espinosa <arturo@ximian.com>
  10. #
  11. # This program is free software; you can redistribute it and/or modify
  12. # it under the terms of the GNU Library General Public License as published
  13. # by the Free Software Foundation; either version 2 of the License, or
  14. # (at your option) any later version.
  15. #
  16. # This program is distributed in the hope that it will be useful,
  17. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19. # GNU Library General Public License for more details.
  20. #
  21. # You should have received a copy of the GNU Library General Public License
  22. # along with this program; if not, write to the Free Software
  23. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
  24.  
  25. package Utils::Parse;
  26.  
  27. use Utils::Util;
  28. use Utils::File;
  29.  
  30.  
  31. # The concept of keyword (kw) here is a key, normaly in its own line, whose
  32. # boolean representation is its own existence.
  33.  
  34. # Every final parsing function to be used by a table must handle one key
  35. # at a time, but maybe parse several values from there and return a
  36. # ref to array or hash.
  37. #
  38. # Always return a scalar. If you need to return an array or a hash,
  39. # return a ref to it.
  40.  
  41. # First some helper functions for the whole process.
  42. # Expand substrings of the form #$substr# to the $value in
  43. # the string or recursively in the array $strarr.
  44.  
  45. sub expand
  46. {
  47.   my ($strarr, @args) = @_;
  48.  
  49.   if (ref $strarr eq "ARRAY")
  50.   {
  51.     my ($i);
  52.     
  53.     $strarr = [ @$strarr ];
  54.     foreach $i (@$strarr)
  55.     {
  56.       $i = &expand ($i, @args);
  57.     }
  58.  
  59.     return $strarr;
  60.   }
  61.  
  62.   while (@args)
  63.   {
  64.     $substr = shift @args;
  65.     $value  = shift @args;
  66.  
  67.     $strarr =~ s/\#$substr\#/$value/;
  68.   }
  69.  
  70.   return $strarr;
  71. }
  72.  
  73. sub replace_hash_values
  74. {
  75.   my ($cp, $hash) = @_;
  76.   my ($j, $replace_key, $value);
  77.  
  78.   foreach $j (@$cp)
  79.   {
  80.     while ($j =~ /%([^%]*)%/)
  81.     {
  82.       $replace_key = $1;
  83.       if (exists $$hash{$replace_key}) 
  84.       {
  85.         $value = $$hash{$replace_key};
  86.         if (ref $value)
  87.         {
  88.           $j = $value;
  89.         }
  90.         else
  91.         {
  92.           $j =~ s/%$replace_key%/$value/g;
  93.         }
  94.       }
  95.       else
  96.       {
  97.         return 0;
  98.       }
  99.     }
  100.   }
  101.  
  102.   return 1;
  103. }
  104.  
  105. sub replace_files
  106. {
  107.   my ($values, $fn_hash) = @_;
  108.   my @ret;
  109.  
  110.   return () if $values eq undef;
  111.   $values = [$values] if !ref $values;
  112.  
  113.   foreach $i (@$values)
  114.   {
  115.     if (exists $$fn_hash{$i})
  116.     {
  117.       push @ret, $$fn_hash{$i};
  118.     }
  119.     else
  120.     {
  121.       push @ret, $i;
  122.     }
  123.   }
  124.  
  125.   return @ret;
  126. }
  127.  
  128. # Additional abstraction: parse table entries can have
  129. # arrays inside. The parsing proc will be ran with every
  130. # combination that the arrays provide. Ex:
  131. # ["user", \&get_foo, [0, 1], [2, 3] ] will parse
  132. # using the combinatory of [0, 1]x[2, 3] until a result
  133. # ne undef is given. Check RedHat 7.2's network parse table
  134. # for further enlightenment.
  135. sub run_entry
  136. {
  137.   my ($hash, $key, $proc, $cp) = @_;
  138.   my ($ncp, $i, $j, $res);
  139.  
  140.   $ncp = [@$cp];
  141.   for ($i = 0; $i < scalar (@$cp); $i ++)
  142.   {
  143.     if (ref $$cp[$i] eq "ARRAY")
  144.     {
  145.       foreach $j (@{$$cp[$i]})
  146.       {
  147.         $$ncp[$i] = $j;
  148.         $res = &run_entry ($hash, $key, $proc, $ncp);
  149.         return $res if $res ne undef;
  150.       }
  151.       return undef;
  152.     }
  153.   }
  154.  
  155.   # OK, the given entry didn't have any array refs in it...
  156.   
  157.   return undef if (!&replace_hash_values ($cp, $hash));
  158.  
  159.   &Utils::Report::enter ();
  160.   &Utils::Report::do_report ("parse_table", "$key");
  161.   &Utils::Report::leave ();
  162.   
  163.   $$hash{$key} = &$proc (@$cp);
  164.   return $$hash{$key};
  165. }
  166.  
  167. # OK, this is the good stuff:
  168.  
  169. # get_from_table takes a file mapping and a parse table.
  170. #
  171. # The functions in the replace tables, most of which are coded in
  172. # this file, receive the mapped files of the first argument, and then
  173. # a set of values.
  174.  
  175. # The value the parse function returns is set into a hash,
  176. # using as key the first item of the parse table entry. This is done
  177. # only if the $hash{$key} is empty, which allows us to try with
  178. # several parse methods to try to get a value, where our parse functions
  179. # can return undef if they failed to get the requested value.
  180. #
  181. # A ref to the hash with all the fetched values is returned.
  182. sub get_from_table
  183. {
  184.   my ($fn, $table) = @_;
  185.   my %hash;
  186.   my ($key, $proc, @param);
  187.   my ($i, @cp, @files);
  188.  
  189.   foreach $i (@$table)
  190.   {
  191.     @cp = @$i;
  192.     $key = shift (@cp);
  193.  
  194.     if ($hash{$key} eq undef)
  195.     {
  196.       $proc = shift (@cp);
  197.       @files = &replace_files (shift (@cp), $fn);
  198.  
  199.       # Don't unshift the resulting files if none were given.
  200.       unshift @cp, @files if (scalar @files) > 0;
  201.  
  202.       &run_entry (\%hash, $key, $proc, \@cp);
  203.     }
  204.   }
  205.  
  206.   foreach $i (keys (%hash))
  207.   {
  208.     delete $hash{$i} if ($hash{$i} eq undef);
  209.   }
  210.   
  211.   return \%hash;
  212. }
  213.  
  214. # Just return the passed values. If there's just
  215. # one value, the value. If more, a reference to an
  216. # array with the values.
  217. sub get_trivial
  218. {
  219.   my (@res) = @_;
  220.  
  221.   &Utils::Report::enter ();
  222.   &Utils::Report::do_report ("parse_trivial", "@res");
  223.   &Utils::Report::leave ();
  224.  
  225.   return $res[0] if (scalar @res) <= 1;
  226.   return \@res;
  227. }
  228.  
  229. # Try to read a line from $fd and remove any leading or
  230. # trailing white spaces. Return ref to read $line or
  231. # -1 if eof.
  232. sub chomp_line_std
  233. {
  234.   my ($fd) = @_;
  235.   my $line;
  236.  
  237.   $line = <$fd>;
  238.   return -1 if !$line;
  239.  
  240.   chomp $line;
  241.   $line =~ s/^[ \t]+//;
  242.   $line =~ s/[ \t]+$//;
  243.  
  244.   return \$line;
  245. }
  246.  
  247. # Assuming $line is a line read from a shell file,
  248. # remove comments.
  249. sub process_sh_line
  250. {
  251.   my ($line) = @_;
  252.   my ($pline);
  253.  
  254.   # This will put escaped hashes out of danger.
  255.   # But only inside valid quotes!
  256.   while ($line =~ /([^\"\']*[\"\'][^\#\"\']*)(\#?)([^\"\']*[\"\'])/g)
  257.   {
  258.       $pline .= $1;
  259.       $pline .= "__hash__" if ($2 ne undef);
  260.       $pline .= $3;
  261.   }
  262.  
  263.   # The line may not match the regexp above,
  264.   $pline = $line if ($pline eq undef);
  265.  
  266.   $pline =~ s/\\\#/\\__hash__/g;
  267.  
  268.   # Nuke everything after a hash and bye bye trailing spaces.
  269.   $pline =~ s/[ \t]*\#.*//;
  270.  
  271.   # Let escaped hashes come back home.
  272.   $pline =~ s/__hash__/\#/g;
  273.  
  274.   return $pline;
  275. }
  276.  
  277. # Same as chomp_line_std, but apply
  278. # the sh line processing before returning.
  279. # -1 if eof, ref to read $line if success.
  280. sub chomp_line_hash_comment
  281. {
  282.   my ($fd) = @_;
  283.   my $line;
  284.  
  285.   $line = &chomp_line_std ($fd);
  286.   return -1 if $line == -1;
  287.  
  288.   $line = &process_sh_line ($$line);
  289.   return \$line;
  290. }
  291.  
  292. # Get an sh line, and remove the export keyword, if any.
  293. sub chomp_line_sh_export
  294. {
  295.   my ($fd) = @_;
  296.   my $line;
  297.  
  298.   $line = &chomp_line_hash_comment ($fd);
  299.   return -1 if $line == -1;
  300.  
  301.   $line = $$line;
  302.  
  303.   $line =~ s/^export //;
  304.  
  305.   return \$line;
  306. }
  307.  
  308. # Parse a $file, wich is assumed to have a column-based format, with $re matching field separators
  309. # and one record per line. Search for $key, and return either a scalar with the first ocurrence,
  310. # or an array with all the found ocurrences.
  311. sub split_ref
  312. {
  313.   my ($file, $key, $re, $all, $line_read_proc) = @_;
  314.   my ($fd, @line, @res);
  315.  
  316.   &Utils::Report::enter ();
  317.   &Utils::Report::do_report ("parse_split", $key, $file);
  318.  
  319.   $proc = $line_read_proc? $line_read_proc : \&chomp_line_std;
  320.   
  321.   $fd = &Utils::File::open_read_from_names ($file);
  322.   $all = 0 if !$fd;
  323.  
  324.   while (($line = &$proc ($fd)) != -1)
  325.   {
  326.     $line = $$line;
  327.     next if $line eq "";
  328.  
  329.     @line = split ($re, $line, 2);
  330.  
  331.     if (shift (@line) =~ "^$key\$")
  332.     {
  333.       if ($all) {
  334.         push @res, $line[0];
  335.       }
  336.       else
  337.       {
  338.         &Utils::Report::leave ();
  339.         &Utils::File::close_file ($fd);
  340.         return \$line[0];
  341.       }
  342.     }
  343.   }
  344.  
  345.   &Utils::Report::leave ();
  346.   &Utils::File::close_file ($fd);
  347.   return \@res if ($all);
  348.   return -1;
  349. }
  350.  
  351. sub split
  352. {
  353.   my $res;
  354.  
  355.   # Don't pass @_ like this anywhere. This is bad practice.
  356.   $res = &split_ref (@_);
  357.  
  358.   return $$res if ref $res eq "SCALAR";
  359.   return @$res if ref $res eq "ARRAY";
  360.   return undef;
  361. }
  362.  
  363. # This gives meaning to the $all flag of &split, and returns a reference to the array, which
  364. # is what we want. (ie search a.com\nsearch b.com\nsearch c.com)
  365. sub split_all
  366. {
  367.   my ($file, $key, $re, $line_read_proc) = @_;
  368.   my @a;
  369.  
  370.   @a = &split ($file, $key, $re, 1, $line_read_proc);
  371.  
  372.   return \@a;
  373. }
  374.  
  375. # Same, but use the hash_comment routine for line analysis.
  376. sub split_all_hash_comment
  377. {
  378.   my ($file, $key, $re) = @_;
  379.  
  380.   return &split_all ($file, $key, $re, \&chomp_line_hash_comment);
  381. }
  382.  
  383. # Make the elements of the resulting array unique.
  384. sub split_all_unique_hash_comment
  385. {
  386.   my ($file, $key, $re) = @_;
  387.   my ($arr, @res);
  388.   my (%hash, $i);
  389.  
  390.   $arr = &split_all ($file, $key, $re, \&chomp_line_hash_comment);
  391.  
  392.   foreach $i (@$arr)
  393.   {
  394.     next if exists $hash{$i};
  395.     $hash{$i} = 1;
  396.     push @res, $i;
  397.   }
  398.  
  399.   return \@res;
  400. }
  401.  
  402. sub split_all_array_with_pos
  403. {
  404.   my ($file, $key, $pos, $re, $sep, $line_read_proc) = @_;
  405.   my ($arr, @s, @ret, $i);
  406.  
  407.   $arr = &split_all ($file, $key, $re, $line_read_proc);
  408.  
  409.   foreach $i (@$arr)
  410.   {
  411.     if ($i)
  412.     {
  413.       @s = split ($sep, $i);
  414.       push @ret, @s[0];
  415.     }
  416.   }
  417.  
  418.   return \@ret;
  419. }
  420.  
  421. # Same, but for $all = 0. (ie nameserver 10.0.0.1)
  422. sub split_first_str
  423. {
  424.   my ($file, $key, $re, $line_read_proc) = @_;
  425.  
  426.   return &split ($file, $key, $re, 0, $line_read_proc);
  427. }
  428.  
  429. # Interpret the result as a boolean. (ie multi on)
  430. sub split_first_bool
  431. {
  432.   my ($file, $key, $re, $line_read_proc) = @_;
  433.   my $ret;
  434.  
  435.   $ret = &split_first_str ($file, $key, $re, $line_read_proc);
  436.  
  437.   return undef if ($ret eq undef);
  438.   return (&Utils::Util::read_boolean ($ret)? 1: 0);
  439. }
  440.  
  441. # After getting the first field, split the result with $sep matching separators. (ie order hosts,bind)
  442. sub split_first_array
  443. {
  444.   my ($file, $key, $re, $sep, $line_read_proc) = @_;
  445.   my @ret;
  446.  
  447.   @ret = split ($sep, &split ($file, $key, $re, 0, $line_read_proc));
  448.  
  449.   return \@ret;
  450. }
  451.  
  452. sub split_first_array_pos
  453. {
  454.   my ($file, $key, $pos, $re, $sep, $line_read_proc) = @_;
  455.   my (@ret);
  456.  
  457.   @ret = split ($sep, &split ($file, $key, $re, 0, $line_read_proc));
  458.   return $ret[$pos];
  459. }
  460.  
  461. # Do an split_first_array and then make
  462. # the array elements unique. This is to fix broken
  463. # searchdomain entries in /etc/resolv.conf, for example.
  464. sub split_first_array_unique
  465. {
  466.   my ($file, $key, $re, $sep, $line_read_proc) = @_;
  467.   my (@arr, @res);
  468.   my (%hash, $i);
  469.  
  470.   @arr = split ($sep, &split ($file, $key, $re, 0, $line_read_proc));
  471.  
  472.   foreach $i (@arr)
  473.   {
  474.     next if exists $hash{$i};
  475.     $hash{$i} = 1;
  476.     push @res, $i;
  477.   }
  478.  
  479.   return \@res;
  480. }
  481.  
  482. # For all keys in $file, sepparated from its values
  483. # by $key_re, sepparate its values using $value_re
  484. # and assign to a newly created hash. Use ONLY when
  485. # you don't know what keys you are going to parse
  486. # (i.e. /etc/hosts). Any other application will not
  487. # be very portable and should be avoided.
  488. sub split_hash
  489. {
  490.   my ($file, $key_re, $value_re) = @_;
  491.   my ($fd, @line, %res, $key);
  492.  
  493.   &Utils::Report::enter ();
  494.   &Utils::Report::do_report ("parse_split_hash", $file);
  495.   
  496.   $fd = &Utils::File::open_read_from_names ($file);
  497.   
  498.   while (<$fd>)
  499.   {
  500.     chomp;
  501.     s/^[ \t]+//;
  502.     s/[ \t]+$//;
  503.     s/\#.*$//;
  504.     next if (/^$/);
  505.     @line = split ($key_re, $_, 2);
  506.  
  507.     $key = shift (@line);
  508.     push @{$res{$key}}, split ($value_re, $line[0]);
  509.   }
  510.  
  511.   &Utils::File::close_file ($fd);
  512.   &Utils::Report::leave ();
  513.   return undef if (scalar keys (%res) == 0);
  514.   return \%res;
  515. }
  516.  
  517. # Same as above, but join lines that end with '\'.
  518. sub split_hash_with_continuation
  519. {
  520.   my ($file, $key_re, $value_re) = @_;
  521.   my ($fd, $l, @line, %res, $key);
  522.  
  523.   &Utils::Report::enter ();
  524.   &Utils::Report::do_report ("parse_split_hash_cont", $file);
  525.   
  526.   $fd = &Utils::File::open_read_from_names ($file);
  527.   
  528.   while (($l = &ini_line_read ($fd)) != -1)
  529.   {
  530.     $_ = $$l;
  531.     chomp;
  532.     s/^[ \t]+//;
  533.     s/[ \t]+$//;
  534.     s/\#.*$//;
  535.     next if (/^$/);
  536.     @line = split ($key_re, $_, 2);
  537.  
  538.     $key = shift (@line);
  539.     $res{$key} = [ split ($value_re, $line[0]) ];
  540.   }
  541.  
  542.   &Utils::File::close_file ($fd);
  543.   &Utils::Report::leave ();
  544.   return undef if (scalar keys (%res) == 0);
  545.   return \%res;
  546. }
  547.  
  548. # Remove escape sequences in a shell value.
  549. sub unescape
  550. {
  551.   my $ret = $_[0];
  552.  
  553.   # Quote shell special chars.
  554.   $ret =~ s/\\\"/\\_/g;
  555.   $ret =~ s/\"//g;
  556.   $ret =~ s/\\_/\"/g;
  557.   $ret =~ s/\\\'/\\_/g;
  558.   $ret =~ s/\'//g;
  559.   $ret =~ s/\\_/\'/g;
  560.   $ret =~ s/\\(.)/$1/g;
  561.  
  562.   return $ret;
  563. }
  564.  
  565. # unescape (escape (x)) == x
  566. sub escape
  567. {
  568.   my ($value) = @_;
  569.  
  570.   $value =~ s/([\ \"\`\$\\])/\\$1/g;
  571.   #$value = "\"$value\"" if ($value =~ /[ \t\'&|*?\[\]\{\}\{\}<>]/);
  572.  
  573.   return $value;
  574. }
  575.  
  576. # For files which are a list of /bin/sh shell variable declarations. (ie GATEWAY=10.10.10.1)
  577. sub get_sh
  578. {
  579.   my ($file, $key) = @_;
  580.   my $ret;
  581.  
  582.   &Utils::Report::enter ();
  583.   &Utils::Report::do_report ("parse_sh", $key, $file);
  584.   $ret = &split_first_str ($file, $key, "[ \t]*=[ \t]*",
  585.                                      \&chomp_line_hash_comment);
  586.   &Utils::Report::leave ();
  587.  
  588.   return &unescape ($ret);
  589. }
  590.  
  591. # Same, but interpret the returning value as a bool. (ie NETWORKING=yes)
  592. sub get_sh_bool
  593. {
  594.   my ($file, $key) = @_;
  595.   my $ret;
  596.  
  597.   $ret = &get_sh ($file, $key);
  598.  
  599.   return undef if ($ret eq undef);
  600.   return (&Utils::Util::read_boolean ($ret)? 1: 0);
  601. }
  602.  
  603. # Get an sh value and then split with $re, returning ref to resulting array.
  604. sub get_sh_split
  605. {
  606.   my ($file, $key, $re) = @_;
  607.   my (@ret, $val);
  608.  
  609.   $val = &get_sh ($file, $key);
  610.   @ret = split ($re, $val);
  611.  
  612.   return \@ret;
  613. }
  614.  
  615. # Get a fully qualified hostname from a $key shell var in $file
  616. # and extract the hostname from there. e.g.: suse70's /etc/rc.config's FQHOSTNAME.
  617. sub get_sh_hostname
  618. {
  619.   my ($file, $key) = @_;
  620.   my ($val);
  621.  
  622.   $val = &get_sh_split ($file, $key, "\\.");
  623.  
  624.   return $$val[0];
  625. }
  626.  
  627. # Get a fully qualified hostname from a $key shell var in $file
  628. # and extract the domain from there. e.g.: suse70's /etc/rc.config's FQHOSTNAME.
  629. sub get_sh_domain
  630. {
  631.   my ($file, $key) = @_;
  632.   my ($val);
  633.  
  634.   $val = &get_sh_split ($file, $key, "\\.");
  635.  
  636.   return join ".", @$val[1..$#$val];
  637. }
  638.  
  639. # For files which are a list of /bin/sh shell variable exports. (eg export GATEWAY=10.10.10.1)
  640. sub get_sh_export
  641. {
  642.   my ($file, $key) = @_;
  643.   my $ret;
  644.  
  645.   &Utils::Report::enter ();
  646.   &Utils::Report::do_report ("parse_sh", $key, $file);
  647.   $ret = &split_first_str ($file, $key, "[ \t]*=[ \t]*",
  648.                                      \&chomp_line_sh_export);
  649.   &Utils::Report::leave ();
  650.  
  651.   return &unescape ($ret);
  652. }
  653.  
  654. # Same, but interpret the returing value as a bool. (ie export NETWORKING=yes)
  655. sub get_sh_export_bool
  656. {
  657.   my ($file, $key) = @_;
  658.   my $ret;
  659.  
  660.   $ret = &get_sh_export ($file, $key);
  661.  
  662.   return undef if ($ret eq undef);
  663.   return (&Utils::Util::read_boolean ($ret)? 1: 0);
  664. }
  665.  
  666. # Same, but accepting a regexp and returning the value between the paren operator
  667. sub get_sh_re
  668. {
  669.   my ($file, $key, $re) = @_;
  670.   my $ret;
  671.  
  672.   $ret = &get_sh ($file, $key);
  673.  
  674.   $ret =~ /$re/i;
  675.   return $1;
  676. }
  677.  
  678.  
  679. # Search for $keyword in $file, delimited by $re (default " ") or EOL.
  680. # If keyword exists, return 1, else 0.
  681. sub get_kw
  682. {
  683.   my ($file, $keyword, $re, $line_read_proc) = @_;
  684.   my $res;
  685.  
  686.   &Utils::Report::enter ();
  687.   &Utils::Report::do_report ("parse_kw", $keyword, $file);
  688.   
  689.   if (! -f "$gst_prefix/$file")
  690.   {
  691.     &Utils::Report::enter ();
  692.     &Utils::Report::do_report ("file_open_read_failed", $file);
  693.     &Utils::Report::leave ();
  694.     &Utils::Report::leave ();
  695.     return undef;
  696.   }
  697.   
  698.   $re = " " if $re eq undef;
  699.   $res = &split_ref ($file, $keyword, $re, 0, $line_read_proc);
  700.  
  701.   &Utils::Report::leave ();
  702.   return 0 if $res == -1;
  703.   return 1;
  704. }
  705.  
  706. # A file containing the desired value in its first line. (ie /etc/hostname)
  707. sub get_first_line
  708. {
  709.   my ($file) = @_;
  710.   my ($fd, $res);
  711.  
  712.   &Utils::Report::enter ();
  713.   &Utils::Report::do_report ("parse_line_first", $file);
  714.   $fd = &Utils::File::open_read_from_names ($file);
  715.   &Utils::Report::leave ();
  716.   
  717.   return undef if !$fd;
  718.  
  719.   chomp ($res = <$fd>);
  720.   &Utils::File::close_file ($fd);
  721.   return $res;
  722. }
  723.  
  724. # parse a chat file, searching for an entry that matches $re.
  725. # $re must have one paren operator (ie "^atd[^0-9]*([0-9, -]+)").
  726. sub get_from_chatfile
  727. {
  728.   my ($file, $re) = @_;
  729.   my ($fd, $found);
  730.  
  731.   &Utils::Report::enter ();
  732.   &Utils::Report::do_report ("parse_chat", $file);
  733.   $fd = &Utils::File::open_read_from_names ("$file");
  734.   &Utils::Report::leave ();
  735.   return undef if !$fd;
  736.  
  737.   while (<$fd>)
  738.   {
  739.     # We'll be emptying $_ as we "scan".
  740.     chomp;
  741.     while ($_ ne "")
  742.     {
  743.       s/^\s*//;
  744.  
  745.       # If it uses quotes. FIXME: Assuming they surround the whole string.
  746.       if (/^\'/)
  747.       {
  748.         s/\'([^\']*)\' ?//;
  749.         $found = $1;
  750.       }
  751.       else
  752.       {
  753.         s/(\S*)\s?//;
  754.         $found = $1;
  755.       }
  756.  
  757.       # If it looks like what we're looking for, return what matched the parens.
  758.       if ($found =~ /$re/i)
  759.       {
  760.         &Utils::File::close_file ($fd);
  761.         return $1;
  762.       }
  763.     }
  764.   }
  765.   
  766.   &Utils::File::close_file ($fd);
  767.   # Oops: not found.
  768.   return undef;
  769. }
  770.  
  771. # Clean an ini line of comments and leading or
  772. # trailing spaces.
  773. sub ini_line_clean
  774. {
  775.   $_ = $_[0];
  776.   
  777.   chomp;
  778.   s/\#.*//;
  779.   s/;.*//;
  780.   s/^[ \t]+//;
  781.   s/[ \t]+$//;
  782.  
  783.   return $_;
  784. }
  785.  
  786. # Read an ini line, which may have to be joined
  787. # with the next one if it ends with '\'.
  788. sub ini_line_read
  789. {
  790.   my $fd = $_[0];
  791.   my $l;
  792.  
  793.   $l = <$fd>;
  794.   return -1 if ($l eq undef);
  795.   
  796.   $l = &ini_line_clean ($l);
  797.   while ($l =~ /\\$/)
  798.   {
  799.     $l =~ s/\\$//;
  800.     $l .= &ini_line_clean (scalar <$fd>);
  801.   }
  802.  
  803.   return \$l;
  804. }
  805.  
  806. # Return an array of all found sections in $file.
  807. sub get_ini_sections
  808. {
  809.   my ($file) = @_;
  810.   my (@sections, $line);
  811.  
  812.   $fd = &Utils::File::open_read_from_names ($file);
  813.   
  814.   while (($line = &ini_line_read ($fd)) != -1)
  815.   {
  816.     $_ = $$line;
  817.     next if (/^$/);
  818.     push @sections, $1 if (/\[([^\]]+)\]/i);
  819.   }
  820.  
  821.   &Utils::File::close_file ($fd);
  822.  
  823.   return @sections;
  824. }
  825.  
  826. # Get the value of a $var in a $section from $file.
  827. sub get_from_ini
  828. {
  829.   my ($file, $section, $var) = @_;
  830.   my ($fd, $res, $line);
  831.   my $found_section_flag = 0;
  832.   my $escaped_section;
  833.  
  834.   &Utils::Report::enter ();
  835.   &Utils::Report::do_report ("parse_ini", $var, $file, $section);
  836.   $fd = &Utils::File::open_read_from_names ($file);
  837.   &Utils::Report::leave ();
  838.   $res = undef;
  839.   $escaped_section = &escape ($section);
  840.  
  841.   while (($line = &ini_line_read ($fd)) != -1)
  842.   {
  843.     $_ = $$line;
  844.     next if (/^$/);
  845.  
  846.     if (/\[$escaped_section\]/i)
  847.     {
  848.       $found_section_flag = 1;
  849.       next;
  850.     }
  851.  
  852.     if ($found_section_flag)
  853.     {
  854.       if (/^$var[ \t]*=/i)
  855.       {
  856.         s/^$var[ \t]*=[ \t]*//i;
  857.         $res = $_;
  858.         last;
  859.       }
  860.       elsif (/\[\S+\]/i)
  861.       {
  862.         last;
  863.       }
  864.     }
  865.   }
  866.  
  867.   &Utils::File::close_file ($fd);
  868.  
  869.   return $res;
  870. }
  871.  
  872. # Same, but treat value as bool and return 1/0.
  873. sub get_from_ini_bool
  874. {
  875.   my ($file, $section, $var) = @_;
  876.   my $ret;
  877.   
  878.   $ret = &get_from_ini ($file, $section, $var);
  879.   
  880.   return 0 if ($ret eq undef);
  881.   return (&Utils::Util::read_boolean ($ret)? 1 : 0);
  882. }
  883.  
  884. # Debian interfaces(5) states that files starting with # are comments.
  885. # Also, leading and trailing spaces are ignored.
  886. sub interfaces_line_clean
  887. {
  888.   $_ = $_[0];
  889.   
  890.   chomp;
  891.   s/^[ \t]+//;
  892.   s/^\#.*//;
  893.   s/[ \t]+$//;
  894.  
  895.   return $_;
  896. }
  897.  
  898. # interfaces(5) also states that \ line continuation is possible.
  899. sub interfaces_line_read
  900. {
  901.   my $fd = $_[0];
  902.   my $l;
  903.  
  904.   $l = <$fd>;
  905.   return -1 if ($l eq undef);
  906.   
  907.   $l = &interfaces_line_clean ($l);
  908.   while ($l =~ /\\$/)
  909.   {
  910.     $l =~ s/\\$//;
  911.     $l .= &interfaces_line_clean (scalar <$fd>);
  912.   }
  913.  
  914.   return \$l;
  915. }
  916.  
  917. # Read lines until a stanza, a line starting with $stanza_type is found.
  918. # Return ref to an array with the stanza params split.
  919. sub interfaces_get_next_stanza
  920. {
  921.   my ($fd, $stanza_type) = @_;
  922.   my $line;
  923.  
  924.   while (($line = &interfaces_line_read ($fd)) != -1)
  925.   {
  926.     $_ = $$line;
  927.     if (/^$stanza_type[ \t]+[^ \t]/)
  928.     {
  929.       s/^$stanza_type[ \t]+//;
  930.       return [ split ("[ \t]+", $_) ];
  931.     }
  932.   }
  933.  
  934.   return -1;
  935. }
  936.  
  937. # Read lines until a line not recognized as a stanza is
  938. # found, and split in a "tuple" of key/value.
  939. sub interfaces_get_next_option
  940. {
  941.   my $fd = $_[0];
  942.   my $line;
  943.  
  944.   while (($line = &interfaces_line_read ($fd)) != -1)
  945.   {
  946.     $_ = $$line;
  947.     next if /^$/;
  948.     
  949.     return [ split ("[ \t]+", $_, 2) ] if (!/^iface[ \t]/);
  950.     return -1;
  951.   }
  952.  
  953.   return -1;
  954. }
  955.  
  956. # Get all stanzas from file. Return array.
  957. sub get_interfaces_stanzas
  958. {
  959.   my ($file, $stanza_type) = @_;
  960.   my ($fd, @res);
  961.  
  962.   $fd = &Utils::File::open_read_from_names ($file);
  963.   $res = undef;
  964.   
  965.   while (($_ = &interfaces_get_next_stanza ($fd, $stanza_type)) != -1)
  966.   {
  967.     push @res, $_;
  968.   }
  969.  
  970.   &Utils::File::close_file ($fd);
  971.  
  972.   return @res;
  973. }
  974.  
  975. # Find stanza for $iface in $file, and return
  976. # tuple for option with $key. Return -1 if unexisting.
  977. sub get_interfaces_option_tuple
  978. {
  979.   my ($file, $iface, $key, $all) = @_;
  980.   my ($fd, @res);
  981.  
  982.   $fd = &Utils::File::open_read_from_names ($file);
  983.  
  984.   while (($stanza = &interfaces_get_next_stanza ($fd, "iface")) != -1)
  985.   {
  986.     if ($$stanza[0] eq $iface)
  987.     {
  988.       while (($tuple = &interfaces_get_next_option ($fd)) != -1)
  989.       {
  990.         if ($$tuple[0] =~ /$key/)
  991.         {
  992.           return $tuple if !$all;
  993.           push @res, $tuple;
  994.         }
  995.       }
  996.  
  997.       return -1 if !$all;
  998.     }
  999.   }
  1000.  
  1001.   return @res if $all;
  1002.   return -1;
  1003. }
  1004.  
  1005. # Go get option $kw for $iface stanza. If found,
  1006. # return 1 (true), else, false.
  1007. sub get_interfaces_option_kw
  1008. {
  1009.   my ($file, $iface, $kw) = @_;
  1010.   my $tuple;
  1011.  
  1012.   &Utils::Report::enter ();
  1013.   &Utils::Report::do_report ("parse_ifaces_kw", $kw, $file);
  1014.   $tuple = &get_interfaces_option_tuple ($file, $iface, $kw);
  1015.   &Utils::Report::leave ();
  1016.  
  1017.   if ($tuple != -1)
  1018.   {
  1019.     &Utils::Report::do_report ("parse_ifaces_kw_strange", $iface, $file) if ($$tuple[1] ne "");
  1020.  
  1021.     return 1;
  1022.   }
  1023.  
  1024.   return 0;
  1025. }
  1026.  
  1027. # For such keywords as noauto, whose existence means
  1028. # a false value.
  1029. sub get_interfaces_option_kw_not
  1030. {
  1031.   my ($file, $iface, $kw) = @_;
  1032.   
  1033.   return &get_interfaces_option_kw ($file, $iface, $kw)? 0 : 1;
  1034. }
  1035.  
  1036. # Go get option $key for $iface in $file and return value.
  1037. sub get_interfaces_option_str
  1038. {
  1039.   my ($file, $iface, $key) = @_;
  1040.   my $tuple;
  1041.  
  1042.   &Utils::Report::enter ();
  1043.   &Utils::Report::do_report ("parse_ifaces_str", $kw, $file);
  1044.   $tuple = &get_interfaces_option_tuple ($file, $iface, $key);
  1045.   &Utils::Report::leave ();
  1046.  
  1047.   if ($tuple != -1)
  1048.   {
  1049.     return $$tuple[1];
  1050.   }
  1051.  
  1052.   return undef;
  1053. }
  1054.  
  1055.  
  1056. # Implementing pump(8) pump.conf file format parser.
  1057. # May be useful for dhcpd too.
  1058. sub pump_get_next_option
  1059. {
  1060.   my ($fd) = @_;
  1061.   my $line;
  1062.  
  1063.   while (($line = &interfaces_line_read ($fd)) != -1)
  1064.   {
  1065.     $line = $$line;
  1066.     if ($line ne "")
  1067.     {
  1068.       return [ split ("[ \t]+", $line, 2) ];
  1069.     }
  1070.   }
  1071.  
  1072.   return -1;
  1073. }
  1074.  
  1075. sub pump_get_device
  1076. {
  1077.   my ($fd, $iface) = @_;
  1078.   my ($opt);
  1079.   
  1080.   while (($opt = &pump_get_next_option ($fd)) != -1)
  1081.   {
  1082.     if ($$opt[0] eq "device")
  1083.     {
  1084.       $$opt[1] =~ s/[ \t]*\{//;
  1085.       return 1 if $$opt[1] eq $iface;
  1086.     }
  1087.   }
  1088.  
  1089.   return 0;
  1090. }
  1091.  
  1092. sub get_pump_iface_option_ref
  1093. {
  1094.   my ($file, $iface, $key) = @_;
  1095.   my ($fd, $opt, $ret);
  1096.  
  1097.   $fd = &Utils::File::open_read_from_names ($file);
  1098.  
  1099.   if (&pump_get_device ($fd, $iface))
  1100.   {
  1101.     while (($opt = &pump_get_next_option ($fd)) != -1)
  1102.     {
  1103.       if ($$opt[0] eq $key)
  1104.       {
  1105.         $ret = &unescape ($$opt[1]);
  1106.         return \$ret;
  1107.       }
  1108.       
  1109.       return -1 if ($$opt[0] eq "}");
  1110.     }
  1111.   }
  1112.  
  1113.   return -1;
  1114. }
  1115.  
  1116. sub get_pump_iface_kw
  1117. {
  1118.   my ($file, $iface, $key) = @_;
  1119.   my ($ret);
  1120.  
  1121.   return 1 if &get_pump_iface_option_ref ($file, $iface, $key) != -1;
  1122.   return 0;
  1123. }
  1124.  
  1125. sub get_pump_iface_kw_not
  1126. {
  1127.   my ($file, $iface, $key) = @_;
  1128.  
  1129.   return 0 if &get_pump_iface_option_ref ($file, $iface, $key) != -1;
  1130.   return 1;
  1131. }
  1132.  
  1133. # extracts hostname from a fully qualified hostname
  1134. # contained in a file
  1135. sub get_fq_hostname
  1136. {
  1137.   my ($file) = @_;
  1138.   my ($ret);
  1139.  
  1140.   $ret = &get_first_line ($file);
  1141.   $ret =~ s/\..*//; #remove domain
  1142.  
  1143.   return $ret;
  1144. }
  1145.  
  1146. # extracts domain from a fully qualified hostname
  1147. # contained in a file
  1148. sub get_fq_domain
  1149. {
  1150.   my ($file) = @_;
  1151.   my ($ret);
  1152.  
  1153.   $ret = &get_first_line ($file);
  1154.   $ret =~ s/^[^\.]*\.//;
  1155.  
  1156.   return $ret;
  1157. }
  1158.  
  1159. sub get_rcinet1conf
  1160. {
  1161.   my ($file, $iface, $kw) = @_;
  1162.   my ($line, $val);
  1163.  
  1164.   $iface =~ s/eth//;
  1165.  
  1166.   #we must double escape those []
  1167.   $line = "$kw\\[$iface\\]";
  1168.   $val = &get_sh ($file, $line);
  1169.  
  1170.   return undef if ($val eq "");
  1171.   return $val;
  1172. }
  1173.  
  1174. sub get_rcinet1conf_bool
  1175. {
  1176.   my ($file, $iface, $kw) = @_;
  1177.   my ($ret);
  1178.  
  1179.   $ret = &get_rcinet1conf ($file, $iface, $kw);
  1180.   
  1181.   return undef if ($ret eq undef);
  1182.   return (&Utils::Util::read_boolean ($ret)? 1: 0);
  1183. }
  1184.  
  1185. # function for parsing /etc/start_if.$iface files in FreeBSD
  1186. sub get_startif
  1187. {
  1188.   my ($file, $regex) = @_;
  1189.   my ($fd, $line, $val);
  1190.  
  1191.   $fd  = &Utils::File::open_read_from_names ($file);
  1192.   $val = undef;
  1193.  
  1194.   return undef if ($fd eq undef);
  1195.  
  1196.   while (<$fd>)
  1197.   {
  1198.     chomp;
  1199.  
  1200.     # ignore comments
  1201.     next if (/^\#/);
  1202.                  
  1203.     if (/$regex/)
  1204.     {
  1205.       $val = $1;
  1206.     }
  1207.   }
  1208.  
  1209.   # remove double quote
  1210.   if ($val =~ /\"(.*)\"/)
  1211.   {
  1212.     $val = $1;
  1213.   }
  1214.  
  1215.   return $val;
  1216. }
  1217.  
  1218. # functions for parsing /etc/ppp/ppp.conf sections in FreeBSD
  1219. sub pppconf_find_next_stanza
  1220. {
  1221.   my ($buff, $line_no) = @_;
  1222.  
  1223.   $line_no = 0 if ($line_no eq undef);
  1224.  
  1225.   while ($$buff[$line_no] ne undef)
  1226.   {
  1227.     if ($$buff[$line_no] !~ /^[\#\n]/)
  1228.     {
  1229.       return $line_no if ($$buff[$line_no] =~ /^[^ \t]+/);
  1230.     }
  1231.  
  1232.     $line_no++;
  1233.   }
  1234.  
  1235.   return -1;
  1236. }
  1237.  
  1238. sub pppconf_find_stanza
  1239. {
  1240.   my ($buff, $section) = @_;
  1241.   my ($line_no) = 0;
  1242.   
  1243.   while (($line_no = &pppconf_find_next_stanza ($buff, $line_no)) != -1)
  1244.   {
  1245.     return $line_no if ($$buff[$line_no] =~ /^$section\:/);
  1246.     $line_no++;
  1247.   }
  1248.  
  1249.   return -1;
  1250. }
  1251.  
  1252. sub get_pppconf_common
  1253. {
  1254.   my ($file, $section, $key) = @_;
  1255.   my ($fd, $val);
  1256.  
  1257.   $fd = &Utils::File::open_read_from_names ($file);
  1258.   return undef if ($fd eq undef);
  1259.  
  1260.   $val = undef;
  1261.  
  1262.   # First of all, we must find the line where the section begins
  1263.   while (<$fd>)
  1264.   {
  1265.     chomp;
  1266.     last if (/^$section\:[ \t]*/);
  1267.   }
  1268.  
  1269.   while (<$fd>)
  1270.   {
  1271.     chomp;
  1272.  
  1273.     # read until the next section arrives
  1274.     last if (/^[^ \t]/);
  1275.  
  1276.     next if (/^\#/);
  1277.  
  1278.     if (/^[ \t]+(add|set|enable|disable)[ \t]+$key/)
  1279.     {
  1280.       $val = $_;
  1281.       last;
  1282.     }
  1283.   }
  1284.  
  1285.   # this is done because commands can be multiline
  1286.   while (<$fd>)
  1287.   {
  1288.     last if (/^[^ \t]/);
  1289.     last if ($val !~ /\\$/);
  1290.  
  1291.     s/^[ \t]*/ /;
  1292.     $val =~ s/\\$//;
  1293.     $val .= $_;
  1294.   }
  1295.  
  1296.   &Utils::File::close_file ($fd);
  1297.  
  1298.   if ($val eq undef)
  1299.   {
  1300.     return undef if ($section eq "default");
  1301.     return &get_pppconf_common ($file, "default", $key);
  1302.   }
  1303.   else
  1304.   {
  1305.     $val =~ s/\#[^\#]*$//;
  1306.     $val =~ s/[ \t]*$//;
  1307.     $val =~ s/^[ \t]*//;
  1308.     return $val;
  1309.   }
  1310. }
  1311.  
  1312. sub get_pppconf
  1313. {
  1314.   my ($file, $section, $key) = @_;
  1315.   my ($val);
  1316.  
  1317.   $val = &get_pppconf_common ($file, $section, $key);
  1318.  
  1319.   if ($val =~ /$key[ \t]+(.+)/)
  1320.   {
  1321.     return $1;
  1322.   }
  1323. }
  1324.  
  1325. sub get_pppconf_bool
  1326. {
  1327.   my ($file, $section, $key) = @_;
  1328.   my ($val);
  1329.  
  1330.   $val = &get_pppconf_common ($file, $section, $key);
  1331.  
  1332.   return 1 if ($val ne undef);
  1333.   return 0;
  1334. }
  1335.  
  1336. sub get_pppconf_re
  1337. {
  1338.   my ($file, $section, $key, $re) = @_;
  1339.   my ($val);
  1340.  
  1341.   $val = &get_pppconf_common ($file, $section, $key);
  1342.  
  1343.   if ($val =~ /$re/i)
  1344.   {
  1345.     return $1;
  1346.   }
  1347. }
  1348.  
  1349. sub get_ppp_options_re
  1350. {
  1351.   my ($file, $re) = @_;
  1352.   my ($fd, @res);
  1353.  
  1354.   &Utils::Report::enter ();
  1355.   &Utils::Report::do_report ("network_get_ppp_option", &Utils::Replace::regexp_to_separator ($re), $file);
  1356.   $fd = &Utils::File::open_read_from_names ("$file");
  1357.   &Utils::Report::leave ();
  1358.  
  1359.   return undef if !$fd;
  1360.  
  1361.   while (($_ = &chomp_line_hash_comment ($fd)) != -1)
  1362.   {
  1363.     $_ = $$_;
  1364.  
  1365.     if (/$re/)
  1366.     {
  1367.       return $1;
  1368.     }
  1369.   }
  1370.  
  1371.   return undef;
  1372. }
  1373.  
  1374. sub get_confd_net
  1375. {
  1376.   my ($file, $key) = @_;
  1377.   my ($str, $contents, $i);
  1378.  
  1379.   $contents = &Utils::File::load_buffer ($file);
  1380.  
  1381.   for ($i = 0; $i <= scalar (@$contents); $i++)
  1382.   {
  1383.     # search for key
  1384.     if ($$contents[$i] =~ /^$key[ \t]*=[ \t]*\(/)
  1385.     {
  1386.       # contents can be multiline,
  1387.       # just get the first value
  1388.       do {
  1389.         $$contents[$i] =~ /\"([^\"]*)\"/;
  1390.         $str = $1;
  1391.         $i++;
  1392.       } while (!$str);
  1393.     }
  1394.   }
  1395.  
  1396.   return $str;
  1397. }
  1398.  
  1399. sub get_confd_net_re
  1400. {
  1401.   my ($file, $key, $re) = @_;
  1402.   my ($str);
  1403.  
  1404.   $str = &get_confd_net ($file, $key);
  1405.  
  1406.   if ($str =~ /$re/i)
  1407.   {
  1408.     return $1;
  1409.   }
  1410. }
  1411.  
  1412. 1;
  1413.