#!/usr/bin/env perl #-*- Mode: perl; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- # replace.pl: Common in-line replacing stuff for the ximian-setup-tools backends. # # Copyright (C) 2000-2001 Ximian, Inc. # # Authors: Hans Petter Jansson # Arturo Espinosa # Michael Vogt - Debian 2.[2|3] support. # David Lee Ludwig - Debian 2.[2|3] support. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Library General Public License as published # by the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Library General Public License for more details. # # You should have received a copy of the GNU Library General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. $SCRIPTSDIR = "___scriptsdir___"; if ($SCRIPTSDIR =~ /^___scriptsdir__[_]/) { $SCRIPTSDIR = "."; $DOTIN = ".in"; } require "$SCRIPTSDIR/util.pl$DOTIN"; require "$SCRIPTSDIR/file.pl$DOTIN"; require "$SCRIPTSDIR/parse.pl$DOTIN"; # General rules: all replacing is in-line. Respect unsupported values, comments # and as many spacing as possible. # The concept of keyword (kw) here is a key, normaly in its own line, whose # boolean representation is its own existence. # A $re is a regular expression. In most functions here, regular expressions # are converted to simple separators, by using xst_replace_regexp_to_separator. # This makes it easier to convert a parse table into a replace table. # Every final replacing function to be used by a table must handle one key # at a time, but may replace several values from there. # # Return 0 for success, and -1 for failure. # # Most of these functions have a parsing counterpart. The convention is # that parse becomes replace and split becomes join: # xst_parse_split_first_str -> xst_replace_join_first_str # xst_replace_from_table takes a file mapping, a replace table, a hash # of values, probably made from XML parsing, and whose keys are # the same keys the table handles. # # Table entries whose keys are not present in the values_hash # will not be processed. More than one entry may process the same key. # # The functions in the replace tables, most of which are coded in # this file, receive the mapped files of the first argument, and then # a set of values. The last argument is the value of the $values_hash # for the corresponding key of the entry. sub xst_replace_from_table { my ($fn, $table, $values_hash, $old_hash) = @_; my ($key, $proc, @param); my ($i, @cp, @files, $res, $res2); $$fn{"OLD_HASH"} = $old_hash; foreach $i (@$table) { @cp = @$i; $key = shift (@cp); if (exists $$values_hash{$key}) { $proc = shift (@cp); @files = &xst_parse_replace_files (shift (@cp), $fn); unshift @cp, @files if (scalar @files) > 0; push (@cp, $$values_hash{$key}); &xst_debug_print_line ("xst_replace_from_table: $key"); next if (!&xst_parse_replace_hash_values (\@cp, $values_hash)); $res = -1 if &$proc (@cp); } } return $res; } # Wacky function that tries to create a field separator from a regular expression. # Doesn't work with all possible regular expressions: just with the ones we are working with. sub xst_replace_regexp_to_separator { $_ = $_[0]; s/\[([^^])([^\]])[^\]]*\]/$1/g; s/\+//g; s/\$//g; s/[^\*]\*//g; return $_; } sub is_array_ref { my $val; return 1 if (ref ($val) eq "ARRAY"); return 0 if (ref ($val) eq undef); &xst_debug_print_line ("is_array_ref: We shouldn't be here!"); return 0; } sub set_value { my ($key, $val, $re) = @_; return $key . &xst_replace_regexp_to_separator ($re) . $val; } # Edit a $file, wich is assumed to have a column-based format, with $re matching field separators # and one record per line. Search for lines with the corresponding $key. # The last arguments can be any number of standard strings. sub xst_replace_split { my ($file, $key, $re, @value) = @_; my ($fd, @line, @res); my ($buff, $i); my ($pre_space, $post_comment); my ($line_key, $val, $ret); &xst_report_enter (); &xst_report ("replace_split", $key, $file); $buff = &xst_file_buffer_load ($file); foreach $i (@$buff) { $pre_space = $post_comment = ""; chomp $i; $pre_space = $1 if $i =~ s/^([ \t]+)//; $post_comment = $1 if $i =~ s/([ \t]*\#.*)//; if ($i ne "") { @line = split ($re, $i, 2); $line_key = shift (@line); # found the key? if ($line_key eq $key) { shift (@value) while ($value[0] eq "" && (scalar @value) > 0); if ((scalar @value) == 0) { $i = ""; next; } $val = shift (@value); chomp $val; $i = &set_value ($key, $val, $re); } } $i = $pre_space . $i . $post_comment . "\n"; } foreach $i (@value) { push (@$buff, &set_value ($key, $i, $re) . "\n"); } &xst_file_buffer_clean ($buff); $ret = &xst_file_buffer_save ($buff, $file); &xst_report_leave (); return $ret; } # Replace all key/values in file with those in @$value, # deleting exceeding ones and appending those required. sub xst_replace_join_all { my ($file, $key, $re, $value) = @_; return &xst_replace_split ($file, $key, $re, @$value); } # Find first $key value and replace with $value. Append if not found. sub xst_replace_join_first_str { my ($file, $key, $re, $value) = @_; return &xst_replace_split ($file, $key, $re, $value); } # Treat value as a bool value, using val_off and val_on as corresponding # boolean representations. sub xst_replace_join_first_bool { my ($file, $key, $re, $val_on, $val_off, $value) = @_; # Fixme: on and off should be a parameter. $value = ($value == 1)? $val_on: $val_off; return &xst_replace_split ($file, $key, $re, $value); } # Find first key in file, and set array join as value. sub xst_replace_join_first_array { my ($file, $key, $re1, $re2, $value) = @_; return &xst_replace_split ($file, $key, $re1, join (&xst_replace_regexp_to_separator ($re2), @$value)); } # Escape $value in /bin/sh way, find/append key and set escaped value. sub xst_replace_sh { my ($file, $key, $value) = @_; my $ret; $value = &xst_parse_shell_escape ($value); &xst_report_enter (); &xst_report ("replace_sh", $key, $file); # This will expunge the whole var if the value is empty. if ($value eq "") { $ret = &xst_replace_split ($file, $key, "[ \t]*=[ \t]*"); } else { $ret = &xst_replace_split ($file, $key, "[ \t]*=[ \t]*", $value); } &xst_report_leave (); return $ret; } # Escape $value in /bin/sh way, find/append key and set escaped value, make sure line har sub xst_replace_sh_export { my ($file, $key, $value) = @_; my $ret; $value = &xst_parse_shell_escape ($value); # This will expunge the whole var if the value is empty. # FIXME: Just adding "export " works for the case I need, though it doesn't # handle arbitraty whitespace. Something should be written to replace xst_replace_split() # here. if ($value eq "") { $ret = &xst_replace_split ($file, "export " . $key, "[ \t]*=[ \t]*"); } else { $ret = &xst_replace_split ($file, "export " . $key, "[ \t]*=[ \t]*", $value); } return $ret; } # Treat value as a yes/no bool, replace in shell style. # val_true and val_false have default yes/no values. # use &xst_replace_sh_bool (file, key, value) if defaults are desired. sub xst_replace_sh_bool { my ($file, $key, $val_true, $val_false, $value) = @_; # default value magic. if ($val_false eq undef) { $value = $val_true; $val_true = undef; } $val_true = "yes" unless $val_true; $val_false = "no" unless $val_false; $value = ($value == 1)? $val_true: $val_false; return &xst_replace_sh ($file, $key, $value); } # Treat value as a yes/no bool, replace in export... shell style. sub xst_replace_sh_export_bool { my ($file, $key, $val_true, $val_false, $value) = @_; # default value magic. if ($val_false eq undef) { $value = $val_true; $val_true = undef; } $val_true = "yes" unless $val_true; $val_false = "no" unless $val_false; $value = ($value == 1)? $val_true: $val_false; return &xst_replace_sh_export ($file, $key, $value); } # Get a fully qualified hostname from a $key shell var in $file # and set the hostname part. e.g.: suse70's /etc/rc.config's FQHOSTNAME. sub xst_replace_sh_set_hostname { my ($file, $key, $value) = @_; my ($domain); $domain = &xst_parse_sh_get_domain ($file, $key); return &xst_replace_sh ($file, $key, "$value.$domain"); } # Get a fully qualified hostname from a $key shell var in $file # and set the domain part. e.g.: suse70's /etc/rc.config's FQHOSTNAME. sub xst_replace_sh_set_domain { my ($file, $key, $value) = @_; my ($hostname); $hostname = &xst_parse_sh_get_hostname ($file, $key); return &xst_replace_sh ($file, $key, "$hostname.$value"); } # Join the array pointed by $value with the corresponding $re separator # and assign that to the $key shell variable in $file. sub xst_replace_sh_join { my ($file, $key, $re, $value) = @_; return &xst_replace_sh ($file, $key, join (&xst_replace_regexp_to_separator ($re), @$value)); } # Quick trick to set a keyword $key in $file. (think /etc/lilo.conf keywords). sub xst_replace_kw { my ($file, $key, $value) = @_; my $ret; &xst_report_enter (); &xst_report ("replace_kw", $key, $file); $ret = &xst_replace_split ($file, $key, "\$", ($value)? "\n" : ""); &xst_report_leave (); return $ret; } # The kind of $file whose $value is its first line contents. # (/etc/hostname) sub xst_replace_line_first { my ($file, $value) = @_; my $fd; &xst_report_enter (); &xst_report ("replace_line_first", $file); $fd = &xst_file_open_write_from_names ($file); &xst_report_leave (); return -1 if !$fd; print $fd $value; &xst_file_close ($fd); return 0; } # For every key in %$value, replace/append the corresponding key/value pair. # The separator for $re1 sub xst_replace_join_hash { my ($file, $re1, $re2, $value) = @_; my ($i, $res, $tmp, $val); $res = 0; foreach $i (keys (%$value)) { $val = join (&xst_replace_regexp_to_separator ($re2), @{$$value{$i}}); $tmp = &xst_replace_split ($file, $i, $re1, $val); $res = $tmp if !$res; } return $res; } # Find $re matching send string and replace parenthesyzed # part of $re with $value. FIXME: apply meeks' more general impl. sub xst_replace_chat { my ($file, $re, $value) = @_; my ($buff, $i, $bak, $found, $substr, $ret); &xst_report_enter (); &xst_report ("replace_chat", $file); $buff = &xst_file_buffer_load ($file); SCAN: foreach $i (@$buff) { $bak = ""; $found = ""; chomp $i; while ($i ne "") { # If it uses quotes. FIXME: Assuming they surround the whole string. if ($i =~ /^\'/) { $i =~ s/\'([^\']*)\' ?//; $found = $1; } else { $i =~ s/([^ \t]*) ?//; $found = $1; } # If it looks like what we're looking for, # substitute what is in parens with value. if ($found =~ /$re/i) { $substr = $1; $found =~ s/$substr/$value/i; $i = $bak . "\'$found\' " . $i . "\n"; last SCAN; } $bak .= "\'$found\'"; $bak .= " " if $bak ne ""; } $i = $bak . "\n"; } $ret = &xst_file_buffer_save ($buff, $file); &xst_report_leave (); return $ret; } # Find/append $section in ini $file and replace/append # $var = $value pair. FIXME: should reimplement with # interfaces style. This is too large. sub xst_replace_ini { my ($file, $section, $var, $value) = @_; my ($buff, $i, $found_flag, $ret); my ($pre_space, $post_comment, $sec_save); &xst_report_enter (); &xst_report ("replace_ini", $var, $section, $file); $buff = &xst_file_buffer_load ($file); &xst_file_buffer_join_lines ($buff); $found_flag = 0; foreach $i (@$buff) { $pre_space = $post_comment = ""; chomp $i; $pre_space = $1 if $i =~ s/^([ \t]+)//; $post_comment = $1 if $i =~ s/([ \t]*[\#;].*)//; if ($i ne "") { if ($i =~ /\[$section\]/i) { $i =~ s/(\[$section\][ \t]*)//i; $sec_save = $1; $found_flag = 1; } if ($found_flag) { if ($i =~ /\[[^\]]+\]/) { $i = "$var = $value\n$i"; $found_flag = 2; } if ($i =~ /^$var[ \t]*=/i) { if ($value ne "") { $i =~ s/^($var[ \t]*=[ \t]*).*/$1$value/i; } else { $i = ""; } $found_flag = 2; } } } if ($found_flag && $sec_save ne "") { $i = $sec_save . $i; $sec_save = ""; } $i = $pre_space . $i . $post_comment . "\n"; last if $found_flag == 2; } push @$buff, "\n[$section]\n" if (!$found_flag); push @$buff, "$var = $value\n" if ($found_flag < 2 && $value ne ""); &xst_file_buffer_clean ($buff); $ret = &xst_file_buffer_save ($buff, $file); &xst_report_leave (); return $ret; } # Well, removes a $section from an ini type $file. sub xst_replace_remove_ini_section { my ($file, $section) = @_; my ($buff, $i, $found_flag, $ret); my ($pre_space, $post_comment, $sec_save); &xst_report_enter (); &xst_report ("replace_del_ini_sect", $section, $file); $buff = &xst_file_buffer_load ($file); &xst_file_buffer_join_lines ($buff); $found_flag = 0; foreach $i (@$buff) { $pre_space = $post_comment = ""; chomp $i; $pre_space = $1 if $i =~ s/^([ \t]+)//; $post_comment = $1 if $i =~ s/([ \t]*[\#;].*)//; if ($i ne "") { if ($i =~ /\[$section\]/i) { $i =~ s/(\[$section\][ \t]*)//i; $found_flag = 1; } elsif ($found_flag && $i =~ /\[.+\]/i) { $i = $pre_space . $i . $post_comment . "\n"; last; } } if ($found_flag) { if ($post_comment =~ /^[ \t]*$/) { $i = ""; } else { $i = $post_comment . "\n"; } } else { $i = $pre_space . $i . $post_comment . "\n"; } } &xst_file_buffer_clean ($buff); $ret = &xst_file_buffer_save ($buff, $file); &xst_report_leave (); return $ret; } # Removes a $var in $section of a ini type $file. sub xst_replace_remove_ini_var { my ($file, $section, $var) = @_; &xst_replace_ini ($file, $section, $var, ""); } # Replace using boolean $value with a yes/no representation, # ini style. sub xst_replace_ini_bool { my ($file, $section, $var, $value) = @_; $value = ($value == 1)? "yes": "no"; return &xst_replace_ini ($file, $section, $var, $value); } # *cap replacement methods. #sub xst_replace_cap #{ # my ($file, $section, $var, $value) = @_; # my ($buff, $i, $found_flag, $ret); # my ($pre_space, $post_comment, $sec_save); # # $buff = &xst_file_buffer_load ($file); ## &xst_file_buffer_join_lines ($buff); # $found_flag = 0; # # foreach $i (@$buff) # { # $pre_space = $post_comment = ""; # # chomp $i; # $pre_space = $1 if $i =~ s/^([ \t]+)//; # $post_comment = $1 if $i =~ s/^([ \t]*[\#].*)//; # # if ($i ne "") # { # if ($i =~ /^$section[|:]/i) # { # $i =~ s/^($section)//i; # $sec_save = $1; # $found_flag = 1; # } # # if ($found_flag) # { # if ($i =~ /^[a-z0-9]+[|:]/) # { # $i = "\t:$var=$value:\n$i"; # $found_flag = 2; # } # # if ($found_flag && $i =~ /^:$var[=:]/i) # { # if ($value ne "") # { # $i =~ s/^(:$var)[^:]*/$1=$value/i; # } # else # { # $i = ""; # } # $found_flag = 2; # } # } # } # # if ($found_flag && $sec_save ne "") # { # $i = $sec_save . $i; # $sec_save = ""; # } # # $i = $pre_space . $i . $post_comment . "\n"; # last if $found_flag == 2; # } # # push @$buff, "\n$section:\\\n" if (!$found_flag); # push @$buff, "\t:$var=$value:\n" if ($found_flag < 2 && $value ne ""); # # &xst_file_buffer_clean ($buff); # $ret = &xst_file_buffer_save ($buff, $file); # return $ret; #} sub xst_replace_remove_cap_section { my ($file, $section) = @_; my ($buff, $i, $found_flag, $ret); my ($pre_space, $post_comment, $sec_save); $buff = &xst_file_buffer_load ($file); $found_flag = 0; foreach $i (@$buff) { $pre_space = $post_comment = ""; chomp $i; $pre_space = $1 if $i =~ s/^([ \t]+)//; $post_comment = $1 if $i =~ s/^([ \t]*[\#].*)//; if ($i ne "") { if ($i =~ /^$section[|:]/i) { $i = ""; $found_flag = 1; } elsif ($found_flag && $i =~ /^[a-z0-9]+[|:]/i) { $i = $pre_space . $i . $post_comment . "\n"; last; } } if ($found_flag) { if ($post_comment =~ /^[ \t]*$/) { $i = ""; } else { $i = $post_comment . "\n"; } } else { $i = $pre_space . $i . $post_comment . "\n"; } } &xst_file_buffer_clean ($buff); $ret = &xst_file_buffer_save ($buff, $file); return $ret; } # Save a printcap buffer to file. This doesn't do any extra processing for now, # but it may do so in the future. sub xst_replace_printcap_buffer_save { my ($file, $buf) = @_; my $ret; &xst_file_buffer_clean ($buf); $ret = &xst_file_buffer_save ($buf, $file); return $ret; } sub xst_replace_printcap_print_stanza { my ($stanza) = @_; return $stanza . ":\n"; } sub xst_replace_printcap_print_option { my ($option, $type, $value) = @_; return "\t:" . $option . $type . $value . ":\n"; } sub xst_replace_printcap_add_stanza { my ($buf, $stanza) = @_; push @$buf, "\n"; push @$buf, "##PRINTTOOL3## LOCAL unknown NAxNA {} Unknown Default {}\n"; push @$buf, &xst_replace_printcap_print_stanza ($stanza); return ($#$buf - 1, $#$buf); } sub xst_replace_printcap_add_option_slot { my ($buf, $stanza_line_no) = @_; my (@buf_tail); @buf_tail = splice (@$buf, $stanza_line_no + 1); push @$buf, "\t:NEW_OPTION:\n"; push @$buf, @buf_tail; return $stanza_line_no + 1; } sub xst_replace_printcap_remove_stanza_from_buf { my ($buf, $printtool_line_no, $stanza_line_no) = @_; my ($next_printtool_line_no, $next_stanza_line_no); my ($splice_start, $splice_end); ($next_printtool_line_no, $next_stanza_line_no) = &xst_parse_printcap_get_next_stanza ($buf, $stanza_line_no + 1); if ($printtool_line_no != -1) { $splice_start = $printtool_line_no; } else { $splice_start = $stanza_line_no; } if ($next_printtool_line_no != -1) { $splice_end = $next_printtool_line_no; } else { $splice_end = $next_stanza_line_no; } if ($splice_end != -1) { splice (@$buf, $splice_start, $splice_end - $splice_start); } else { splice (@$buf, $splice_start); } } sub xst_replace_printcap_remove_option_slot { my ($buf, $option_line_no) = @_; splice (@$buf, $option_line_no, 1); } # High-level API. sub xst_replace_printcap_remove_printer { my ($file, $printer) = @_; my ($buf, $printtool_line_no, $stanza_line_no); $buf = &xst_parse_printcap_buffer_load ($file); ($printtool_line_no, $stanza_line_no) = &xst_parse_printcap_find_stanza ($buf, 0, $printer); &xst_replace_printcap_remove_stanza_from_buf ($buf, $printtool_line_no, $stanza_line_no); $ret = &xst_replace_printcap_buffer_save ($file, $buf); return $ret; } # High-level API. sub xst_replace_printcap { my ($file, $section, $var, $type, $value) = @_; my ($printtool_line_no, $stanza_line_no, $option_line_no); my ($buf, $ret); $buf = &xst_parse_printcap_buffer_load ($file); ($printtool_line_no, $stanza_line_no) = &xst_parse_printcap_find_stanza ($buf, 0, $section); if ($stanza_line_no == -1) { ($printtool_line_no, $stanza_line_no) = &xst_replace_printcap_add_stanza ($buf, $section); } $option_line_no = &xst_parse_printcap_find_option ($buf, $stanza_line_no + 1, $var); if ($option_line_no == -1) { $option_line_no = &xst_replace_printcap_add_option_slot ($buf, $stanza_line_no); } if ($type ne "") { $$buf [$option_line_no] = "\t:" . $var . $type . $value . ":\n"; } elsif ($value == 1) { $$buf [$option_line_no] = "\t:" . $var . ":\n"; } else { &xst_replace_printcap_remove_option_slot ($buf, $option_line_no); } $ret = &xst_replace_printcap_buffer_save ($file, $buf); return $ret; } # Debian /etc/network/interfaces in-line replacing methods. # From loaded buffer, starting at $line_no, find next debian # interfaces format stanza. Return array ref with all stanza args. # -1 if not found. # NOTE: $line_no is a scalar ref. and gives the position of next stanza. sub xst_replace_interfaces_get_next_stanza { my ($buff, $line_no, $stanza_type) = @_; my ($i, $line); while ($$line_no < (scalar @$buff)) { $_ = $$buff[$$line_no]; $_ = &xst_parse_interfaces_line_clean ($_); if (/^$stanza_type[ \t]+[^ \t]/) { s/^$stanza_type[ \t]+//; return [ split ("[ \t]+", $_) ]; } $$line_no ++; } return -1; } sub xst_replace_interfaces_line_is_stanza { my ($line) = @_; return 1 if $line =~ /^(iface|auto|mapping)[ \t]+[^ \t]/; return 0; } # Scan for next option. An option is something that is # not a stanza. Return key/value tuple ref, -1 if not found. # $$line_no will contain position. sub xst_replace_interfaces_get_next_option { my ($buff, $line_no) = @_; my ($i, $line, $empty_lines); $empty_lines = 0; while ($$line_no < (scalar @$buff)) { $_ = $$buff[$$line_no]; $_ = &xst_parse_interfaces_line_clean ($_); if (!/^$/) { return [ split ("[ \t]+", $_, 2) ] if (! &xst_replace_interfaces_line_is_stanza ($_)); $$line_no -= $empty_lines; return -1; } else { $empty_lines ++; } $$line_no ++; } $$line_no -= $empty_lines; return -1; } # Search buffer for option with key $key, starting # at $$line_no position. Return 1/0 found result. # $$line_no will show position. sub xst_replace_interfaces_option_locate { my ($buff, $line_no, $key) = @_; my $option; while (($option = &xst_replace_interfaces_get_next_option ($buff, $line_no)) != -1) { return 1 if ($$option[0] eq $key); $$line_no ++; } return 0; } # Locate stanza line for $iface in $buff, starting at $$line_no. sub xst_replace_interfaces_iface_stanza_locate { my ($buff, $line_no, $iface) = @_; my $stanza; while (($stanza = &xst_replace_interfaces_get_next_stanza ($buff, $line_no, "iface")) != -1) { return 1 if ($$stanza[0] eq $iface); $$line_no ++; } return 0; } # Create a Debian Woody stanza, type auto, with the requested # @ifaces as values. sub xst_replace_interfaces_auto_stanza_create { my ($buff, @ifaces) = @_; push @$buff, "\n"; push @$buff, "auto " . join (" ", @ifaces) . "\n"; } # Append a stanza for $iface to buffer. sub xst_replace_interfaces_iface_stanza_create { my ($buff, $iface) = @_; push @$buff, "\n"; push @$buff, "iface $iface inet static\n"; } # Delete $iface stanza and all its option lines. sub xst_replace_interfaces_iface_stanza_delete { my ($file, $iface) = @_; my ($buff, $line_no, $stanza); $buff = &xst_file_buffer_load ($file); &xst_file_buffer_join_lines ($buff); $line_no = 0; return -1 if (!&xst_replace_interfaces_iface_stanza_locate ($buff, \$line_no, $iface)); $$buff[$line_no] = ""; $line_no ++; while (&xst_replace_interfaces_get_next_option ($buff, \$line_no) != -1) { $$buff[$line_no] = ""; $line_no ++; } &xst_file_buffer_clean ($buff); return &xst_file_buffer_save ($buff, $file); } # Find $iface stanza line and replace $pos value (ie the method). sub xst_replace_interfaces_stanza_value { my ($file, $iface, $pos, $value) = @_; my ($buff, $line_no, $stanza); my ($pre_space, $line, $line_arr); $buff = &xst_file_buffer_load ($file); &xst_file_buffer_join_lines ($buff); $line_no = 0; if (!&xst_replace_interfaces_iface_stanza_locate ($buff, \$line_no, $iface)) { $line_no = 0; &xst_replace_interfaces_iface_stanza_create ($buff, $iface); &xst_replace_interfaces_iface_stanza_locate ($buff, \$line_no, $iface); } $line = $$buff[$line_no]; chomp $line; $pre_space = $1 if $line =~ s/^([ \t]+)//; $line =~ s/^iface[ \t]+//; @line_arr = split ("[ \t]+", $line); $line_arr[$pos] = $value; $$buff[$line_no] = $pre_space . "iface " . join (' ', @line_arr) . "\n"; &xst_file_buffer_clean ($buff); return &xst_file_buffer_save ($buff, $file); } # Find/append $key option in $iface stanza and set $value. sub xst_replace_interfaces_option_str { my ($file, $iface, $key, $value) = @_; my ($buff, $line_no, $stanza, $ret); my ($pre_space, $line, $line_arr); &xst_report_enter (); &xst_report ("replace_ifaces_str", $key, $iface); $buff = &xst_file_buffer_load ($file); &xst_file_buffer_join_lines ($buff); $line_no = 0; if (!&xst_replace_interfaces_iface_stanza_locate ($buff, \$line_no, $iface)) { $line_no = 0; &xst_replace_interfaces_iface_stanza_create ($buff, $iface); &xst_replace_interfaces_iface_stanza_locate ($buff, \$line_no, $iface); } $line_no++; if (&xst_replace_interfaces_option_locate ($buff, \$line_no, $key)) { if ($value eq "") # Delete option if value is empty. { $$buff[$line_no] = ""; } else { chomp $$buff[$line_no]; $$buff[$line_no] =~ s/^([ \t]*$key[ \t]).*/$1/; } } elsif ($value ne "") { $line_no --; chomp $$buff[$line_no]; $$buff[$line_no] =~ s/^([ \t]*)(.*)/$1$2\n$1$key /; } $$buff[$line_no] .= $value . "\n" if $value ne ""; &xst_file_buffer_clean ($buff); $ret = &xst_file_buffer_save ($buff, $file); &xst_report_leave (); return $ret; } # $key option is keyword. $value says if it should exist or not. sub xst_replace_interfaces_option_kw { my ($file, $iface, $key, $value) = @_; return &xst_replace_interfaces_option_str ($file, $iface, $key, $value? " ": ""); } # !$value says if keyword should exist or not (ie noauto). sub xst_replace_interfaces_option_kw_not { my ($file, $iface, $key, $value) = @_; return &xst_replace_interfaces_option_kw ($file, $iface, $key, !$value); } # Implementing pump(8) pump.conf file format replacer. # May be useful for dhcpd too. # Try to find the next option, returning an array ref # with the found key and the rest of the options in # two items, or -1 if not found. sub xst_replace_pump_get_next_option { my ($buff, $line_no) = @_; while ($$line_no < (scalar @$buff)) { $_ = $$buff[$$line_no]; $_ = &xst_parse_interfaces_line_clean ($_); if ($_ ne "") { return [ split ("[ \t]+", $_, 2) ]; } $$line_no ++; } return -1; } # Iterate with get_next_option, starting at $line_no # until the option with $key is found, or eof. # Return 0/1 as found. sub xst_replace_pump_option_locate { my ($buff, $line_no, $key) = @_; my ($opt); while (($opt = &xst_replace_pump_get_next_option ($buff, $line_no)) != -1) { return 1 if $$opt[0] eq $key; return 0 if $$opt[0] eq "}"; $$line_no ++; } return 0; } # Try to find a "device" option whose interface is $iface, # starting at $$line_no. Return 0/1 as found. sub xst_replace_pump_get_device { my ($buff, $line_no, $iface) = @_; my ($opt); while (($opt = &xst_replace_pump_get_next_option ($buff, $line_no)) != -1) { if ($$opt[0] eq "device") { $$opt[1] =~ s/[ \t]*\{//; return 1 if $$opt[1] eq $iface; } $$line_no ++; } return 0; } # Add a device entry for $iface at the end of $buff. sub xst_replace_pump_add_device { my ($buff, $iface) = @_; push @$buff, "\n"; push @$buff, "device $iface {\n"; push @$buff, "\t\n"; push @$buff, "}\n"; } # Find a "device" section for $iface and # replace/add/delete the $key option inside the section. sub xst_replace_pump_iface_option_str { my ($file, $iface, $key, $value) = @_; my ($line_no, $ret); $buff = &xst_file_buffer_load ($file); $line_no = 0; if (!&xst_replace_pump_get_device ($buff, \$line_no, $iface)) { $line_no = 0; &xst_replace_pump_add_device ($buff, $iface); &xst_replace_pump_get_device ($buff, \$line_no, $iface); } $line_no ++; if (&xst_replace_pump_option_locate ($buff, \$line_no, $key)) { if ($value eq "") { $$buff[$line_no] = ""; } else { chomp $$buff[$line_no]; $$buff[$line_no] =~ s/^([ \t]*$key[ \t]).*/$1/; } } elsif ($value ne "") { $line_no --; chomp $$buff[$line_no]; $$buff[$line_no] =~ s/^([ \t]*)(.*)/$1$2\n$1$key /; } if ($value ne "") { $value =~ s/^[ \t]+//; $value =~ s/[ \t]+$//; $$buff[$line_no] .= &xst_parse_shell_escape ($value) . "\n"; } &xst_file_buffer_clean ($buff); $ret = &xst_file_buffer_save ($buff, $file); &xst_report_leave (); return $ret; } # Same as function above, except $key is a keyword. sub xst_replace_pump_iface_kw { my ($file, $iface, $key, $value) = @_; return &xst_replace_pump_iface_option_str ($file, $iface, $key, $value? " ": ""); } # Same, but use the negative of $value (i.e. nodns) sub xst_replace_pump_iface_kw_not { my ($file, $iface, $key, $value) = @_; return &xst_replace_pump_iface_kw ($file, $iface, $key, !$value); } sub xst_replace_xml_pcdata { my ($file, $varpath, $data) = @_; my ($model, $branch, $fd, $compressed); ($model, $compressed) = &xst_xml_model_scan ($file); $branch = &xst_xml_model_ensure ($model, $varpath); &xst_xml_model_set_pcdata ($branch, $data); return &xst_xml_model_save ($model, $file, $compressed); } sub xst_replace_xml_attribute { my ($file, $varpath, $attr, $value) = @_; my ($model, $branch, $fd, $compressed); ($model, $compressed) = &xst_xml_model_scan ($file); $branch = &xst_xml_model_ensure ($model, $varpath); &xst_xml_model_set_attribute ($branch, $attr, $value); return &xst_xml_model_save ($model, $file, $compressed); } sub xst_replace_xml_pcdata_with_type { my ($file, $varpath, $type, $data) = @_; my ($model, $branch, $fd, $compressed); ($model, $compressed) = &xst_xml_model_scan ($file); $branch = &xst_xml_model_ensure ($model, $varpath); &xst_xml_model_set_pcdata ($branch, $data); &xst_xml_model_set_attribute ($branch, "TYPE", $type); return &xst_xml_model_save ($model, $file, $compressed); } sub xst_replace_xml_attribute_with_type { my ($file, $varpath, $attr, $type, $value) = @_; my ($model, $branch, $fd, $compressed); ($model, $compressed) = &xst_xml_model_scan ($file); $branch = &xst_xml_model_ensure ($model, $varpath); &xst_xml_model_set_attribute ($branch, $attr, $value); &xst_xml_model_set_attribute ($branch, "TYPE", $type); return &xst_xml_model_save ($model, $file, $compressed); } sub xst_replace_alchemist_ensure_list_types { my ($model, $varpath, $setpath) = @_; my ($branch, @path); $branch = &xst_xml_model_find ($model, $varpath); @path = split /\//, $setpath; # NOTE: The following could be done with a depth-iterator callback from a func # similar to xst_xml_model_find (). for $elem (@path) { next if ($elem eq ""); my @children = @$branch; shift @children; # Attributes $branch = undef; while (@children) { if ($children [0] eq $elem) { shift @children; $branch = shift @children; &xst_xml_model_set_attribute ($branch, "TYPE", "LIST"); last; } shift @children; shift @children; } last if ($branch == undef); } } sub xst_replace_alchemist { my ($file, $varpath, $type, $value) = @_; my ($fullpath, $model, $branch, $fd, $compressed); $fullpath = "/adm_context/datatree/" . $varpath; ($model, $compressed) = &xst_xml_model_scan ($file); $branch = &xst_xml_model_ensure ($model, $fullpath); &xst_replace_alchemist_ensure_list_types ($model, "/adm_context/datatree/", $varpath); &xst_xml_model_set_attribute ($branch, "VALUE", $value); &xst_xml_model_set_attribute ($branch, "TYPE", $type); return &xst_xml_model_save ($model, $file, $compressed); } sub xst_replace_alchemist_print { my ($file, $printer, $varpath, $type, $value) = @_; my ($fullpath, $model, $branch, $fd, $compressed); $fullpath = "/adm_context/datatree/printconf/print_queues/" . $printer . "/" . $varpath; ($model, $compressed) = &xst_xml_model_scan ($file); $branch = &xst_xml_model_ensure ($model, $fullpath); &xst_replace_alchemist_ensure_list_types ($model, "/adm_context/datatree/", "printconf/print_queues/" . $printer . "/" . $varpath); &xst_xml_model_set_attribute ($branch, "VALUE", $value); &xst_xml_model_set_attribute ($branch, "TYPE", $type); $branch = &xst_xml_model_find ($model, "/adm_context/datatree/printconf/print_queues/" . $printer); &xst_xml_model_set_attribute ($branch, "ATOMIC", "TRUE"); return &xst_xml_model_save ($model, $file, $compressed); } # This could be split up. sub xst_replace_alchemist_print_option { my ($file, $printer, $name, $type, $value) = @_; my ($varpath, $model, $branch, $fd, $compressed, $options, $option); ($model, $compressed) = &xst_xml_model_scan ($file); $branch = &xst_xml_model_ensure ($model, "/adm_context/datatree/printconf/print_queues/" . $printer . "/filter_data/foomatic_defaults"); &xst_replace_alchemist_ensure_list_types ($model, "/adm_context/datatree/", "printconf/print_queues/" . $printer . "/filter_data/foomatic_defaults"); &xst_xml_model_set_attribute ($branch, "ANONYMOUS", "TRUE"); # See if option is already defined. $options = &xst_xml_model_get_children ($branch); foreach $o (@$options) { my $opt_node = &xst_xml_model_find ($o, "name"); next if (!$opt_node); if (&xst_xml_model_get_attribute ($opt_node, "VALUE") eq $name) { $option = $o; last; } } # If not, create node for it. if (!$option) { $option = &xst_xml_model_add ($branch, "", $option_default); &xst_xml_model_set_attribute ($option, "TYPE", "LIST"); } # Set the option attributes. my $node = &xst_xml_model_ensure ($option, "name"); &xst_xml_model_set_attribute ($node, "TYPE", "STRING"); &xst_xml_model_set_attribute ($node, "VALUE", $name); $node = &xst_xml_model_ensure ($option, "type"); &xst_xml_model_set_attribute ($node, "TYPE", "STRING"); &xst_xml_model_set_attribute ($node, "VALUE", $type); $node = &xst_xml_model_ensure ($option, "default"); &xst_xml_model_set_attribute ($node, "TYPE", "STRING"); &xst_xml_model_set_attribute ($node, "VALUE", $value); return &xst_xml_model_save ($model, $file, $compressed); }