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 / Replace.pm < prev    next >
Encoding:
Perl POD Document  |  2006-08-14  |  31.8 KB  |  1,411 lines

  1. #!/usr/bin/env perl
  2. #-*- Mode: perl; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  3.  
  4. # replace.pl: Common in-line replacing 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. #          Michael Vogt <mvo@debian.org> - Debian 2.[2|3] support.
  11. #          David Lee Ludwig <davidl@wpi.edu> - Debian 2.[2|3] support.
  12. #
  13. # This program is free software; you can redistribute it and/or modify
  14. # it under the terms of the GNU Library General Public License as published
  15. # by the Free Software Foundation; either version 2 of the License, or
  16. # (at your option) any later version.
  17. #
  18. # This program is distributed in the hope that it will be useful,
  19. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21. # GNU Library General Public License for more details.
  22. #
  23. # You should have received a copy of the GNU Library General Public License
  24. # along with this program; if not, write to the Free Software
  25. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
  26.  
  27. package Utils::Replace;
  28.  
  29. use Utils::Util;
  30. use Utils::File;
  31. use Utils::Parse;
  32.  
  33.  
  34. # General rules: all replacing is in-line. Respect unsupported values, comments
  35. # and as many spacing as possible.
  36.  
  37. # The concept of keyword (kw) here is a key, normaly in its own line, whose
  38. # boolean representation is its own existence.
  39.  
  40. # A $re is a regular expression. In most functions here, regular expressions
  41. # are converted to simple separators, by using gst_replace_regexp_to_separator.
  42. # This makes it easier to convert a parse table into a replace table.
  43.  
  44. # Every final replacing function to be used by a table must handle one key
  45. # at a time, but may replace several values from there.
  46. #
  47. # Return 0 for success, and -1 for failure.
  48. #
  49. # Most of these functions have a parsing counterpart. The convention is
  50. # that parse becomes replace and split becomes join:
  51. # split_first_str -> join_first_str
  52.  
  53. # Additional abstraction: replace table entries can have
  54. # arrays inside. The replace proc will be ran with every
  55. # combination that the arrays provide. Ex:
  56. # ["user", \&gst_replace_foo, [0, 1], [2, 3] ] will replace
  57. # using all possibilities in the combinatory of [0, 1]x[2, 3].
  58. # Check RedHat 7.2's network replace table for further
  59. # enlightenment.
  60. sub run_entry
  61. {
  62.   my ($values_hash, $key, $proc, $cp, $value) = @_;
  63.   my ($ncp, $i, $j, $res);
  64.  
  65.   $ncp = [@$cp];
  66.   for ($i = 0; $i < scalar (@$cp); $i ++)
  67.   {
  68.       if (ref $$cp[$i] eq "ARRAY")
  69.       {
  70.           foreach $j (@{$$cp[$i]})
  71.           {
  72.               $$ncp[$i] = $j;
  73.               $res = -1 if &run_entry ($values_hash, $key, $proc, $ncp, $value);
  74.           }
  75.           return $res;
  76.       }
  77.   }
  78.   
  79.   # OK, the given entry didn't have any array refs in it...
  80.   
  81.   return -1 if (!&Utils::Parse::replace_hash_values ($ncp, $values_hash));
  82.   push (@$ncp, $$values_hash{$key}) unless $key eq "_always_";
  83.   $res = -1 if &$proc (@$ncp);
  84.   return $res;
  85. }
  86.  
  87. # gst_replace_from_table takes a file mapping, a replace table, a hash
  88. # of values, probably made from XML parsing, and whose keys are
  89. # the same keys the table handles.
  90. #
  91. # Table entries whose keys are not present in the values_hash
  92. # will not be processed. More than one entry may process the same key.
  93. #
  94. # The functions in the replace tables, most of which are coded in
  95. # this file, receive the mapped files of the first argument, and then
  96. # a set of values. The last argument is the value of the $values_hash
  97. # for the corresponding key of the entry.
  98. sub set_from_table
  99. {
  100.   my ($fn, $table, $values_hash, $old_hash) = @_;
  101.   my ($key, $proc, @param);
  102.   my ($i, @cp, @files, $res);
  103.  
  104.   $$fn{"OLD_HASH"} = $old_hash;
  105.   
  106.   foreach $i (@$table)
  107.   {
  108.     @cp = @$i;
  109.     $key = shift (@cp);
  110.  
  111.     $proc = shift (@cp);
  112.     @files = &Utils::Parse::replace_files (shift (@cp), $fn);
  113.     unshift @cp, @files if (scalar @files) > 0;
  114.  
  115.     if ((exists $$values_hash{$key}) or ($key eq "_always_"))
  116.     {
  117.       $res = &run_entry ($values_hash, $key, $proc, \@cp, $$values_hash{$key});
  118.     }
  119.     elsif ((!exists $$values_hash{$key}) && (exists $$old_hash{$key}))
  120.     {
  121.       # we need to remove all the instances of the known variables that doesn't exist in the XML
  122.       $res = &run_entry ($values_hash, $key, $proc, \@cp, undef);
  123.     }
  124.   }
  125.  
  126.   return $res;
  127. }
  128.  
  129. # Wacky function that tries to create a field separator from a regular expression.
  130. # Doesn't work with all possible regular expressions: just with the ones we are working with.
  131. sub regexp_to_separator
  132. {
  133.   $_ = $_[0];
  134.  
  135.   s/\[([^^])([^\]])[^\]]*\]/$1/g;
  136.   s/\+//g;
  137.   s/\$//g;
  138.   s/[^\*]\*//g;
  139.  
  140.   return $_;
  141. }
  142.  
  143. sub set_value
  144. {
  145.   my ($key, $val, $re) = @_;
  146.   
  147.   return $key . ®exp_to_separator ($re) . $val;
  148. }
  149.  
  150. # Edit a $file, wich is assumed to have a column-based format, with $re matching field separators
  151. # and one record per line. Search for lines with the corresponding $key.
  152. # The last arguments can be any number of standard strings.
  153. sub split
  154. {
  155.   my ($file, $key, $re, @value) = @_;
  156.   my ($fd, @line, @res);
  157.   my ($buff, $i);
  158.   my ($pre_space, $post_comment);
  159.   my ($line_key, $val, $ret);
  160.  
  161.   &Utils::Report::enter ();
  162.   &Utils::Report::do_report ("replace_split", $key, $file);
  163.  
  164.   $buff = &Utils::File::load_buffer ($file);
  165.   
  166.   foreach $i (@$buff)
  167.   {
  168.     $pre_space = $post_comment = "";
  169.  
  170.     chomp $i;
  171.     $pre_space    = $1 if $i =~ s/^([ \t]+)//;
  172.     $post_comment = $1 if $i =~ s/([ \t]*\#.*)//;
  173.     
  174.     if ($i ne "")
  175.     {
  176.       @line = split ($re, $i, 2);
  177.       $line_key = shift (@line);
  178.  
  179.       # found the key?
  180.       if ($line_key eq $key)
  181.       {
  182.         shift (@value) while ($value[0] eq "" && (scalar @value) > 0);
  183.  
  184.         if ((scalar @value) == 0)
  185.         {
  186.           $i = "";
  187.           next;
  188.         }
  189.  
  190.         $val = shift (@value);
  191.  
  192.         chomp $val;
  193.         $i = &set_value ($key, $val, $re);
  194.       }
  195.     }
  196.  
  197.     $i = $pre_space . $i . $post_comment . "\n";
  198.   }
  199.  
  200.   foreach $i (@value)
  201.   {
  202.     push (@$buff, &set_value ($key, $i, $re) . "\n") if ($i ne "");
  203.   }
  204.  
  205.   &Utils::File::clean_buffer ($buff);
  206.   $ret = &Utils::File::save_buffer ($buff, $file);
  207.   &Utils::Report::leave ();
  208.   return $ret;
  209. }
  210.  
  211. # Replace all key/values in file with those in @$value,
  212. # deleting exceeding ones and appending those required.
  213. sub join_all
  214. {
  215.   my ($file, $key, $re, $value) = @_;
  216.  
  217.   return &split ($file, $key, $re, @$value);
  218. }
  219.  
  220. # Find first $key value and replace with $value. Append if not found.
  221. sub join_first_str
  222. {
  223.   my ($file, $key, $re, $value) = @_;
  224.  
  225.   return &split ($file, $key, $re, $value);
  226. }
  227.  
  228. # Treat value as a bool value, using val_off and val_on as corresponding
  229. # boolean representations.
  230. sub join_first_bool
  231. {
  232.   my ($file, $key, $re, $val_on, $val_off, $value) = @_;
  233.  
  234.   # Fixme: on and off should be a parameter.
  235.   $value = ($value == 1)? $val_on: $val_off;
  236.   
  237.   return &split ($file, $key, $re, $value);
  238. }
  239.  
  240. # Find first key in file, and set array join as value.
  241. sub join_first_array
  242. {
  243.   my ($file, $key, $re1, $re2, $value) = @_;
  244.  
  245.   return &split ($file, $key, $re1, join (®exp_to_separator ($re2), @$value));
  246. }
  247.  
  248. # Escape $value in /bin/sh way, find/append key and set escaped value.
  249. sub set_sh
  250. {
  251.   my ($file, $key, $value) = @_;
  252.   my $ret;
  253.  
  254.   $value = &Utils::Parse::escape ($value);
  255.  
  256.   &Utils::Report::enter ();
  257.   &Utils::Report::do_report ("replace_sh", $key, $file);
  258.  
  259.   # This will expunge the whole var if the value is empty.
  260.   if ($value eq "")
  261.   {
  262.     $ret = &split ($file, $key, "[ \t]*=[ \t]*");
  263.   }
  264.   else
  265.   {
  266.     $ret = &split ($file, $key, "[ \t]*=[ \t]*", $value);
  267.   }
  268.   
  269.   &Utils::Report::leave ();
  270.   return $ret;
  271. }
  272.  
  273. # Escape $value in /bin/sh way, find/append key and set escaped value, make sure line har 
  274. sub set_sh_export
  275. {
  276.   my ($file, $key, $value) = @_;
  277.   my $ret;
  278.  
  279.   $value = &Utils::Parse::escape ($value);
  280.  
  281.   # This will expunge the whole var if the value is empty.
  282.  
  283.   # FIXME: Just adding "export " works for the case I need, though it doesn't
  284.   # handle arbitraty whitespace. Something should be written to replace split()
  285.   # here.
  286.  
  287.   if ($value eq "")
  288.   {
  289.     $ret = &split ($file, "export " . $key, "[ \t]*=[ \t]*");
  290.   }
  291.   else
  292.   {
  293.     $ret = &split ($file, "export " . $key, "[ \t]*=[ \t]*", $value);
  294.   }
  295.   
  296.   return $ret;
  297. }
  298.  
  299. # Treat value as a yes/no bool, replace in shell style.
  300. # val_true and val_false have default yes/no values.
  301. # use &set_sh_bool (file, key, value) if defaults are desired.
  302. sub set_sh_bool
  303. {
  304.   my ($file, $key, $val_true, $val_false, $value) = @_;
  305.  
  306.   # default value magic.
  307.   if ($val_false eq undef)
  308.   {
  309.       $value = $val_true;
  310.       $val_true = undef;
  311.   }
  312.  
  313.   $val_true  = "yes" unless $val_true;
  314.   $val_false = "no"  unless $val_false;
  315.  
  316.   $value = ($value == 1)? $val_true: $val_false;
  317.   
  318.   return &set_sh ($file, $key, $value);
  319. }
  320.  
  321. # Treat value as a yes/no bool, replace in export... shell style.
  322. sub set_sh_export_bool
  323. {
  324.   my ($file, $key, $val_true, $val_false, $value) = @_;
  325.  
  326.   # default value magic.
  327.   if ($val_false eq undef)
  328.   {
  329.       $value = $val_true;
  330.       $val_true = undef;
  331.   }
  332.  
  333.   $val_true  = "yes" unless $val_true;
  334.   $val_false = "no"  unless $val_false;
  335.  
  336.   $value = ($value == 1)? $val_true: $val_false;
  337.   
  338.   return &set_sh_export ($file, $key, $value);
  339. }
  340.  
  341. # Get a fully qualified hostname from a $key shell var in $file
  342. # and set the hostname part. e.g.: suse70's /etc/rc.config's FQHOSTNAME.
  343. sub set_hostname
  344. {
  345.   my ($file, $key, $value) = @_;
  346.   my ($domain);
  347.  
  348.   $domain = &Utils::Parse::get_sh_domain ($file, $key);
  349.   return &set_sh ($file, $key, "$value.$domain");
  350. }
  351.  
  352. # Get a fully qualified hostname from a $key shell var in $file
  353. # and set the domain part. e.g.: suse70's /etc/rc.config's FQHOSTNAME.
  354. sub set_domain
  355. {
  356.   my ($file, $key, $value) = @_;
  357.   my ($hostname);
  358.  
  359.   $hostname = &Utils::Parse::get_sh_hostname ($file, $key);
  360.   return &set_sh ($file, $key, "$hostname.$value");
  361. }
  362.  
  363. # Join the array pointed by $value with the corresponding $re separator
  364. # and assign that to the $key shell variable in $file.
  365. sub set_sh_join
  366. {
  367.   my ($file, $key, $re, $value) = @_;
  368.  
  369.   return &set_sh ($file, $key,
  370.                           join (®exp_to_separator ($re), @$value));
  371. }
  372.  
  373. # replace a regexp with $value
  374. sub set_sh_re
  375. {
  376.   my ($file, $key, $re, $value) = @_;
  377.   my ($val);
  378.  
  379.   $val = &Utils::Parse::get_sh ($file, $key);
  380.  
  381.   if ($val =~ /$re/)
  382.   {
  383.     $val =~ s/$re/$value/;
  384.   }
  385.   else
  386.   {
  387.     $val .= $value;
  388.   }
  389.  
  390.   $val = '"' . $val . '"' if ($val !~ /^\".*\"$/);
  391.  
  392.   return &split ($file, $key, "[ \t]*=[ \t]*", $val)
  393. }
  394.  
  395. # Quick trick to set a keyword $key in $file. (think /etc/lilo.conf keywords).
  396. sub set_kw
  397. {
  398.   my ($file, $key, $value) = @_;
  399.   my $ret;
  400.  
  401.   &Utils::Report::enter ();
  402.   &Utils::Report::do_report ("replace_kw", $key, $file);
  403.   $ret = &split ($file, $key, "\$", ($value)? "\n" : "");
  404.   &Utils::Report::leave ();
  405.   return $ret;
  406. }
  407.  
  408. # The kind of $file whose $value is its first line contents.
  409. # (/etc/hostname)
  410. sub set_first_line
  411. {
  412.   my ($file, $value) = @_;
  413.   my $fd;
  414.  
  415.   &Utils::Report::enter ();
  416.   &Utils::Report::do_report ("replace_line_first", $file);
  417.   $fd = &Utils::File::open_write_from_names ($file);
  418.   &Utils::Report::leave ();
  419.   return -1 if !$fd;
  420.  
  421.   print $fd "$value\n";
  422.   &Utils::File::close_file ($fd);
  423.   
  424.   return 0;
  425. }
  426.  
  427. # For every key in %$value, replace/append the corresponding key/value pair.
  428. # The separator for $re1 
  429. sub join_hash
  430. {
  431.   my ($file, $re1, $re2, $value) = @_;
  432.   my ($i, $res, $tmp, $val);
  433.   my ($oldhash, %merge);
  434.  
  435.   $oldhash = &Utils::Parse::split_hash ($file, $re1, $re2);
  436.   foreach $i (keys (%$value), keys (%$oldhash))
  437.   {
  438.     $merge{$i} = 1;
  439.   }
  440.  
  441.   $res = 0;
  442.   
  443.   foreach $i (keys (%merge))
  444.   {
  445.     if (exists $$value{$i})
  446.     {
  447.       $val = join (®exp_to_separator ($re2), @{$$value{$i}});
  448.       $tmp = &split ($file, $i, $re1, $val);
  449.     }
  450.     else
  451.     {
  452.       # This deletes the entry.
  453.       $tmp = &split ($file, $i, $re1);
  454.     }
  455.     $res = $tmp if !$res;
  456.   }
  457.  
  458.   return $res;
  459. }
  460.  
  461. # Find $re matching send string and replace parenthesyzed
  462. # part of $re with $value. FIXME: apply meeks' more general impl.
  463. sub set_chat
  464. {
  465.   my ($file, $re, $value) = @_;
  466.   my ($buff, $i, $bak, $found, $substr, $ret);
  467.  
  468.   &Utils::Report::enter ();
  469.   &Utils::Report::do_report ("replace_chat", $file);
  470.   $buff = &Utils::File::load_buffer ($file);
  471.  
  472.   SCAN: foreach $i (@$buff)
  473.   {
  474.     $bak = "";
  475.     $found = "";
  476.     my ($quoted);
  477.     chomp $i;
  478.  
  479.     while ($i ne "")
  480.     {
  481.      # If it uses quotes. FIXME: Assuming they surround the whole string.
  482.      if ($i =~ /^\'/)
  483.      {
  484.        $i =~ s/\'([^\']*)\' ?//;
  485.        $found = $1;
  486.        $quoted = 1;
  487.      }
  488.      else
  489.      {
  490.        $i =~ s/([^ \t]*) ?//;
  491.        $found = $1;
  492.        $quoted = 0;
  493.      }
  494.      
  495.      # If it looks like what we're looking for,
  496.      # substitute what is in parens with value.
  497.      if ($found =~ /$re/i)
  498.      {
  499.        $substr = $1;
  500.        $found =~ s/$substr/$value/i;
  501.  
  502.        if ($quoted == 1)
  503.        {
  504.          $i = $bak . "\'$found\' " . $i . "\n";
  505.        }
  506.        else
  507.        {
  508.          $i = $bak . "$found " . $i . "\n";
  509.        }
  510.  
  511.        last SCAN;
  512.      }
  513.  
  514.      if ($quoted == 1)
  515.      {
  516.        $bak .= "\'$found\'";
  517.      }
  518.      else
  519.      {
  520.        $bak .= "$found";
  521.      }
  522.  
  523.      $bak .= " " if $bak ne "";
  524.     }
  525.     
  526.     $i = $bak . "\n";
  527.   }
  528.  
  529.   $ret = &Utils::File::save_buffer ($buff, $file);
  530.   &Utils::Report::leave ();
  531.   return $ret;
  532. }
  533.  
  534. # Find/append $section in ini $file and replace/append
  535. # $var = $value pair. FIXME: should reimplement with
  536. # interfaces style. This is too large.
  537. sub set_ini
  538. {
  539.   my ($file, $section, $var, $value) = @_;
  540.   my ($buff, $i, $found_flag, $ret);
  541.   my ($pre_space, $post_comment, $sec_save);
  542.  
  543.   &Utils::Report::enter ();
  544.   &Utils::Report::do_report ("replace_ini", $var, $section, $file);
  545.  
  546.   $buff = &Utils::File::load_buffer ($file);
  547.  
  548.   &Utils::File::join_buffer_lines ($buff);
  549.   $found_flag = 0;
  550.   
  551.   foreach $i (@$buff)
  552.   {
  553.     $pre_space = $post_comment = "";
  554.     
  555.     chomp $i;
  556.     $pre_space = $1 if $i =~ s/^([ \t]+)//;
  557.     $post_comment = $1 if $i =~ s/([ \t]*[\#;].*)//;
  558.     
  559.     if ($i ne "")
  560.     {
  561.       if ($i =~ /\[$section\]/i)
  562.       {
  563.         $i =~ s/(\[$section\][ \t]*)//i;
  564.         $sec_save = $1;
  565.         $found_flag = 1;
  566.       }
  567.  
  568.       if ($found_flag)
  569.       {
  570.         if ($i =~ /\[[^\]]+\]/)
  571.         {
  572.           $i = "$var = $value\n$i" if ($value ne "");
  573.           $found_flag = 2;
  574.         }
  575.         
  576.         if ($i =~ /^$var[ \t]*=/i)
  577.         {
  578.           if ($value ne "")
  579.           {
  580.             $i =~ s/^($var[ \t]*=[ \t]*).*/$1$value/i;
  581.           }
  582.           else
  583.           {
  584.             $i = "";
  585.           }
  586.           $found_flag = 2;
  587.         }
  588.       }
  589.     }
  590.     
  591.     if ($found_flag && $sec_save ne "")
  592.     {
  593.       $i = $sec_save . $i;
  594.       $sec_save = "";
  595.     }
  596.     
  597.     $i = $pre_space . $i . $post_comment . "\n";
  598.     last if $found_flag == 2;
  599.   }
  600.  
  601.   push @$buff, "\n[$section]\n" if (!$found_flag);
  602.   push @$buff, "$var = $value\n" if ($found_flag < 2 && $value ne "");
  603.  
  604.   &Utils::File::clean_buffer ($buff);
  605.   $ret = &Utils::File::save_buffer ($buff, $file);
  606.   &Utils::Report::leave ();
  607.   return $ret;
  608. }
  609.  
  610. # Well, removes a $section from an ini type $file.
  611. sub remove_ini_section
  612. {
  613.   my ($file, $section) = @_;
  614.   my ($buff, $i, $found_flag, $ret);
  615.   my ($pre_space, $post_comment, $sec_save);
  616.  
  617.   &Utils::Report::enter ();
  618.   &Utils::Report::do_report ("replace_del_ini_sect", $section, $file);
  619.  
  620.   $buff = &Utils::File::load_buffer ($file);
  621.  
  622.   &Utils::File::join_buffer_lines ($buff);
  623.   $found_flag = 0;
  624.  
  625.   foreach $i (@$buff)
  626.   {
  627.     $pre_space = $post_comment = "";
  628.  
  629.     chomp $i;
  630.     $pre_space = $1 if $i =~ s/^([ \t]+)//;
  631.     $post_comment = $1 if $i =~ s/([ \t]*[\#;].*)//;
  632.     
  633.     if ($i ne "")
  634.     {
  635.       if ($i =~ /\[$section\]/i)
  636.       {
  637.         $i =~ s/(\[$section\][ \t]*)//i;
  638.         $found_flag = 1;
  639.       }
  640.       elsif ($found_flag && $i =~ /\[.+\]/i)
  641.       {
  642.         $i = $pre_space . $i . $post_comment . "\n";
  643.         last;
  644.       }
  645.     }
  646.  
  647.     if ($found_flag)
  648.     {
  649.       if ($post_comment =~ /^[ \t]*$/)
  650.       {
  651.         $i = "";
  652.       }
  653.       else
  654.       {
  655.         $i = $post_comment . "\n";
  656.       }
  657.     }
  658.     else
  659.     {
  660.       $i = $pre_space . $i . $post_comment . "\n";
  661.     }
  662.   }
  663.  
  664.   &Utils::File::clean_buffer ($buff);
  665.   $ret = &Utils::File::save_buffer ($buff, $file);
  666.   &Utils::Report::leave ();
  667.   return $ret;
  668. }
  669.  
  670. # Removes a $var in $section of a ini type $file.
  671. sub remove_ini_var
  672. {
  673.   my ($file, $section, $var) = @_;
  674.   &set_ini ($file, $section, $var, "");
  675. }
  676.  
  677. # Replace using boolean $value with a yes/no representation,
  678. # ini style.
  679. sub set_ini_bool
  680. {
  681.   my ($file, $section, $var, $value) = @_;
  682.  
  683.   $value = ($value == 0)? "no": "yes";
  684.  
  685.   return &set_ini ($file, $section, $var, $value);
  686. }
  687.  
  688.  
  689. # Debian /etc/network/interfaces in-line replacing methods.
  690.  
  691. # From loaded buffer, starting at $line_no, find next debian
  692. # interfaces format stanza. Return array ref with all stanza args.
  693. # -1 if not found.
  694. # NOTE: $line_no is a scalar ref. and gives the position of next stanza.
  695. sub interfaces_get_next_stanza
  696. {
  697.   my ($buff, $line_no, $stanza_type) = @_;
  698.   my ($i, $line);
  699.  
  700.   while ($$line_no < (scalar @$buff))
  701.   {
  702.     $_ = $$buff[$$line_no];
  703.     $_ = &Utils::Parse::interfaces_line_clean ($_);
  704.  
  705.     if (/^$stanza_type[ \t]+[^ \t]/)
  706.     {
  707.       s/^$stanza_type[ \t]+//;
  708.       return [ split ("[ \t]+", $_) ];
  709.     }
  710.     $$line_no ++;
  711.   }
  712.  
  713.   return -1;
  714. }
  715.  
  716. sub interfaces_line_is_stanza
  717. {
  718.   my ($line) = @_;
  719.  
  720.   return 1 if $line =~ /^(iface|auto|mapping)[ \t]+[^ \t]/;
  721.   return 0;
  722. }
  723.  
  724. # Scan for next option. An option is something that is
  725. # not a stanza. Return key/value tuple ref, -1 if not found.
  726. # $$line_no will contain position.
  727. sub interfaces_get_next_option
  728. {
  729.   my ($buff, $line_no) = @_;
  730.   my ($i, $line, $empty_lines);
  731.  
  732.   $empty_lines = 0;
  733.   
  734.   while ($$line_no < (scalar @$buff))
  735.   {
  736.     $_ = $$buff[$$line_no];
  737.     $_ = &Utils::Parse::interfaces_line_clean ($_);
  738.  
  739.     if (!/^$/)
  740.     {
  741.       return [ split ("[ \t]+", $_, 2) ] if (! &interfaces_line_is_stanza ($_));
  742.       $$line_no -= $empty_lines;
  743.       return -1;
  744.     }
  745.     else
  746.     {
  747.       $empty_lines ++;
  748.     }
  749.     
  750.     $$line_no ++;
  751.   }
  752.  
  753.   $$line_no -= $empty_lines;
  754.   return -1;
  755. }
  756.  
  757. # Search buffer for option with key $key, starting
  758. # at $$line_no position. Return 1/0 found result.
  759. # $$line_no will show position.
  760. sub interfaces_option_locate
  761. {
  762.   my ($buff, $line_no, $key) = @_;
  763.   my $option;
  764.  
  765.   while (($option = &interfaces_get_next_option ($buff, $line_no)) != -1)
  766.   {
  767.     return 1 if ($$option[0] eq $key);
  768.     $$line_no ++;
  769.   }
  770.   
  771.   return 0;
  772. }
  773.  
  774. # Locate stanza line for $iface in $buff, starting at $$line_no.
  775. sub interfaces_next_stanza_locate
  776. {
  777.   my ($buff, $line_no) = @_;
  778.  
  779.   return &interfaces_get_next_stanza ($buff, \$$line_no, "(iface|auto|mapping)");
  780. }
  781.  
  782. sub interfaces_iface_stanza_locate
  783. {
  784.   my ($buff, $line_no, $iface) = @_;
  785.  
  786.   return &interfaces_generic_stanza_locate ($buff, \$$line_no, $iface, "iface");
  787. }
  788.  
  789. sub interfaces_auto_stanza_locate
  790. {
  791.   my ($buff, $line_no, $iface) = @_;
  792.  
  793.   return &interfaces_generic_stanza_locate ($buff, \$$line_no, $iface, "auto");
  794. }
  795.  
  796. sub interfaces_generic_stanza_locate
  797. {
  798.   my ($buff, $line_no, $iface, $stanza_name) = @_;
  799.   my $stanza;
  800.  
  801.   while (($stanza = &interfaces_get_next_stanza ($buff, \$$line_no, $stanza_name)) != -1)
  802.   {
  803.     return 1 if ($$stanza[0] eq $iface);
  804.     $$line_no++;
  805.   }
  806.  
  807.   return 0;
  808. }
  809.  
  810. # Create a Debian Woody stanza, type auto, with the requested
  811. # @ifaces as values.
  812. sub interfaces_auto_stanza_create
  813. {
  814.   my ($buff, @ifaces) = @_;
  815.   my ($count);
  816.   
  817.   push @$buff, "\n" if ($$buff[$count] ne "");
  818.   push @$buff, "auto " . join (" ", @ifaces) . "\n";
  819. }
  820.  
  821. # Append a stanza for $iface to buffer.
  822. sub interfaces_iface_stanza_create
  823. {
  824.   my ($buff, $iface) = @_;
  825.   my ($count);
  826.  
  827.   $count = $#$buff;
  828.   push @$buff, "\n" if ($$buff[$count] ne "");
  829.   push @$buff, "iface $iface inet static\n";
  830. }
  831.  
  832. # Delete $iface stanza and all its option lines.
  833. sub interfaces_iface_stanza_delete
  834. {
  835.   my ($file, $iface) = @_;
  836.   my ($buff, $line_no, $line_end, $stanza);
  837.  
  838.   $buff = &Utils::File::load_buffer ($file);
  839.   &Utils::File::join_buffer_lines ($buff);
  840.   $line_no = 0;
  841.  
  842.   return -1 if (!&interfaces_iface_stanza_locate ($buff, \$line_no, $iface));
  843.   $line_end = $line_no + 1;
  844.   &interfaces_next_stanza_locate ($buff, \$line_end);
  845.  
  846.   while ($line_no < $line_end)
  847.   {
  848.     delete $$buff[$line_no];
  849.     $line_no++;
  850.   }
  851.   
  852.   $line_no = 0;
  853.   if (&interfaces_auto_stanza_locate ($buff, \$line_no, $iface))
  854.   {
  855.     $line_end = $line_no + 1;
  856.     &interfaces_next_stanza_locate ($buff, \$line_end);
  857.  
  858.     while ($line_no < $line_end)
  859.     {
  860.       delete $$buff[$line_no];
  861.       $line_no++;
  862.     }
  863.   }
  864.   
  865.   &Utils::File::clean_buffer ($buff);
  866.   return &Utils::File::save_buffer ($buff, $file);
  867. }
  868.  
  869. # Find $iface stanza line and replace $pos value (ie the method).
  870. sub set_interfaces_stanza_value
  871. {
  872.   my ($file, $iface, $pos, $value) = @_;
  873.   my ($buff, $line_no, $stanza);
  874.   my ($pre_space, $line, $line_arr);
  875.  
  876.   $buff = &Utils::File::load_buffer ($file);
  877.   &Utils::File::join_buffer_lines ($buff);
  878.   $line_no = 0;
  879.  
  880.   if (!&interfaces_iface_stanza_locate ($buff, \$line_no, $iface))
  881.   {
  882.     $line_no = 0;
  883.     &interfaces_iface_stanza_create ($buff, $iface);
  884.     &interfaces_iface_stanza_locate ($buff, \$line_no, $iface);
  885.   }
  886.  
  887.   $line = $$buff[$line_no];
  888.   chomp $line;
  889.   $pre_space = $1 if $line =~ s/^([ \t]+)//;
  890.   $line =~ s/^iface[ \t]+//;
  891.   @line_arr = split ("[ \t]+", $line);
  892.   $line_arr[$pos] = $value;
  893.   $$buff[$line_no] = $pre_space . "iface " . join (' ', @line_arr) . "\n";
  894.  
  895.   &Utils::File::clean_buffer ($buff);
  896.   return &Utils::File::save_buffer ($buff, $file);
  897. }
  898.  
  899. # Find/append $key option in $iface stanza and set $value.
  900. sub set_interfaces_option_str
  901. {
  902.   my ($file, $iface, $key, $value) = @_;
  903.   my ($buff, $line_no, $stanza, $ret);
  904.   my ($pre_space, $line, $line_arr);
  905.  
  906.   &Utils::Report::enter ();
  907.   &Utils::Report::do_report ("replace_ifaces_str", $key, $iface);
  908.   
  909.   $buff = &Utils::File::load_buffer ($file);
  910.   &Utils::File::join_buffer_lines ($buff);
  911.   $line_no = 0;
  912.  
  913.   if (!&interfaces_iface_stanza_locate ($buff, \$line_no, $iface))
  914.   {
  915.     $line_no = 0;
  916.     &interfaces_iface_stanza_create ($buff, $iface);
  917.     &interfaces_iface_stanza_locate ($buff, \$line_no, $iface);
  918.   }
  919.  
  920.   $line_no++;
  921.  
  922.   if (&interfaces_option_locate ($buff, \$line_no, $key))
  923.   {
  924.     if ($value eq "") # Delete option if value is empty.
  925.     {
  926.       $$buff[$line_no] = "";
  927.     }
  928.     else
  929.     {
  930.       chomp $$buff[$line_no];
  931.       $$buff[$line_no] =~ s/^([ \t]*$key[ \t]).*/$1/;
  932.     }
  933.   }
  934.   elsif ($value ne "")
  935.   {
  936.     $line_no --;
  937.     chomp $$buff[$line_no];
  938.     $$buff[$line_no] =~ s/^([ \t]*)(.*)/$1$2\n$1$key /;
  939.   }
  940.  
  941.   $$buff[$line_no] .= $value . "\n" if $value ne "";
  942.   
  943.   &Utils::File::clean_buffer ($buff);
  944.   $ret = &Utils::File::save_buffer ($buff, $file);
  945.   &Utils::Report::leave ();
  946.   return $ret;
  947. }
  948.  
  949. # $key option is keyword. $value says if it should exist or not.
  950. sub set_interfaces_option_kw
  951. {
  952.   my ($file, $iface, $key, $value) = @_;
  953.  
  954.   return &set_interfaces_option_str ($file, $iface, $key, $value? " ": "");
  955. }
  956.  
  957. # !$value says if keyword should exist or not (ie noauto).
  958. sub set_interfaces_option_kw_not
  959. {
  960.   my ($file, $iface, $key, $value) = @_;
  961.  
  962.   return &set_interfaces_option_kw ($file, $iface, $key, !$value);
  963. }
  964.  
  965.  
  966. # Implementing pump(8) pump.conf file format replacer.
  967. # May be useful for dhcpd too.
  968.  
  969. # Try to find the next option, returning an array ref
  970. # with the found key and the rest of the options in
  971. # two items, or -1 if not found.
  972. sub pump_get_next_option
  973. {
  974.   my ($buff, $line_no) = @_;
  975.  
  976.   while ($$line_no < (scalar @$buff))
  977.   {
  978.     $_ = $$buff[$$line_no];
  979.     $_ = &Utils::Parse::interfaces_line_clean ($_);
  980.     if ($_ ne "")
  981.     {
  982.       return [ split ("[ \t]+", $_, 2) ];
  983.     }
  984.     
  985.     $$line_no ++;
  986.   }
  987.  
  988.   return -1;
  989. }
  990.  
  991. # Iterate with get_next_option, starting at $line_no
  992. # until the option with $key is found, or eof.
  993. # Return 0/1 as found.
  994. sub pump_option_locate
  995. {
  996.   my ($buff, $line_no, $key) = @_;
  997.   my ($opt);
  998.   
  999.   while (($opt = &pump_get_next_option ($buff, $line_no)) != -1)
  1000.   {
  1001.     return 1 if $$opt[0] eq $key;
  1002.     return 0 if $$opt[0] eq "}";
  1003.  
  1004.     $$line_no ++;
  1005.   }
  1006.   
  1007.   return 0;
  1008. }
  1009.  
  1010. # Try to find a "device" option whose interface is $iface,
  1011. # starting at $$line_no. Return 0/1 as found.
  1012. sub pump_get_device
  1013. {
  1014.   my ($buff, $line_no, $iface) = @_;
  1015.   my ($opt);
  1016.  
  1017.   while (($opt = &pump_get_next_option ($buff, $line_no)) != -1)
  1018.   {
  1019.     if ($$opt[0] eq "device")
  1020.     {
  1021.       $$opt[1] =~ s/[ \t]*\{//;
  1022.       return 1 if $$opt[1] eq $iface;
  1023.     }
  1024.  
  1025.     $$line_no ++;
  1026.   }
  1027.  
  1028.   return 0;
  1029. }
  1030.  
  1031. # Add a device entry for $iface at the end of $buff.
  1032. sub pump_add_device
  1033. {
  1034.   my ($buff, $iface) = @_;
  1035.  
  1036.   push @$buff, "\n";
  1037.   push @$buff, "device $iface {\n";
  1038.   push @$buff, "\t\n";
  1039.   push @$buff, "}\n";
  1040. }
  1041.  
  1042. # Find a "device" section for $iface and
  1043. # replace/add/delete the $key option inside the section.
  1044. sub set_pump_iface_option_str
  1045. {
  1046.   my ($file, $iface, $key, $value) = @_;
  1047.   my ($line_no, $ret);
  1048.  
  1049.   $buff = &Utils::File::load_buffer ($file);
  1050.   $line_no = 0;
  1051.  
  1052.   if (!&pump_get_device ($buff, \$line_no, $iface))
  1053.   {
  1054.     $line_no = 0;
  1055.     &pump_add_device ($buff, $iface);
  1056.     &pump_get_device ($buff, \$line_no, $iface);
  1057.   }
  1058.  
  1059.   $line_no ++;
  1060.  
  1061.   if (&pump_option_locate ($buff, \$line_no, $key))
  1062.   {
  1063.     if ($value eq "")
  1064.     {
  1065.       $$buff[$line_no] = "";
  1066.     }
  1067.     else
  1068.     {
  1069.       chomp $$buff[$line_no];
  1070.       $$buff[$line_no] =~ s/^([ \t]*$key[ \t]).*/$1/;
  1071.     }
  1072.   }
  1073.   elsif ($value ne "")
  1074.   {
  1075.     $line_no --;
  1076.     chomp $$buff[$line_no];
  1077.     $$buff[$line_no] =~ s/^([ \t]*)(.*)/$1$2\n$1$key /;
  1078.   }
  1079.  
  1080.   if ($value ne "")
  1081.   {
  1082.     $value =~ s/^[ \t]+//;
  1083.     $value =~ s/[ \t]+$//;
  1084.     $$buff[$line_no] .= &Utils::Parse::escape ($value) . "\n";
  1085.   }
  1086.  
  1087.   &Utils::File::clean_buffer ($buff);
  1088.   $ret = &Utils::File::save_buffer ($buff, $file);
  1089.   &Utils::Report::leave ();
  1090.   return $ret;
  1091. }
  1092.  
  1093. # Same as function above, except $key is a keyword.
  1094. sub set_pump_iface_kw
  1095. {
  1096.   my ($file, $iface, $key, $value) = @_;
  1097.  
  1098.   return &set_pump_iface_option_str ($file, $iface, $key, $value? " ": "");
  1099. }
  1100.  
  1101. # Same, but use the negative of $value (i.e. nodns)
  1102. sub set_pump_iface_kw_not
  1103. {
  1104.   my ($file, $iface, $key, $value) = @_;
  1105.  
  1106.   return &set_pump_iface_kw ($file, $iface, $key, !$value);
  1107. }
  1108.  
  1109. sub set_xml_pcdata
  1110. {
  1111.   my ($file, $varpath, $data) = @_;
  1112.   my ($model, $branch, $fd, $compressed);
  1113.  
  1114.   ($model, $compressed) = &Utils::XML::model_scan ($file);
  1115.   $branch = &Utils::XML::model_ensure ($model, $varpath);
  1116.  
  1117.   &Utils::XML::model_set_pcdata ($branch, $data);
  1118.  
  1119.   return &Utils::XML::model_save ($model, $file, $compressed);
  1120. }
  1121.  
  1122. sub set_xml_attribute
  1123. {
  1124.   my ($file, $varpath, $attr, $value) = @_;
  1125.   my ($model, $branch, $fd, $compressed);
  1126.  
  1127.   ($model, $compressed) = &Utils::XML::model_scan ($file);
  1128.   $branch = &Utils::XML::model_ensure ($model, $varpath);
  1129.  
  1130.   &Utils::XML::model_set_attribute ($branch, $attr, $value);
  1131.  
  1132.   return &Utils::XML::model_save ($model, $file, $compressed);
  1133. }
  1134.  
  1135. sub set_xml_pcdata_with_type
  1136. {
  1137.   my ($file, $varpath, $type, $data) = @_;
  1138.   my ($model, $branch, $fd, $compressed);
  1139.  
  1140.   ($model, $compressed) = &Utils::XML::model_scan ($file);
  1141.   $branch = &Utils::XML::model_ensure ($model, $varpath);
  1142.  
  1143.   &Utils::XML::model_set_pcdata ($branch, $data);
  1144.   &Utils::XML::model_set_attribute ($branch, "TYPE", $type);
  1145.  
  1146.   return &Utils::XML::model_save ($model, $file, $compressed);
  1147. }
  1148.  
  1149. sub set_xml_attribute_with_type
  1150. {
  1151.   my ($file, $varpath, $attr, $type, $value) = @_;
  1152.   my ($model, $branch, $fd, $compressed);
  1153.  
  1154.   ($model, $compressed) = &Utils::XML::model_scan ($file);
  1155.   $branch = &Utils::XML::model_ensure ($model, $varpath);
  1156.  
  1157.   &Utils::XML::model_set_attribute ($branch, $attr, $value);
  1158.   &Utils::XML::model_set_attribute ($branch, "TYPE", $type);
  1159.  
  1160.   return &Utils::XML::model_save ($model, $file, $compressed);
  1161. }
  1162.  
  1163. sub set_fq_hostname
  1164. {
  1165.   my ($file, $hostname, $domain) = @_;
  1166.  
  1167.   if ($domain eq undef)
  1168.   {
  1169.     return &set_first_line ($file, "$hostname");
  1170.   }
  1171.   else
  1172.   {
  1173.     return &set_first_line ($file, "$hostname.$domain");
  1174.   }
  1175. }
  1176.  
  1177. sub set_rcinet1conf
  1178. {
  1179.   my ($file, $iface, $kw, $val) = @_;
  1180.   my ($line);
  1181.  
  1182.   $iface =~ s/eth//;
  1183.   $line = "$kw\[$iface\]";
  1184.  
  1185.   $val = "\"$val\"" if ($val ne undef);
  1186.  
  1187.   return &split ($file, $line, "[ \t]*=[ \t]*", $val);
  1188. }
  1189.  
  1190. sub set_rcinet1conf_global
  1191. {
  1192.   my ($file, $kw, $val) = @_;
  1193.  
  1194.   $val = "\"$val\"";
  1195.  
  1196.   return &split ($file, $kw, "[ \t]*=[ \t]*", $val)
  1197. }
  1198.  
  1199. sub set_wireless_opts
  1200. {
  1201.   my ($file, $iface, $proc, $kw, $value) = @_;
  1202.   my $ifaces = &$proc ();
  1203.   my $found = 0;
  1204.   my $search = 1;
  1205.   my $buff;
  1206.   
  1207.   foreach $i (@$ifaces)
  1208.   {
  1209.     $found = 1 if ($iface eq $i);
  1210.   }
  1211.  
  1212.   $buff = &Utils::File::load_buffer ($file);
  1213.  
  1214.   foreach $i (@$buff)
  1215.   {
  1216.     if (/^case/)
  1217.     {
  1218.       # we don't want to search inside the case
  1219.       $search = 0;
  1220.     }
  1221.     elsif (/^esac/)
  1222.     {
  1223.       # we want to continue searching
  1224.       $search = 1;
  1225.     }
  1226.     if ((/^[ \t]*$kw/) && ($search))
  1227.     {
  1228.       $_ = "$kw=\"$value\"";
  1229.       $found = 1;
  1230.     }
  1231.   }
  1232.  
  1233.   if (!$found)
  1234.   {
  1235.     push @$buff, "$kw=\"$value\"";
  1236.   }
  1237.  
  1238.   &Utils::File::clean_buffer ($buff);
  1239.   return &Utils::File::save_buffer ($buff, $file);
  1240. }
  1241.  
  1242. # Functions for replacing in FreeBSD's /etc/ppp/ppp.conf
  1243. sub set_pppconf_common
  1244. {
  1245.   my ($pppconf, $section, $key, $string) = @_;
  1246.   my ($buff, $line_no, $end_line_no, $i, $found);
  1247.  
  1248.   $buff = &Utils::File::load_buffer ($pppconf);
  1249.  
  1250.   $line_no = &Utils::Parse::pppconf_find_stanza ($buff, $section);
  1251.  
  1252.   if ($line_no ne -1)
  1253.   {
  1254.     # The stanza exists
  1255.     $line_no++;
  1256.  
  1257.     $end_line_no = &Utils::Parse::pppconf_find_next_stanza ($buff, $line_no);
  1258.     $end_line_no = scalar @$buff + 1 if ($end_line_no == -1);
  1259.     $end_line_no--;
  1260.  
  1261.     for ($i = $line_no; $i <= $end_line_no; $i++)
  1262.     {
  1263.       if ($$buff[$i] =~ /[ \t]+$key/)
  1264.       {
  1265.         if ($string ne undef)
  1266.         {
  1267.           $$buff[$i] = " $string\n";
  1268.           $found = 1;
  1269.         }
  1270.         else
  1271.         {
  1272.           delete $$buff[$i];
  1273.         }
  1274.       }
  1275.     }
  1276.  
  1277.     if ($found != 1)
  1278.     {
  1279.       $$buff[$end_line_no] .= " $string\n" if ($string ne undef);
  1280.     }
  1281.   }
  1282.   else
  1283.   {
  1284.     if ($string ne undef)
  1285.     {
  1286.       push @$buff, "$section:\n";
  1287.       push @$buff, " $string\n";
  1288.     }
  1289.   }
  1290.  
  1291.   &Utils::File::clean_buffer ($buff);
  1292.   return &Utils::File::save_buffer ($buff, $pppconf);
  1293. }
  1294.  
  1295. sub set_pppconf
  1296. {
  1297.   my ($pppconf, $section, $key, $value) = @_;
  1298.   &set_pppconf_common ($pppconf, $section, $key, "set $key $value");
  1299. }
  1300.  
  1301. sub set_pppconf_bool
  1302. {
  1303.   my ($pppconf, $section, $key, $value) = @_;
  1304.   &set_pppconf_common ($pppconf, $section, $key,
  1305.                        ($value == 1)? "enable $key" : "disable $key");
  1306. }
  1307.  
  1308. sub set_ppp_options_re
  1309. {
  1310.   my ($file, $re, $value) = @_;
  1311.   my ($buff, $line, $replaced, $ret);
  1312.   my ($pre_space, $post_comment);
  1313.  
  1314.   &Utils::Report::enter ();
  1315.   &Utils::Report::do_report ("network_set_ppp_option", &Utils::Replace::regexp_to_separator ($re), $file);
  1316.  
  1317.   $buff = &Utils::File::load_buffer ($file);
  1318.  
  1319.   foreach $line (@$buff)
  1320.   {
  1321.     $pre_space = $post_comment = "";
  1322.     chomp $line;
  1323.     $pre_space = $1 if $line =~ s/^([ \t]+)//;
  1324.     $post_comment = $1 if $line =~ s/([ \t]*\#.*)//;
  1325.     
  1326.     if ($line =~ /$re/)
  1327.     {
  1328.       $line = "$value\n";
  1329.       $replaced = 1;
  1330.       last;
  1331.     }
  1332.  
  1333.     $line = $pre_space . $line . $post_comment . "\n";
  1334.   }
  1335.  
  1336.   push @$buff, "$value\n" if !$replaced;
  1337.   
  1338.   &Utils::File::clean_buffer ($buff);
  1339.   $ret = &Utils::File::save_buffer ($buff, $file);
  1340.   &Utils::Report::leave ();
  1341.   return $ret;
  1342. }
  1343.  
  1344. sub set_ppp_options_connect
  1345. {
  1346.   my ($file, $value) = @_;
  1347.   my $ret;
  1348.  
  1349.   &Utils::Report::enter ();
  1350.   &Utils::Report::do_report ("network_set_ppp_connect", $file);
  1351.   $ret = &set_ppp_options_re ($file, "^connect", "connect \"/usr/sbin/chat -v -f /etc/chatscripts/$value\"");
  1352.   &Utils::Report::leave ();
  1353.   return $ret;
  1354. }
  1355.  
  1356. sub set_confd_net_re
  1357. {
  1358.   my ($file, $key, $re, $value) = @_;
  1359.   my ($str, $contents, $i, $found, $done);
  1360.  
  1361.   $found = $done = 0;
  1362.   $contents = &Utils::File::load_buffer ($file);
  1363.  
  1364.   for ($i = 0; $i <= scalar (@$contents); $i++)
  1365.   {
  1366.     # search for key
  1367.     if ($$contents[$i] =~ /^$key[ \t]*=[ \t]*\(/)
  1368.     {
  1369.       $found = 1;
  1370.  
  1371.       do {
  1372.         if ($$contents[$i] =~ /\"([^\"]*)\"/)
  1373.         {
  1374.           $str = $1;
  1375.  
  1376.           if ($str =~ /$re/)
  1377.           {
  1378.             $str =~ s/$re/$value/;
  1379.           }
  1380.           else
  1381.           {
  1382.             $str .= $value;
  1383.           }
  1384.  
  1385.           $$contents[$i] =~ s/\"([^\"]*)\"/\"$str\"/;
  1386.           $done = 1;
  1387.         }
  1388.  
  1389.         $i++;
  1390.       } while (!$done);
  1391.     }
  1392.   }
  1393.  
  1394.   if (!$found)
  1395.   {
  1396.     push @$contents, "$key=(\"$value\")\n";
  1397.   }
  1398.  
  1399.   return &Utils::File::save_buffer ($contents, $file);
  1400. }
  1401.  
  1402. sub set_confd_net
  1403. {
  1404.   my ($file, $key, $value) = @_;
  1405.  
  1406.   return &set_confd_net_re ($file, $key, ".*", $value);
  1407. }
  1408.  
  1409.  
  1410. 1;
  1411.