home *** CD-ROM | disk | FTP | other *** search
/ PC Welt 2006 November (DVD) / PCWELT_11_2006.ISO / casper / filesystem.squashfs / usr / share / system-tools-backends-2.0 / scripts / Utils / Parse.pm < prev    next >
Encoding:
Perl POD Document  |  2006-08-14  |  28.2 KB  |  1,452 lines

  1. #!/usr/bin/env 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, $substr, $value);
  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.     @s = split ($sep, $i);
  412.     push @ret, @s[0];
  413.   }
  414.  
  415.   return \@ret;
  416. }
  417.  
  418. # Same, but for $all = 0. (ie nameserver 10.0.0.1)
  419. sub split_first_str
  420. {
  421.   my ($file, $key, $re, $line_read_proc) = @_;
  422.  
  423.   return &split ($file, $key, $re, 0, $line_read_proc);
  424. }
  425.  
  426. # Interpret the result as a boolean. (ie multi on)
  427. sub split_first_bool
  428. {
  429.   my ($file, $key, $re, $line_read_proc) = @_;
  430.   my $ret;
  431.  
  432.   $ret = &split_first_str ($file, $key, $re, $line_read_proc);
  433.  
  434.   return undef if ($ret eq undef);
  435.   return (&Utils::Util::read_boolean ($ret)? 1: 0);
  436. }
  437.  
  438. # After getting the first field, split the result with $sep matching separators. (ie order hosts,bind)
  439. sub split_first_array
  440. {
  441.   my ($file, $key, $re, $sep, $line_read_proc) = @_;
  442.   my @ret;
  443.  
  444.   @ret = split ($sep, &split ($file, $key, $re, 0, $line_read_proc));
  445.  
  446.   return \@ret;
  447. }
  448.  
  449. sub split_first_array_pos
  450. {
  451.   my ($file, $key, $pos, $re, $sep, $line_read_proc) = @_;
  452.   my (@ret);
  453.  
  454.   @ret = split ($sep, &split ($file, $key, $re, 0, $line_read_proc));
  455.   return $ret[$pos];
  456. }
  457.  
  458. # Do an split_first_array and then make
  459. # the array elements unique. This is to fix broken
  460. # searchdomain entries in /etc/resolv.conf, for example.
  461. sub split_first_array_unique
  462. {
  463.   my ($file, $key, $re, $sep, $line_read_proc) = @_;
  464.   my (@arr, @res);
  465.   my (%hash, $i);
  466.  
  467.   @arr = split ($sep, &split ($file, $key, $re, 0, $line_read_proc));
  468.  
  469.   foreach $i (@arr)
  470.   {
  471.     next if exists $hash{$i};
  472.     $hash{$i} = 1;
  473.     push @res, $i;
  474.   }
  475.  
  476.   return \@res;
  477. }
  478.  
  479. # For all keys in $file, sepparated from its values
  480. # by $key_re, sepparate its values using $value_re
  481. # and assign to a newly created hash. Use ONLY when
  482. # you don't know what keys you are going to parse
  483. # (i.e. /etc/hosts). Any other application will not
  484. # be very portable and should be avoided.
  485. sub split_hash
  486. {
  487.   my ($file, $key_re, $value_re) = @_;
  488.   my ($fd, @line, %res, $key);
  489.  
  490.   &Utils::Report::enter ();
  491.   &Utils::Report::do_report ("parse_split_hash", $file);
  492.   
  493.   $fd = &Utils::File::open_read_from_names ($file);
  494.   
  495.   while (<$fd>)
  496.   {
  497.     chomp;
  498.     s/^[ \t]+//;
  499.     s/[ \t]+$//;
  500.     s/\#.*$//;
  501.     next if (/^$/);
  502.     @line = split ($key_re, $_, 2);
  503.  
  504.     $key = shift (@line);
  505.     push @{$res{$key}}, split ($value_re, $line[0]);
  506.   }
  507.  
  508.   &Utils::File::close_file ($fd);
  509.   &Utils::Report::leave ();
  510.   return undef if (scalar keys (%res) == 0);
  511.   return \%res;
  512. }
  513.  
  514. # Same as above, but join lines that end with '\'.
  515. sub split_hash_with_continuation
  516. {
  517.   my ($file, $key_re, $value_re) = @_;
  518.   my ($fd, $l, @line, %res, $key);
  519.  
  520.   &Utils::Report::enter ();
  521.   &Utils::Report::do_report ("parse_split_hash_cont", $file);
  522.   
  523.   $fd = &Utils::File::open_read_from_names ($file);
  524.   
  525.   while (($l = &ini_line_read ($fd)) != -1)
  526.   {
  527.     $_ = $$l;
  528.     chomp;
  529.     s/^[ \t]+//;
  530.     s/[ \t]+$//;
  531.     s/\#.*$//;
  532.     next if (/^$/);
  533.     @line = split ($key_re, $_, 2);
  534.  
  535.     $key = shift (@line);
  536.     $res{$key} = [ split ($value_re, $line[0]) ];
  537.   }
  538.  
  539.   &Utils::File::close_file ($fd);
  540.   &Utils::Report::leave ();
  541.   return undef if (scalar keys (%res) == 0);
  542.   return \%res;
  543. }
  544.  
  545. # Remove escape sequences in a shell value.
  546. sub unescape
  547. {
  548.   my $ret = $_[0];
  549.  
  550.   # Quote shell special chars.
  551.   $ret =~ s/\\\"/\\_/g;
  552.   $ret =~ s/\"//g;
  553.   $ret =~ s/\\_/\"/g;
  554.   $ret =~ s/\\\'/\\_/g;
  555.   $ret =~ s/\'//g;
  556.   $ret =~ s/\\_/\'/g;
  557.   $ret =~ s/\\(.)/$1/g;
  558.  
  559.   return $ret;
  560. }
  561.  
  562. # unescape (escape (x)) == x
  563. sub escape
  564. {
  565.   my ($value) = @_;
  566.   
  567.   $value =~ s/([\"\`\$\\])/\\$1/g;
  568.   $value = "\"$value\"" if ($value =~ /[ \t\'&|*?\[\]\{\}\{\}<>]/);
  569.  
  570.   return $value;
  571. }
  572.  
  573. # For files which are a list of /bin/sh shell variable declarations. (ie GATEWAY=10.10.10.1)
  574. sub get_sh
  575. {
  576.   my ($file, $key) = @_;
  577.   my $ret;
  578.  
  579.   &Utils::Report::enter ();
  580.   &Utils::Report::do_report ("parse_sh", $key, $file);
  581.   $ret = &split_first_str ($file, $key, "[ \t]*=[ \t]*",
  582.                                      \&chomp_line_hash_comment);
  583.   &Utils::Report::leave ();
  584.  
  585.   return &unescape ($ret);
  586. }
  587.  
  588. # Same, but interpret the returning value as a bool. (ie NETWORKING=yes)
  589. sub get_sh_bool
  590. {
  591.   my ($file, $key) = @_;
  592.   my $ret;
  593.  
  594.   $ret = &get_sh ($file, $key);
  595.  
  596.   return undef if ($ret eq undef);
  597.   return (&Utils::Util::read_boolean ($ret)? 1: 0);
  598. }
  599.  
  600. # Get an sh value and then split with $re, returning ref to resulting array.
  601. sub get_sh_split
  602. {
  603.   my ($file, $key, $re) = @_;
  604.   my (@ret, $val);
  605.  
  606.   $val = &get_sh ($file, $key);
  607.   @ret = split ($re, $val);
  608.  
  609.   return \@ret;
  610. }
  611.  
  612. # Get a fully qualified hostname from a $key shell var in $file
  613. # and extract the hostname from there. e.g.: suse70's /etc/rc.config's FQHOSTNAME.
  614. sub get_sh_hostname
  615. {
  616.   my ($file, $key) = @_;
  617.   my ($val);
  618.  
  619.   $val = &get_sh_split ($file, $key, "\\.");
  620.  
  621.   return $$val[0];
  622. }
  623.  
  624. # Get a fully qualified hostname from a $key shell var in $file
  625. # and extract the domain from there. e.g.: suse70's /etc/rc.config's FQHOSTNAME.
  626. sub get_sh_domain
  627. {
  628.   my ($file, $key) = @_;
  629.   my ($val);
  630.  
  631.   $val = &get_sh_split ($file, $key, "\\.");
  632.  
  633.   return join ".", @$val[1..$#$val];
  634. }
  635.  
  636. # For files which are a list of /bin/sh shell variable exports. (eg export GATEWAY=10.10.10.1)
  637. sub get_sh_export
  638. {
  639.   my ($file, $key) = @_;
  640.   my $ret;
  641.  
  642.   &Utils::Report::enter ();
  643.   &Utils::Report::do_report ("parse_sh", $key, $file);
  644.   $ret = &split_first_str ($file, $key, "[ \t]*=[ \t]*",
  645.                                      \&chomp_line_sh_export);
  646.   &Utils::Report::leave ();
  647.  
  648.   return &unescape ($ret);
  649. }
  650.  
  651. # Same, but interpret the returing value as a bool. (ie export NETWORKING=yes)
  652. sub get_sh_export_bool
  653. {
  654.   my ($file, $key) = @_;
  655.   my $ret;
  656.  
  657.   $ret = &get_sh_export ($file, $key);
  658.  
  659.   return undef if ($ret eq undef);
  660.   return (&Utils::Util::read_boolean ($ret)? 1: 0);
  661. }
  662.  
  663. # Same, but accepting a regexp and returning the value between the paren operator
  664. sub get_sh_re
  665. {
  666.   my ($file, $key, $re) = @_;
  667.   my $ret;
  668.  
  669.   $ret = &get_sh ($file, $key);
  670.  
  671.   $ret =~ /$re/i;
  672.   return $1;
  673. }
  674.  
  675.  
  676. # Search for $keyword in $file, delimited by $re (default " ") or EOL.
  677. # If keyword exists, return 1, else 0.
  678. sub get_kw
  679. {
  680.   my ($file, $keyword, $re, $line_read_proc) = @_;
  681.   my $res;
  682.  
  683.   &Utils::Report::enter ();
  684.   &Utils::Report::do_report ("parse_kw", $keyword, $file);
  685.   
  686.   if (! -f "$gst_prefix/$file")
  687.   {
  688.     &Utils::Report::enter ();
  689.     &Utils::Report::do_report ("file_open_read_failed", $file);
  690.     &Utils::Report::leave ();
  691.     &Utils::Report::leave ();
  692.     return undef;
  693.   }
  694.   
  695.   $re = " " if $re eq undef;
  696.   $res = &split_ref ($file, $keyword, $re, 0, $line_read_proc);
  697.  
  698.   &Utils::Report::leave ();
  699.   return 0 if $res == -1;
  700.   return 1;
  701. }
  702.  
  703. # A file containing the desired value in its first line. (ie /etc/hostname)
  704. sub get_first_line
  705. {
  706.   my ($file) = @_;
  707.   my ($fd, $res);
  708.  
  709.   &Utils::Report::enter ();
  710.   &Utils::Report::do_report ("parse_line_first", $file);
  711.   $fd = &Utils::File::open_read_from_names ($file);
  712.   &Utils::Report::leave ();
  713.   
  714.   return undef if !$fd;
  715.  
  716.   chomp ($res = <$fd>);
  717.   &Utils::File::close_file ($fd);
  718.   return $res;
  719. }
  720.  
  721. # parse a chat file, searching for an entry that matches $re.
  722. # $re must have one paren operator (ie "^atd[^0-9]*([0-9, -]+)").
  723. sub get_from_chatfile
  724. {
  725.   my ($file, $re) = @_;
  726.   my ($fd, $found);
  727.  
  728.   &Utils::Report::enter ();
  729.   &Utils::Report::do_report ("parse_chat", $file);
  730.   $fd = &Utils::File::open_read_from_names ("$file");
  731.   &Utils::Report::leave ();
  732.   return undef if !$fd;
  733.  
  734.   while (<$fd>)
  735.   {
  736.     # We'll be emptying $_ as we "scan".
  737.     chomp;
  738.     while ($_ ne "")
  739.     {
  740.       # If it uses quotes. FIXME: Assuming they surround the whole string.
  741.       if (/^\'/)
  742.       {
  743.         s/\'([^\']*)\' ?//;
  744.         $found = $1;
  745.       }
  746.       else
  747.       {
  748.         s/([^ \t]*) ?//;
  749.         $found = $1;
  750.       }
  751.       
  752.       # If it looks like what we're looking for, return what matched the parens.
  753.       if ($found =~ /$re/i)
  754.       {
  755.         &Utils::File::close_file ($fd);
  756.         return $1;
  757.       }
  758.     }
  759.   }
  760.   
  761.   &Utils::File::close_file ($fd);
  762.   # Oops: not found.
  763.   return undef;
  764. }
  765.  
  766. # Clean an ini line of comments and leading or
  767. # trailing spaces.
  768. sub ini_line_clean
  769. {
  770.   $_ = $_[0];
  771.   
  772.   chomp;
  773.   s/\#.*//;
  774.   s/;.*//;
  775.   s/^[ \t]+//;
  776.   s/[ \t]+$//;
  777.  
  778.   return $_;
  779. }
  780.  
  781. # Read an ini line, which may have to be joined
  782. # with the next one if it ends with '\'.
  783. sub ini_line_read
  784. {
  785.   my $fd = $_[0];
  786.   my $l;
  787.  
  788.   $l = <$fd>;
  789.   return -1 if ($l eq undef);
  790.   
  791.   $l = &ini_line_clean ($l);
  792.   while ($l =~ /\\$/)
  793.   {
  794.     $l =~ s/\\$//;
  795.     $l .= &ini_line_clean (scalar <$fd>);
  796.   }
  797.  
  798.   return \$l;
  799. }
  800.  
  801. # Return an array of all found sections in $file.
  802. sub get_ini_sections
  803. {
  804.   my ($file) = @_;
  805.   my (@sections, $line);
  806.  
  807.   $fd = &Utils::File::open_read_from_names ($file);
  808.   
  809.   while (($line = &ini_line_read ($fd)) != -1)
  810.   {
  811.     $_ = $$line;
  812.     next if (/^$/);
  813.     push @sections, $1 if (/\[([^\]]+)\]/i);
  814.   }
  815.  
  816.   &Utils::File::close_file ($fd);
  817.  
  818.   return @sections;
  819. }
  820.  
  821. # Get the value of a $var in a $section from $file.
  822. sub get_from_ini
  823. {
  824.   my ($file, $section, $var) = @_;
  825.   my ($fd, $res, $line);
  826.   my $found_section_flag = 0;
  827.  
  828.   &Utils::Report::enter ();
  829.   &Utils::Report::do_report ("parse_ini", $var, $file, $section);
  830.   $fd = &Utils::File::open_read_from_names ($file);
  831.   &Utils::Report::leave ();
  832.   $res = undef;
  833.   
  834.   while (($line = &ini_line_read ($fd)) != -1)
  835.   {
  836.     $_ = $$line;
  837.     next if (/^$/);
  838.  
  839.     if (/\[$section\]/i)
  840.     {
  841.       $found_section_flag = 1;
  842.       next;
  843.     }
  844.  
  845.     if ($found_section_flag)
  846.     {
  847.       if (/^$var[ \t]*=/i)
  848.       {
  849.         s/^$var[ \t]*=[ \t]*//i;
  850.         $res = $_;
  851.         last;
  852.       }
  853.       elsif (/\[\S+\]/i)
  854.       {
  855.         last;
  856.       }
  857.     }
  858.   }
  859.  
  860.   &Utils::File::close_file ($fd);
  861.  
  862.   return $res;
  863. }
  864.  
  865. # Same, but treat value as bool and return 1/0.
  866. sub get_from_ini_bool
  867. {
  868.   my ($file, $section, $var) = @_;
  869.   my $ret;
  870.   
  871.   $ret = &get_from_ini ($file, $section, $var);
  872.   
  873.   return 0 if ($ret eq undef);
  874.   return (&Utils::Util::read_boolean ($ret)? 1 : 0);
  875. }
  876.  
  877. # Debian interfaces(5) states that files starting with # are comments.
  878. # Also, leading and trailing spaces are ignored.
  879. sub interfaces_line_clean
  880. {
  881.   $_ = $_[0];
  882.   
  883.   chomp;
  884.   s/^[ \t]+//;
  885.   s/^\#.*//;
  886.   s/[ \t]+$//;
  887.  
  888.   return $_;
  889. }
  890.  
  891. # interfaces(5) also states that \ line continuation is possible.
  892. sub interfaces_line_read
  893. {
  894.   my $fd = $_[0];
  895.   my $l;
  896.  
  897.   $l = <$fd>;
  898.   return -1 if ($l eq undef);
  899.   
  900.   $l = &interfaces_line_clean ($l);
  901.   while ($l =~ /\\$/)
  902.   {
  903.     $l =~ s/\\$//;
  904.     $l .= &interfaces_line_clean (scalar <$fd>);
  905.   }
  906.  
  907.   return \$l;
  908. }
  909.  
  910. # Read lines until a stanza, a line starting with $stanza_type is found.
  911. # Return ref to an array with the stanza params split.
  912. sub interfaces_get_next_stanza
  913. {
  914.   my ($fd, $stanza_type) = @_;
  915.   my $line;
  916.  
  917.   while (($line = &interfaces_line_read ($fd)) != -1)
  918.   {
  919.     $_ = $$line;
  920.     if (/^$stanza_type[ \t]+[^ \t]/)
  921.     {
  922.       s/^$stanza_type[ \t]+//;
  923.       return [ split ("[ \t]+", $_) ];
  924.     }
  925.   }
  926.  
  927.   return -1;
  928. }
  929.  
  930. # Read lines until a line not recognized as a stanza is
  931. # found, and split in a "tuple" of key/value.
  932. sub interfaces_get_next_option
  933. {
  934.   my $fd = $_[0];
  935.   my $line;
  936.  
  937.   while (($line = &interfaces_line_read ($fd)) != -1)
  938.   {
  939.     $_ = $$line;
  940.     next if /^$/;
  941.     
  942.     return [ split ("[ \t]+", $_, 2) ] if (!/^iface[ \t]/);
  943.     return -1;
  944.   }
  945.  
  946.   return -1;
  947. }
  948.  
  949. # Get all stanzas from file. Return array.
  950. sub get_interfaces_stanzas
  951. {
  952.   my ($file, $stanza_type) = @_;
  953.   my ($fd, @res);
  954.  
  955.   $fd = &Utils::File::open_read_from_names ($file);
  956.   $res = undef;
  957.   
  958.   while (($_ = &interfaces_get_next_stanza ($fd, $stanza_type)) != -1)
  959.   {
  960.     push @res, $_;
  961.   }
  962.  
  963.   &Utils::File::close_file ($fd);
  964.  
  965.   return @res;
  966. }
  967.  
  968. # Find stanza for $iface in $file, and return
  969. # tuple for option with $key. Return -1 if unexisting.
  970. sub get_interfaces_option_tuple
  971. {
  972.   my ($file, $iface, $key, $all) = @_;
  973.   my ($fd, @res);
  974.  
  975.   $fd = &Utils::File::open_read_from_names ($file);
  976.  
  977.   while (($stanza = &interfaces_get_next_stanza ($fd, "iface")) != -1)
  978.   {
  979.     if ($$stanza[0] eq $iface)
  980.     {
  981.       while (($tuple = &interfaces_get_next_option ($fd)) != -1)
  982.       {
  983.         if ($$tuple[0] =~ /$key/)
  984.         {
  985.           return $tuple if !$all;
  986.           push @res, $tuple;
  987.         }
  988.       }
  989.  
  990.       return -1 if !$all;
  991.     }
  992.   }
  993.  
  994.   return @res if $all;
  995.   return -1;
  996. }
  997.  
  998. # Go get option $kw for $iface stanza. If found,
  999. # return 1 (true), else, false.
  1000. sub get_interfaces_option_kw
  1001. {
  1002.   my ($file, $iface, $kw) = @_;
  1003.   my $tuple;
  1004.  
  1005.   &Utils::Report::enter ();
  1006.   &Utils::Report::do_report ("parse_ifaces_kw", $kw, $file);
  1007.   $tuple = &get_interfaces_option_tuple ($file, $iface, $kw);
  1008.   &Utils::Report::leave ();
  1009.  
  1010.   if ($tuple != -1)
  1011.   {
  1012.     &Utils::Report::do_report ("parse_ifaces_kw_strange", $iface, $file) if ($$tuple[1] ne "");
  1013.  
  1014.     return 1;
  1015.   }
  1016.  
  1017.   return 0;
  1018. }
  1019.  
  1020. # For such keywords as noauto, whose existence means
  1021. # a false value.
  1022. sub get_interfaces_option_kw_not
  1023. {
  1024.   my ($file, $iface, $kw) = @_;
  1025.   
  1026.   return &get_interfaces_option_kw ($file, $iface, $kw)? 0 : 1;
  1027. }
  1028.  
  1029. # Go get option $key for $iface in $file and return value.
  1030. sub get_interfaces_option_str
  1031. {
  1032.   my ($file, $iface, $key) = @_;
  1033.   my $tuple;
  1034.  
  1035.   &Utils::Report::enter ();
  1036.   &Utils::Report::do_report ("parse_ifaces_str", $kw, $file);
  1037.   $tuple = &get_interfaces_option_tuple ($file, $iface, $key);
  1038.   &Utils::Report::leave ();
  1039.  
  1040.   if ($tuple != -1)
  1041.   {
  1042.     return $$tuple[1];
  1043.   }
  1044.  
  1045.   return undef;
  1046. }
  1047.  
  1048.  
  1049. # Implementing pump(8) pump.conf file format parser.
  1050. # May be useful for dhcpd too.
  1051. sub pump_get_next_option
  1052. {
  1053.   my ($fd) = @_;
  1054.   my $line;
  1055.  
  1056.   while (($line = &interfaces_line_read ($fd)) != -1)
  1057.   {
  1058.     $line = $$line;
  1059.     if ($line ne "")
  1060.     {
  1061.       return [ split ("[ \t]+", $line, 2) ];
  1062.     }
  1063.   }
  1064.  
  1065.   return -1;
  1066. }
  1067.  
  1068. sub pump_get_device
  1069. {
  1070.   my ($fd, $iface) = @_;
  1071.   my ($opt);
  1072.   
  1073.   while (($opt = &pump_get_next_option ($fd)) != -1)
  1074.   {
  1075.     if ($$opt[0] eq "device")
  1076.     {
  1077.       $$opt[1] =~ s/[ \t]*\{//;
  1078.       return 1 if $$opt[1] eq $iface;
  1079.     }
  1080.   }
  1081.  
  1082.   return 0;
  1083. }
  1084.  
  1085. sub get_pump_iface_option_ref
  1086. {
  1087.   my ($file, $iface, $key) = @_;
  1088.   my ($fd, $opt, $ret);
  1089.  
  1090.   $fd = &Utils::File::open_read_from_names ($file);
  1091.  
  1092.   if (&pump_get_device ($fd, $iface))
  1093.   {
  1094.     while (($opt = &pump_get_next_option ($fd)) != -1)
  1095.     {
  1096.       if ($$opt[0] eq $key)
  1097.       {
  1098.         $ret = &unescape ($$opt[1]);
  1099.         return \$ret;
  1100.       }
  1101.       
  1102.       return -1 if ($$opt[0] eq "}");
  1103.     }
  1104.   }
  1105.  
  1106.   return -1;
  1107. }
  1108.  
  1109. sub get_pump_iface_kw
  1110. {
  1111.   my ($file, $iface, $key) = @_;
  1112.   my ($ret);
  1113.  
  1114.   return 1 if &get_pump_iface_option_ref ($file, $iface, $key) != -1;
  1115.   return 0;
  1116. }
  1117.  
  1118. sub get_pump_iface_kw_not
  1119. {
  1120.   my ($file, $iface, $key) = @_;
  1121.  
  1122.   return 0 if &get_pump_iface_option_ref ($file, $iface, $key) != -1;
  1123.   return 1;
  1124. }
  1125.  
  1126. # extracts hostname from a fully qualified hostname
  1127. # contained in a file
  1128. sub get_fq_hostname
  1129. {
  1130.   my ($file) = @_;
  1131.   my ($ret);
  1132.  
  1133.   $ret = &get_first_line ($file);
  1134.   $ret =~ s/\..*//; #remove domain
  1135.  
  1136.   return $ret;
  1137. }
  1138.  
  1139. # extracts domain from a fully qualified hostname
  1140. # contained in a file
  1141. sub get_fq_domain
  1142. {
  1143.   my ($file) = @_;
  1144.   my ($ret);
  1145.  
  1146.   $ret = &get_first_line ($file);
  1147.   $ret =~ s/^[^\.]*\.//;
  1148.  
  1149.   return $ret;
  1150. }
  1151.  
  1152. sub get_rcinet1conf
  1153. {
  1154.   my ($file, $iface, $kw) = @_;
  1155.   my ($line);
  1156.  
  1157.   $iface =~ s/eth//;
  1158.  
  1159.   #we must double escape those []
  1160.   $line = "$kw\\[$iface\\]";
  1161.  
  1162.   return &get_sh ($file, $line);
  1163. }
  1164.  
  1165. sub get_rcinet1conf_bool
  1166. {
  1167.   my ($file, $iface, $kw) = @_;
  1168.   my ($ret);
  1169.  
  1170.   $ret = &get_rcinet1conf ($file, $iface, $kw);
  1171.   
  1172.   return undef if ($ret eq undef);
  1173.   return (&Utils::Util::read_boolean ($ret)? 1: 0);
  1174. }
  1175.  
  1176. sub get_wireless_opts
  1177. {
  1178.   my ($file, $iface, $proc, $kw) = @_;
  1179.   my $ifaces = &$proc ();
  1180.   my $found = 0;
  1181.   my $search = 1;
  1182.   my $val = "";
  1183.   my $fd;
  1184.   
  1185.   foreach $i (@$ifaces)
  1186.   {
  1187.     $found = 1 if ($iface eq $i);
  1188.   }
  1189.  
  1190.   return undef if (!$found);
  1191.  
  1192.   $fd = &Utils::File::open_read_from_names ($file);
  1193.   while (<$fd>)
  1194.   {
  1195.     $line = $_;
  1196.  
  1197.     if ($line =~ /^case/)
  1198.     {
  1199.       # we don't want to search inside the case
  1200.       $search = 0;
  1201.     }
  1202.     elsif ($line =~ /^esac/)
  1203.     {
  1204.       # continue searching
  1205.       $search = 1;
  1206.     }
  1207.     elsif (($line =~ /^[ \t]*$kw/ ) && ($search))
  1208.     {
  1209.       $line =~ s/.*=//;
  1210.  
  1211.       if ($line =~ /"(.*)"/)
  1212.       {
  1213.         $line = $1;
  1214.       }
  1215.  
  1216.       $val = $line;
  1217.     }
  1218.   }
  1219.  
  1220.   &Utils::File::close_file ($fd);
  1221.   return $val;
  1222. }
  1223.  
  1224. # function for parsing /etc/start_if.$iface files in FreeBSD
  1225. sub get_startif
  1226. {
  1227.   my ($file, $regex) = @_;
  1228.   my ($fd, $line, $val);
  1229.  
  1230.   $fd  = &Utils::File::open_read_from_names ($file);
  1231.   $val = undef;
  1232.  
  1233.   return undef if ($fd eq undef);
  1234.  
  1235.   while (<$fd>)
  1236.   {
  1237.     chomp;
  1238.  
  1239.     # ignore comments
  1240.     next if (/^\#/);
  1241.                  
  1242.     if (/$regex/)
  1243.     {
  1244.       $val = $1;
  1245.     }
  1246.   }
  1247.  
  1248.   # remove double quote
  1249.   if ($val =~ /\"(.*)\"/)
  1250.   {
  1251.     $val = $1;
  1252.   }
  1253.  
  1254.   return $val;
  1255. }
  1256.  
  1257. # functions for parsing /etc/ppp/ppp.conf sections in FreeBSD
  1258. sub pppconf_find_next_stanza
  1259. {
  1260.   my ($buff, $line_no) = @_;
  1261.  
  1262.   $line_no = 0 if ($line_no eq undef);
  1263.  
  1264.   while ($$buff[$line_no] ne undef)
  1265.   {
  1266.     if ($$buff[$line_no] !~ /^[\#\n]/)
  1267.     {
  1268.       return $line_no if ($$buff[$line_no] =~ /^[^ \t]+/);
  1269.     }
  1270.  
  1271.     $line_no++;
  1272.   }
  1273.  
  1274.   return -1;
  1275. }
  1276.  
  1277. sub pppconf_find_stanza
  1278. {
  1279.   my ($buff, $section) = @_;
  1280.   my ($line_no) = 0;
  1281.   
  1282.   while (($line_no = &pppconf_find_next_stanza ($buff, $line_no)) != -1)
  1283.   {
  1284.     return $line_no if ($$buff[$line_no] =~ /^$section\:/);
  1285.     $line_no++;
  1286.   }
  1287.  
  1288.   return -1;
  1289. }
  1290.  
  1291. sub get_pppconf_common
  1292. {
  1293.   my ($file, $section, $key) = @_;
  1294.   my ($fd, $val);
  1295.  
  1296.   $fd = &Utils::File::open_read_from_names ($file);
  1297.   return undef if ($fd eq undef);
  1298.  
  1299.   $val = undef;
  1300.  
  1301.   # First of all, we must find the line where the section begins
  1302.   while (<$fd>)
  1303.   {
  1304.     chomp;
  1305.     last if (/^$section\:[ \t]*/);
  1306.   }
  1307.  
  1308.   while (<$fd>)
  1309.   {
  1310.     chomp;
  1311.  
  1312.     # read until the next section arrives
  1313.     last if (/^[^ \t]/);
  1314.  
  1315.     next if (/^\#/);
  1316.  
  1317.     if (/^[ \t]+(add|set|enable|disable)[ \t]+$key/)
  1318.     {
  1319.       $val = $_;
  1320.       last;
  1321.     }
  1322.   }
  1323.  
  1324.   # this is done because commands can be multiline
  1325.   while (<$fd>)
  1326.   {
  1327.     last if (/^[^ \t]/);
  1328.     last if ($val !~ /\\$/);
  1329.  
  1330.     s/^[ \t]*/ /;
  1331.     $val =~ s/\\$//;
  1332.     $val .= $_;
  1333.   }
  1334.  
  1335.   &Utils::File::close_file ($fd);
  1336.  
  1337.   if ($val eq undef)
  1338.   {
  1339.     return undef if ($section eq "default");
  1340.     return &get_pppconf_common ($file, "default", $key);
  1341.   }
  1342.   else
  1343.   {
  1344.     $val =~ s/\#[^\#]*$//;
  1345.     $val =~ s/[ \t]*$//;
  1346.     $val =~ s/^[ \t]*//;
  1347.     return $val;
  1348.   }
  1349. }
  1350.  
  1351. sub get_pppconf
  1352. {
  1353.   my ($file, $section, $key) = @_;
  1354.   my ($val);
  1355.  
  1356.   $val = &get_pppconf_common ($file, $section, $key);
  1357.  
  1358.   if ($val =~ /$key[ \t]+(.+)/)
  1359.   {
  1360.     return $1;
  1361.   }
  1362. }
  1363.  
  1364. sub get_pppconf_bool
  1365. {
  1366.   my ($file, $section, $key) = @_;
  1367.   my ($val);
  1368.  
  1369.   $val = &get_pppconf_common ($file, $section, $key);
  1370.  
  1371.   return 1 if ($val ne undef);
  1372.   return 0;
  1373. }
  1374.  
  1375. sub get_pppconf_re
  1376. {
  1377.   my ($file, $section, $key, $re) = @_;
  1378.   my ($val);
  1379.  
  1380.   $val = &get_pppconf_common ($file, $section, $key);
  1381.  
  1382.   if ($val =~ /$re/i)
  1383.   {
  1384.     return $1;
  1385.   }
  1386. }
  1387.  
  1388. sub get_ppp_options_re
  1389. {
  1390.   my ($file, $re) = @_;
  1391.   my ($fd, @res);
  1392.  
  1393.   &Utils::Report::enter ();
  1394.   &Utils::Report::do_report ("network_get_ppp_option", &Utils::Replace::regexp_to_separator ($re), $file);
  1395.   $fd = &Utils::File::open_read_from_names ("$file");
  1396.   &Utils::Report::leave ();
  1397.  
  1398.   return undef if !$fd;
  1399.  
  1400.   while (($_ = &chomp_line_hash_comment ($fd)) != -1)
  1401.   {
  1402.     $_ = $$_;
  1403.  
  1404.     if (/$re/)
  1405.     {
  1406.       return $1;
  1407.     }
  1408.   }
  1409.  
  1410.   return undef;
  1411. }
  1412.  
  1413. sub get_confd_net
  1414. {
  1415.   my ($file, $key) = @_;
  1416.   my ($str, $contents, $i);
  1417.  
  1418.   $contents = &Utils::File::load_buffer ($file);
  1419.  
  1420.   for ($i = 0; $i <= scalar (@$contents); $i++)
  1421.   {
  1422.     # search for key
  1423.     if ($$contents[$i] =~ /^$key[ \t]*=[ \t]*\(/)
  1424.     {
  1425.       # contents can be multiline,
  1426.       # just get the first value
  1427.       do {
  1428.         $$contents[$i] =~ /\"([^\"]*)\"/;
  1429.         $str = $1;
  1430.         $i++;
  1431.       } while (!$str);
  1432.     }
  1433.   }
  1434.  
  1435.   return $str;
  1436. }
  1437.  
  1438. sub get_confd_net_re
  1439. {
  1440.   my ($file, $key, $re) = @_;
  1441.   my ($str);
  1442.  
  1443.   $str = &get_confd_net ($file, $key);
  1444.  
  1445.   if ($str =~ /$re/i)
  1446.   {
  1447.     return $1;
  1448.   }
  1449. }
  1450.  
  1451. 1;
  1452.