File Coverage

blib/lib/Config/Cfe.pm
Criterion Covered Total %
statement 179 477 37.5
branch 41 166 24.7
condition 5 18 27.7
subroutine 19 63 30.1
pod 0 59 0.0
total 244 783 31.1


line stmt bran cond sub pod time code
1             # $Id: Cfe.pm,v 1.49 2002/06/03 10:22:08 jh Exp $
2              
3             # Copyright 2001 Jörgen Hägg
4             # You may distribute under the terms of the GNU General Public License.
5              
6             =head1 NAME
7              
8             Config::Cfe - File configuration module
9              
10             =head1 SYNOPSIS
11              
12             use Config::Cfe;
13              
14             =head1 DESCRIPTION
15              
16             This module contains functions that ease updating
17             small text files, usually konfiguration files.
18             Will complain if the file is more than 100000 lines (configurable).
19              
20             It is inspired from cfengine, but does only the editing, cfengine
21             can do much more.
22              
23              
24             =head1 is_sunos is_sunos4 is_sunos5
25             is_freebsd is_linux is_debian
26             is_i386 is_i486 is_i586 is_i686
27              
28             Boolean test functions
29              
30             =cut
31              
32             package Config::Cfe;
33 1     1   1329 use strict;
  1         2  
  1         49  
34 1     1   5 use vars qw($VERSION @ISA @EXPORT $debug $verbose $par);
  1         1  
  1         141  
35              
36             ($VERSION) = '$Revision: 1.49 $ ' =~ /\$Revision:\s+([^\s]+)/;
37              
38 1     1   6 use Carp;
  1         12  
  1         8355  
39             require Exporter;
40             @ISA = qw(Exporter);
41             @EXPORT = qw(read_file write_file abort_file new_file
42             par_os_type par_os_rev par_mach_type
43             locate incr goto_top goto_end
44             append insert append_file delete_all delete_to delete_n delete_file
45             find_str eval_to eval_all eval_n add_list
46             eval_where replace_all
47             exec_where
48             set_comment
49             comment_where comment_to comment_n uncomment_where uncomment_to
50             uncomment_n set_debug
51             insert_sect append_sect start_sect end_sect delete_sect find_sect
52             uncomment_sect comment_sect get_sect
53             is_sunos4 is_sunos5 is_freebsd is_sunos is_linux
54             is_i386 is_i486 is_i586 is_i686
55             is_debian
56             curr_line last_line list_lines get_line
57             cfe_version
58             );
59              
60             my (@verb_save);
61              
62             =head1 predefined functions
63              
64             A few predefined boolean functions are available
65             for easy architecture checking:
66              
67             Function True if this is
68             _________________________________________
69             is_debian a Debian GNU/Linux system, returns the actual version
70             is_linux a Linux system
71             is_i386 a 386-based system
72             is_i486 a 486-based system
73             is_i586 a 586-based system
74             is_i686 a 686-based system
75             is_sunos a SunOS system
76             is_sunos4 a SunOS 4 system
77             is_sunos5 a SunOS 5 system
78             is_freebsd a FreeBSD system
79              
80             =head1 predefined parameter functions
81              
82             These functions returns some interesting values about the OS.
83              
84             Function Returns
85             _________________________________
86             par_os_type OS type from 'uname'
87             par_os_rev OS revision from 'uname', modified to integer
88             par_mach_type OS architecture from 'uname'
89              
90             =cut
91              
92             ######################################################################
93              
94             =head1 cfe_version [minimal version]
95              
96             Returns the current version of the B-module.
97             If called with a version number, cfe will die if B is less than
98             this version.
99              
100             =cut
101              
102             sub cfe_version {
103 0     0 0 0 my ($min_vers) = @_;
104              
105 0         0 my $rev = '$Revision: 1.49 $';
106 0         0 my ($v) = $rev =~ /:\s+([\d\.]+)/;
107 0         0 my $ndv = sprintf("%.3f", $v);
108 0         0 $ndv =~ s/\.//;
109 0         0 my $min_ndv = sprintf("%.3f", $min_vers);
110 0         0 $min_ndv =~ s/\.//;
111              
112 0 0 0     0 if ($min_vers > 0 && $ndv < $min_ndv) {
113 0         0 croak sprintf("This program requires Config::Cfe ".
114             "version %.3f or greater, abort\n", $min_vers);
115             }
116 0         0 $ndv;
117             }
118              
119             ######################################################################
120              
121             =head1 set_debug [debug [verbose]]
122              
123             If B is non-zero then B will write
124             the new file but B rename the file from I to I.
125             B greater than 0 will print out debug info.
126              
127             Example:
128              
129             use Getopt::Std;
130             getopt('dv');
131              
132             $host = hostname;
133             set_debug($opt_d, $opt_v);
134              
135             =cut
136              
137             sub set_debug {
138 0     0 0 0 $debug = $_[0];
139 0         0 $verbose = $_[1];
140 0         0 for my $i (@verb_save) {
141 0 0       0 print $i if $verbose;
142             }
143 0         0 @verb_save = ();
144             }
145              
146             =head1 new_file filename
147              
148             Start editing a new file, will overwrite any existing file.
149             A new file will get file mode 0666 & umask.
150              
151             =cut
152              
153             sub new_file {
154 0     0 0 0 my ($file) = @_;
155              
156             # &lock($file);
157 0         0 $par->{'file_name'} = $file;
158 0         0 $par->{'edit'} = $par->{'cur_line'} = 0;
159 0         0 $par->{'lines'} = [];
160 0         0 &verbose("new_file", $file);
161             }
162              
163             =head1 read_file filename [,1]
164              
165             Start editing an existing file.
166             If I doesn't exist, B will
167             either complain or, if optional second argument is true, create
168             the file.
169              
170             =cut
171              
172             sub read_file {
173 1     1 0 196 my ($file, $exist_flag) = @_;
174              
175             # &lock($file);
176 1 50 33     8 if ($exist_flag && !-e $file) {
177 0         0 &new_file($file);
178 0         0 $par->{'mode'} = 0666 & umask;
179 0         0 $par->{'uid'} = $>;
180 0         0 $par->{'gid'} = $);
181 0         0 &verbose('read_file', sprintf("mode=0%o, uid=%d, gid=%d",
182             $par->{'mode'}, $par->{'uid'}, $par->{'gid'}));
183 0         0 return;
184             }
185            
186 1         7 &get_filemode($file);
187 1         3 $par->{'file_name'} = $file;
188 1         4 $par->{'edit'} = $par->{'cur_line'} = 0;
189 1         3 my $lines = $par->{'lines'} = [];
190 1 50       14 unless (-e $file) {
191 0         0 &verbose("read_file", "$file does not exist");
192 0         0 return;
193             }
194 1 50       30 open(FILE, $file) || croak "open $file:$!";
195 1         15 while() {
196 14         16 chop;
197 14         23 push(@$lines, $_);
198 14 50       50 croak "too many lines" if @$lines >= $par->{'max_lines'};
199             }
200 1         9 close(FILE);
201 1         8 &verbose("read_file", "$file, ".(@$lines*1)." lines");
202             }
203              
204             =head1 write_file [\%par]
205              
206             Write the current file if it has been changed.
207             Control of the file can be done thru the hash parameter B.
208             The old file will be renamed to B<.>IB<.cfe>.
209             Will not rename the final file (I) to I
210             if debug is active.
211              
212             Accepted keys for B:
213              
214             key name default
215             --------------------------------------
216             mode filemode 0666 & umask.
217             uid numeric userid 0
218             gid numeric groupid 0
219              
220             =cut
221              
222             sub write_file {
223 1     1 0 5 my (%tpar) = @_;
224 1         1 my ($tmp, $fsize, $file);
225              
226 1 50       4 return unless $par->{'edit'};
227              
228 1 50       8 $tpar{'mode'} = $par->{'mode'} unless $tpar{'mode'};
229 1 50       6 $tpar{'uid'} = $par->{'uid'} unless $tpar{'uid'};
230 1 50       5 $tpar{'gid'} = $par->{'gid'} unless $tpar{'gid'};
231              
232 1 50       4 croak "No filename!" unless $file = $par->{'file_name'};
233 1         5 ($tmp = $file) =~ s/$/.new/;
234 1 50       111 open(FILEO, ">$tmp") || croak "can't create $tmp:$!";
235 1         32 chmod $tpar{'mode'}, $tmp;
236 1         23 chown $tpar{'uid'}, $tpar{'gid'}, $tmp;
237 1         2 for my $line (@{$par->{'lines'}}) {
  1         6  
238 16         31 print FILEO "$line\n";
239 16         22 $fsize += length($line)+1;
240             }
241 1         43 close(FILEO);
242 1 50       15 &abort_file("incorrect filesize, abort") if -s $tmp != $fsize;
243 1 50       4 unless ($debug) {
244 1         107 rename($file, ".$file.cfe");
245 1 50       45 rename($tmp, $file) || &abort_file("rename $tmp to $file:$!");
246             }
247 1         4 $par->{'lines'} = [];
248 1         8 &verbose('write_file', "wrote $file, ".
249 1         5 (@{$par->{'lines'}}*1)." lines, $fsize bytes");
250             # &unlock($file);
251 1         3 &reset_par;
252             }
253              
254             =head1 abort_file
255              
256             Quit editing the current file.
257              
258             =cut
259              
260             sub abort_file {
261 0     0 0 0 my ($txt) = @_;
262 0         0 my ($tmp, $fsize, $file);
263              
264 0 0       0 croak "No filename!" unless $file = $par->{'file_name'};
265 0         0 ($tmp = $file) =~ s/$/.new/;
266 0 0       0 unlink($tmp) || croak "unlink $tmp:$!";
267 0 0       0 croak $txt if $txt;
268             # &unlock($file);
269 0         0 &reset_par;
270             }
271              
272             =head1 append_file filename
273              
274             Appends B at the current line.
275              
276             =cut
277              
278             sub append_file {
279 0     0 0 0 my ($file) = @_;
280 0         0 my (@tmp);
281              
282 0         0 &verbose("append_file", "**** $file");
283 0 0       0 open(FILE, $file) || croak "open $file:$!";
284 0         0 while() {
285 0         0 chop;
286 0         0 push(@tmp, $_);
287 0         0 &verbose("append_file", $_);
288 0         0 croak "too many lines"
289 0 0       0 if @{$par->{'lines'}} >= $par->{'max_lines'};
290             }
291 0         0 close(FILE);
292 0         0 &append(@tmp);
293             }
294              
295             =head1 list_lines [from_line [to_line]]
296              
297             This is a debug function, it will list all current lines to stdout.
298              
299             =cut
300              
301             sub list_lines {
302 4     4 0 17 my ($from, $to) = @_;
303 4         5 my ($i);
304              
305 4         7 my $lines = $par->{'lines'};
306 4 50       11 $from = 0 if $from eq '';
307 4 50       11 $to = @$lines if $to eq '';
308              
309 4         13 for ($i = $from; $i < $to; $i++) {
310 65         247 printf STDOUT "%3d: %s\n", $i, $lines->[$i];
311             }
312             }
313             ######################################################################
314              
315             =head1 get_line from_line [to_line]
316              
317             B will return a pointer to an array with the
318             lines from I up to, not including I,
319             or the specified line if I isn't specified.
320             The array is a copy of the real lines, any changes to this
321             array will be lost.
322              
323             =cut
324              
325             sub get_line {
326 0     0 0 0 my ($from, $to) = @_;
327 0         0 my $ret = [];
328              
329 0 0       0 croak "no start line defined" if !defined $from;
330 0         0 my $lines = $par->{'lines'};
331 0 0       0 return $lines->[$from] if !defined $to;
332              
333 0         0 my $i;
334 0         0 for ($i = $from; $i < $to; $i++) {
335 0         0 push(@$ret, $lines->[$i]);
336             }
337 0         0 return $ret;
338             }
339             ######################################################################
340              
341             =head1 locate regexp
342              
343             Search for I, returns true at the first occurance.
344             B always starts at the beginning.
345              
346             =cut
347              
348             sub locate {
349 0     0 0 0 my ($regexp) = @_;
350 0         0 my ($n, $x);
351              
352 0         0 $n = $par->{'cur_line'} = &find_row(0, $regexp);
353 0 0       0 $x = '*found*: '.$par->{'lines'}->[$n] if $n >= 0;
354 0         0 &verbose("locate", "'$regexp': $x");
355 0         0 return $n >= 0;
356             }
357              
358             =head1 incr [[-]n]
359              
360             Increments the line pointer, negative values allowed.
361             Returns the new line number.
362              
363             =cut
364              
365             sub incr {
366 0     0 0 0 my ($n) = @_;
367              
368 0 0       0 $n = 1 unless $n;
369 0         0 carp("outside file"), return
370 0 0       0 if $par->{'cur_line'}+$n > @{$par->{'lines'}};
371 0         0 $par->{'cur_line'} += $n;
372 0         0 &verbose("incr", "$n, cur_line: ".$par->{'cur_line'});
373 0         0 $par->{'cur_line'};
374             }
375              
376             =head1 goto_top
377              
378             Set the first line to be the current line.
379              
380             =cut
381              
382             sub goto_top {
383 1     1 0 6 $par->{'cur_line'} = 0;
384 1         3 &verbose("goto_top", "beginning");
385             }
386              
387             =head1 goto_end
388              
389             Set the last line to be the current line.
390              
391             =cut
392              
393             sub goto_end {
394 0     0 0 0 $par->{'cur_line'} = @{$par->{'lines'}}-1;
  0         0  
395 0         0 &verbose("goto_end", "eof");
396             }
397              
398             =head1 curr_line [N]
399              
400             Returns the current line number.
401             Sets the current line number if arg B exist;
402              
403             =cut
404              
405             sub curr_line {
406 0 0   0 0 0 if (@_) {
407 0         0 $par->{'cur_line'} = shift;
408             }
409 0         0 return $par->{'cur_line'};
410             }
411              
412             =head1 last_line
413              
414             Returns the last line number.
415              
416             =cut
417              
418             sub last_line {
419 0     0 0 0 return @{$par->{'lines'}}*1-1;
  0         0  
420             }
421             ######################################################################
422              
423             =head1 append line1 [line2 [...]]
424              
425             Appends one or more lines after the current line.
426             The line pointer will point to the last appended line.
427              
428             =cut
429              
430             sub append {
431 3     3 0 9 my @new = &split_cr(@_);
432              
433 3 50       8 &verbose("append", '**** start') if @new > 1;
434 3         4 splice(@{$par->{'lines'}}, $par->{'cur_line'}+1, 0, @new);
  3         11  
435 3         5 $par->{'cur_line'} += @new;
436 3         6 &verbose("append", @new, "**** end");
437 3         8 ++$par->{'edit'};
438             }
439              
440             =head1 insert line1 [line2 [...]]
441              
442             Inserts one or more lines before the current line.
443             The line pointer will point to the first inserted line.
444              
445             =cut
446              
447             sub insert {
448 1     1 0 9 my @new = &split_cr(@_);
449              
450 1 50       6 &verbose("insert", '**** start') if @new > 1;
451 1         1 splice(@{$par->{'lines'}}, $par->{'cur_line'}, 0, @new);
  1         5  
452 1         828 &verbose("insert", @new, "**** end, cur_line: ".$par->{'cur_line'});
453 1         4 ++$par->{'edit'};
454             }
455              
456             ######################################################################
457              
458             =head1 add_list prefix listpointer [separator [newlinestring [suffix [length]]]]
459              
460             Append a list after the current line.
461             The result is one or more lines starting with B and
462             the list joined by B. If the line is too long it will
463             be broken into several separated by B.
464             Default for B is 'B<:>' and for B 'B<\\\n>'.
465             Default for B is 75.
466             Example:
467              
468             @list = qw(/bin /usr/bin /usr/local/bin);
469             add_list PATH= \@list;
470              
471             will look like this:
472              
473             PATH=/bin:/usr/bin:/usr/local/bin
474              
475             =cut
476              
477             sub add_list {
478 0     0 0 0 my ($prefix, $list, $sep, $newl, $suffix, $max) = @_;
479 0         0 my ($str, $newstr, $new, $cnt, @new);
480              
481 0 0       0 $max = 75 unless $max;
482 0 0       0 $newl = "\\\n" unless $newl;
483 0 0       0 $sep = ':' unless $sep;
484 0         0 $str = $prefix;
485 0         0 my $i;
486 0         0 for ($i = 0; $i < @$list; $i++) {
487 0         0 $new = $list->[$i];
488 0 0       0 $str .= $sep if $cnt++;
489 0 0       0 if (length($str)+length($new) > $max) {
490 0         0 push(@new, $str);
491 0         0 $str = '';
492             }
493 0         0 $str .= $new;
494             }
495 0 0       0 push(@new, $str) if $str;
496 0         0 $newstr = join($newl, @new);
497 0         0 $newstr .= $suffix;
498 0         0 &verbose("add_list", $newstr);
499 0         0 &append($newstr);
500             }
501              
502             ######################################################################
503              
504             =head1 delete_all regexp
505              
506             Deletes all lines containing B.
507             Returns the deleted lines.
508             If B is a pointer to a function or a anonymous function, it
509             will be executed with current line number and a pointer to the
510             current line as argument. The line will be deleted if the function
511             returns true.
512              
513             =cut
514              
515             sub delete_all {
516 1     1 0 6 my ($regexp) = @_;
517 1         1 my (@del, $lines, $new);
518 1         2 my $line = 0;
519              
520 1         4 &verbose("delete_all", "**** $regexp");
521 1         3 $lines = $par->{'lines'};
522 1         2 $new = [];
523              
524 1         5 for (my $i = 0; $i < @$lines; $i++) {
525 19         26 $_ = $lines->[$i];
526 19 50       32 if (ref($regexp) =~ /CODE/) {
527 0         0 my $del = &$regexp($i, \$lines->[$i]);
528 0 0       0 if ($del) {
529 0         0 &verbose("deleted", "$i:$_");
530 0         0 push(@del, $_);
531             }
532             else {
533 0         0 push(@$new, $_);
534             }
535 0         0 next;
536             }
537 19 100       56 unless (/$regexp/) {
538 16         49 push(@$new, $_);
539             }
540             else {
541 3         10 &verbose("deleted", "$i:$_");
542 3         10 push(@del, $_);
543             }
544             }
545 1         2 $par->{'lines'} = $new;
546 1         3 $par->{'edit'} += @del*1;
547 1         5 return @del;
548             }
549              
550             =head1 delete_to regexp
551              
552             Deletes all lines from the current to the line
553             matching B.
554             Returns '' if regexp not found, otherwhise returns the
555             deleted lines.
556             Current line will be the line preceding the deleted area.
557              
558             =cut
559              
560             sub delete_to {
561 0     0 0 0 my ($regexp) = @_;
562 0         0 my (@d, $i, $line, $cur);
563              
564 0         0 my $cur = $par->{'cur_line'};
565 0         0 my $last = &find_row($cur, $regexp);
566 0 0       0 return 0 unless $last >= 0;
567              
568 0         0 my @d = splice(@{$par->{'lines'}}, $cur, $last-$cur+1);
  0         0  
569 0         0 $par->{'cur_line'}--;
570 0         0 &verbose("delete_to", "*** $regexp", @d);
571 0         0 $par->{'edit'} += @d*1;
572 0         0 return @d;
573             }
574              
575             =head1 delete_n N
576              
577             Deletes B lines from the current.
578             Deletes to the end of file if N equal C.
579             Returns the deleted lines;
580             Current line will be the line preceding the deleted area.
581              
582             =cut
583              
584             sub delete_n {
585 0     0 0 0 my ($n) = @_;
586 0         0 my (@d);
587              
588 0 0       0 $n = &last_line+1 if $n eq 'end';
589 0         0 @d = splice(@{$par->{'lines'}}, $par->{'cur_line'}, $n);
  0         0  
590 0         0 &verbose("delete_n", $n, @d);
591 0         0 $par->{'cur_line'}--;
592 0         0 return @d;
593 0         0 $par->{'edit'}++;
594             }
595              
596             =head1 delete_file
597              
598             Deletes all lines.
599              
600             =cut
601              
602             sub delete_file {
603 0     0 0 0 $par->{'lines'} = [];
604 0         0 $par->{'edit'}++;
605             }
606              
607             ######################################################################
608              
609             =head1 find_str regexp
610              
611             B returns true if any line matching B is found.
612             Current line is not changed.
613              
614             =cut
615              
616             sub find_str {
617 0     0 0 0 my ($regexp) = @_;
618              
619 0         0 return &find_row(0, $regexp) >= 0;
620             }
621              
622             ######################################################################
623              
624             =head1 eval_to regexp exp
625              
626             B will evaluate B on each line up to the line
627             matching B.
628             Returns true if any line was changed.
629             B can be any perl expression like C<'s/bin/usr/'>.
630             If B is a pointer to a function or a anonymous function, it
631             will be executed with current line number and a pointer to the
632             current line as argument.
633             Changes to the current line will be preserved.
634              
635             =cut
636              
637             sub eval_to {
638 0     0 0 0 my ($regexp, $eval) = @_;
639 0         0 my ($edit, $i, $line, $cur, $last);
640              
641 0         0 $cur = $par->{'cur_line'};
642 0         0 $last = &find_row($cur, $regexp);
643 0 0       0 carp "can't find $regexp" unless $last >= 0;
644              
645 0         0 my $lines = $par->{'lines'};
646 0         0 &verbose("eval_to", "$regexp, $eval");
647 0         0 for ($i = $cur; $i <= $last; $i++) {
648 0 0       0 if (eval "\$par->{'lines'}->[\$i] =~ $eval") {
649 0 0       0 if (ref($eval) =~ /CODE/) {
650 0         0 my $tline = $lines->[$i];
651 0         0 &$eval($i, \$lines->[$i]);
652 0 0       0 if ($tline ne $lines->[$i]) {
653 0         0 &verbose("exec", "$i: ".$lines->[$i]);
654 0         0 $edit++;
655             }
656 0         0 next;
657             }
658 0         0 $edit++;
659 0         0 &verbose("eval", "$i: ".$lines->[$i]);
660             }
661             }
662 0         0 $par->{'edit'} += $edit;
663 0         0 return $edit;
664             }
665              
666             =head1 eval_all exp
667              
668             B will evaluate B on each line in the file.
669             Returns true if any line was changed.
670             See B.
671              
672             =cut
673              
674             sub eval_all {
675 0     0 0 0 my ($eval) = @_;
676 0         0 my ($edit, $i, $line, $cur, $last);
677              
678 0         0 my $lines = $par->{'lines'};
679 0         0 &verbose("eval_all", "**** $eval");
680 0         0 for ($i = 0; $i < @$lines; $i++) {
681 0 0       0 if (ref($eval) =~ /CODE/) {
    0          
682 0         0 my $tline = $lines->[$i];
683 0         0 &$eval($i, \$lines->[$i]);
684 0 0       0 if ($tline ne $lines->[$i]) {
685 0         0 &verbose("exec", "$i: ".$lines->[$i]);
686 0         0 $edit++;
687             }
688 0         0 next;
689             }
690             elsif (eval "\$lines->[\$i] =~ $eval") {
691 0         0 &verbose("eval", "$i: ".$lines->[$i]);
692 0         0 $edit++;
693             }
694             }
695 0         0 $par->{'edit'} += $edit;
696 0         0 return $edit;
697             }
698              
699             =head1 eval_n N exp
700              
701             B will evaluate B on B lines from the current line.
702             Returns true if any line was changed.
703             A value of 0 will change the current line, 1 will change the current
704             and the next line.
705             See B.
706             Evaluates to the end of file if N equal C.
707              
708             =cut
709              
710             sub eval_n {
711 0     0 0 0 my ($n, $eval) = @_;
712 0         0 my ($edit, $i, $line, $cur, $last, $start);
713              
714 0 0       0 $n = &last_line if $n eq 'end';
715 0         0 my $lines = $par->{'lines'};
716 0         0 $start = $par->{'cur_line'};
717 0         0 $last = $start+$n;
718 0 0       0 $last = @$lines-1 if $last >= @$lines;
719 0         0 &verbose("eval_n", "**** $n,$eval: $start-$last");
720 0         0 for ($i = $start; $i <= $last; $i++) {
721 0 0       0 if (ref($eval) =~ /CODE/) {
722 0         0 my $tline = $lines->[$i];
723 0         0 &$eval($i, \$lines->[$i]);
724 0 0       0 if ($tline ne $lines->[$i]) {
725 0         0 &verbose("exec", "$i: ".$lines->[$i]);
726 0         0 $edit++;
727             }
728 0         0 next;
729             }
730 0 0       0 if (eval "\$lines->[\$i] =~ $eval") {
731 0         0 &verbose("eval_n", "$i: ".$lines->[$i]);
732 0         0 $edit++;
733             }
734             }
735 0         0 $par->{'edit'} += $edit;
736 0         0 return $edit;
737             }
738              
739             =head1 eval_where regexp1 exp1 [regexp2 exp2 [...]]
740              
741             B will evaluate B for each line
742             matching B.
743             Returns true if any line was changed.
744             See B.
745              
746             =cut
747              
748             sub eval_where {
749 1     1 0 10 my ($i, $line, $cur, $last);
750 0         0 my ($eval, $regexp, $edit);
751              
752 1         5 my $lines = $par->{'lines'};
753 1         3 &verbose("eval_where", '**** start');
754 1   66     14 while(($regexp = shift) && ($eval = shift)) {
755 1         5 &verbose("eval", "$regexp, $eval");
756 1         5 for ($i = 0; $i < @$lines; $i++) {
757 16 100       62 if ($lines->[$i] =~ /$regexp/) {
758 3 50       10 if (ref($eval) =~ /CODE/) {
759 3         5 my $tline = $lines->[$i];
760 3         8 &$eval($i, \$lines->[$i]);
761 3 50       28 if ($tline ne $lines->[$i]) {
762 3         9 &verbose("exec",
763             "$i: ".$lines->[$i]);
764 3         4 $edit++;
765             }
766 3         9 next;
767             }
768 0 0       0 if (eval "\$lines->[\$i] =~ $eval") {
769 0         0 &verbose("eval", "$i: ".$lines->[$i]);
770 0         0 $edit++;
771             }
772             }
773             }
774             }
775 1         3 $par->{'edit'} += $edit;
776 1         3 return $edit;
777             }
778              
779             ######################################################################
780              
781             =head1 replace_all from1 to1 [from2 to2 [...]]
782              
783             B replaces all occurrences of the string
784             B with the string B.
785              
786             =cut
787              
788             sub replace_all {
789 0     0 0 0 my ($from, $to, $i);
790 0         0 my ($edit, $lines);
791              
792 0         0 &verbose("replace_all", '**** start');
793 0   0     0 while(($from = shift) && ($to = shift)) {
794 0         0 &verbose("replace_all", "$from -> $to");
795 0         0 $lines = $par->{'lines'};
796 0         0 for ($i = 0; $i < @$lines; $i++) {
797 0 0       0 if ($lines->[$i] =~ s/$from/$to/g) {
798 0         0 &verbose("replace_all", "$i: ".$lines->[$i]);
799 0         0 $edit++;
800             }
801             }
802             }
803 0         0 $par->{'edit'} += $edit;
804 0         0 return $edit;
805             }
806              
807             ######################################################################
808              
809             =head1 set_comment [start_comment [end_comment]]
810              
811             B sets the current comment strings.
812             B defaults to B<'# '>,
813             B defaults to B<''>.
814             Using B whithout arguments will set the
815             default comment strings again.
816              
817             =cut
818              
819             sub set_comment {
820 3     3 0 7 my ($cstart, $cend) = @_;
821            
822 3 50       26 $cstart = $par->{'cstart'} = '# ' unless $cstart;
823 3 50       13 $cend = $par->{'cend'} = '' unless $cend;
824 3         17 &verbose("set_comment", "$par->{'cstart'}, $par->{'cend'}");
825 3         8 my $x = ', do not change this line!';
826              
827 3         8 $par->{'begin_sect'} = $cstart.'CFE begin <%s>'.$x;
828 3         8 &verbose("set_comment", $par->{'begin_sect'});
829              
830 3         9 $par->{'end_sect'} = $cstart.'CFE end <%s>'.$x;
831 3         8 &verbose("set_comment", $par->{'end_sect'});
832              
833 3         14 $par->{'find_sect'} = '^'.quotemeta($cstart).'CFE %s <%s>';
834 3         13 &verbose("set_comment", $par->{'find_sect'});
835             }
836              
837             =head1 comment_where regexp
838              
839             B inserts a comment in the beginning of each
840             line matching B.
841             See also B.
842              
843             =cut
844              
845             sub comment_where {
846 0     0 0 0 my ($regexp) = @_;
847 0         0 my ($cstart, $cend);
848            
849 0         0 &verbose("comment_where", $regexp);
850 0         0 $cstart = $par->{'cstart'};
851 0         0 $cend = $par->{'cend'};
852 0         0 return &eval_where($regexp, "s/^(.*)\$/$cstart\$1$cend/");
853             }
854              
855             =head1 comment_to regexp
856              
857             B inserts a comment in the beginning of each
858             line from the current line up to the line
859             matching B.
860             See also B.
861              
862             =cut
863              
864             sub comment_to {
865 0     0 0 0 my ($regexp) = @_;
866 0         0 my ($cstart, $cend);
867            
868 0         0 &verbose("comment_to", $regexp);
869 0         0 $cstart = $par->{'cstart'};
870 0         0 $cend = $par->{'cend'};
871 0         0 return &eval_to($regexp, "s/^(.*)\$/$cstart\$1$cend/");
872             }
873              
874             =head1 comment_n N
875              
876             B inserts a comment in the beginning of B
877             lines starting with the current line.
878             See also B.
879             Comments to the end of file if N equal C.
880              
881             =cut
882              
883             sub comment_n {
884 0     0 0 0 my ($n) = @_;
885 0         0 my ($cstart, $cend);
886            
887 0         0 &verbose("comment_n", $n);
888 0 0       0 $n = last_line if $n eq 'end';
889 0         0 $cstart = $par->{'cstart'};
890 0         0 $cend = $par->{'cend'};
891 0         0 return &eval_n($n, "s/^(.*)\$/$cstart\$1$cend/");
892             }
893              
894             =head1 uncomment_where regexp
895              
896             B removes the comment in the beginning of each
897             line matching B.
898             See also B.
899              
900             =cut
901              
902             sub uncomment_where {
903 0     0 0 0 my ($regexp) = @_;
904 0         0 my ($cstart, $cend);
905            
906 0         0 &verbose("uncomment_where", $regexp);
907 0         0 $cstart = $par->{'cstart'};
908 0         0 $cend = $par->{'cend'};
909 0         0 return &eval_where($regexp, "s/^$cstart(.*)$cend\$/\$1/");
910             }
911              
912             =head1 uncomment_to regexp
913              
914             B removes the comment in the beginning of each
915             line from the current line up to the line
916             matching B.
917             See also B.
918              
919             =cut
920              
921             sub uncomment_to {
922 0     0 0 0 my ($regexp) = @_;
923 0         0 my ($cstart, $cend);
924            
925 0         0 &verbose("uncomment_to", $regexp);
926 0         0 $cstart = $par->{'cstart'};
927 0         0 $cend = $par->{'cend'};
928 0         0 return &eval_to($regexp, "s/^$cstart(.*)$cend\$/\$1/");
929             }
930              
931             =head1 uncomment_n N
932              
933             B inserts a comment in the beginning of B
934             lines starting with the current line.
935             See also B and B.
936             Uncomments to the end of file if N equal C.
937              
938             =cut
939              
940             sub uncomment_n {
941 0     0 0 0 my ($n) = @_;
942 0         0 my ($cstart, $cend);
943            
944 0         0 &verbose("uncomment_n", $n);
945 0 0       0 $n = last_line if $n eq 'end';
946 0         0 $cstart = $par->{'cstart'};
947 0         0 $cend = $par->{'cend'};
948 0         0 return &eval_n($n, "s/^$cstart(.*)$cend\$/\$1/");
949             }
950             ######################################################################
951              
952             =head1 append_sect section
953              
954             Start appending a new section by adding a B tag named
955             I
.
956              
957             =cut
958              
959             sub append_sect {
960 1     1 0 6 my ($section) = @_;
961              
962 1         2 &verbose("append_sect", $section);
963 1         10 &append(sprintf($par->{'begin_sect'}, $section));
964 1         2 $par->{'cur_sect'} = $section;
965             }
966              
967             =head1 insert_sect section
968              
969             Start inserting a new section
970              
971             =cut
972              
973             sub insert_sect {
974 0     0 0 0 my ($section) = @_;
975              
976 0         0 &verbose("start_sect", $section);
977 0         0 &insert(sprintf($par->{'begin_sect'}, $section));
978 0         0 $par->{'cur_sect'} = $section;
979             }
980             sub start_sect {
981 0     0 0 0 &insert_sect($_[0]);
982             }
983              
984             =head1 end_sect
985              
986             Appends a comment at current line that marks the end of the section.
987              
988             =cut
989              
990             sub end_sect {
991 1 50   1 0 7 carp "No active section!" unless $par->{'cur_sect'};
992 1         2 &verbose("end_sect", '***');
993 1         6 &append(sprintf($par->{'end_sect'}, $par->{'cur_sect'}));
994 1         3 $par->{'cur_sect'} = '';
995             }
996              
997             =head1 delete_sect section
998              
999             Deletes the section name B
.
1000             Returns true if section actually was deleted.
1001             Will set the current line.
1002              
1003             =cut
1004              
1005             sub delete_sect {
1006 0     0 0 0 my ($section) = @_;
1007            
1008 0         0 &verbose("delete_sect", $section);
1009 0 0       0 return 0 unless &locate(sprintf($par->{'find_sect'},
1010             'begin', $section));
1011 0         0 return &delete_to(sprintf($par->{'find_sect'}, 'end', $section));
1012             }
1013              
1014             =head1 find_sect section
1015              
1016             Returns true if B
exist.
1017             Will set the current line.
1018              
1019             =cut
1020              
1021             sub find_sect {
1022 0     0 0 0 my ($section) = @_;
1023            
1024 0         0 &verbose("find_sect", $section);
1025 0         0 return &locate(sprintf($par->{'find_sect'}, 'begin', $section));
1026             }
1027              
1028             =head1 comment_sect section
1029              
1030             Comment out the section name B
.
1031             Will set the current line.
1032              
1033             =cut
1034              
1035             sub comment_sect {
1036 0     0 0 0 my ($section) = @_;
1037            
1038 0         0 &verbose("comment_sect", $section);
1039 0 0       0 return 0 unless &locate(sprintf($par->{'find_sect'},
1040             'begin', $section));
1041 0         0 my $from = &incr();
1042 0         0 my $to = &find_row($from,
1043             sprintf($par->{'find_sect'}, 'end', $section));
1044 0 0       0 return unless $to >= $from;
1045 0         0 return &comment_n($to-$from-1);
1046             }
1047              
1048             =head1 uncomment_sect section
1049              
1050             Enable the section name B
by removing the comment string
1051             at the beginning of the lines. Will set the current line.
1052              
1053             =cut
1054              
1055             sub uncomment_sect {
1056 0     0 0 0 my ($section) = @_;
1057              
1058 0         0 &verbose("uncomment_sect", $section);
1059 0 0       0 return 0 unless &locate(sprintf($par->{'find_sect'},
1060             'begin', $section));
1061 0         0 my $from = &incr();
1062 0         0 my $to = &find_row($from,
1063             sprintf($par->{'find_sect'}, 'end', $section));
1064 0 0       0 return unless $to >= $from;
1065 0         0 return &uncomment_n($to-$from-1);
1066            
1067             }
1068              
1069             =head1 get_sect section
1070              
1071             Fetch the content of section named B
, return a pointer
1072             to an anonymous array.
1073             Will set the current line.
1074              
1075             =cut
1076              
1077             sub get_sect {
1078 0     0 0 0 my ($section) = @_;
1079              
1080 0         0 &verbose("get_sect", $section);
1081 0 0       0 return 0 unless &locate(sprintf($par->{'find_sect'},
1082             'begin', $section));
1083 0         0 my $from = &incr();
1084 0         0 my $to = &find_row($from,
1085             sprintf($par->{'find_sect'}, 'end', $section));
1086 0 0       0 return [] unless $to >= $from;
1087 0         0 return &get_line($from, $to);
1088            
1089             }
1090             ######################################################################
1091             # Internal subroutine
1092             # start searching from line 'start' for 'regexp';
1093             # Returns the line number or -1 if not found.
1094             sub find_row {
1095 0     0 0 0 my ($start, $regexp) = @_;
1096 0         0 my ($i, $line);
1097              
1098 0         0 my $lines = $par->{'lines'};
1099 0         0 my $l = -1;
1100 0         0 for ($i = $start; $i < @$lines; $i++) {
1101 0 0       0 $l = $i, last if $lines->[$i] =~ /$regexp/;
1102             }
1103 0         0 return $l;
1104             }
1105             # splits any multilined string and returns an array of
1106             # guaranteed single lines.
1107             sub split_cr {
1108 4     4 0 5 my ($i, @new);
1109 4         8 for my $i (@_) {
1110 5 100       18 push(@new, ''), next if $i eq '';
1111 4         16 push(@new, split(/\n/, $i));
1112             }
1113 4         12 return @new;
1114             }
1115             # Print out debug info if verbose is active.
1116             #
1117             sub verbose {
1118 42     42 0 109 my ($func, @txt) = @_;
1119 42         48 my ($l, $i);
1120              
1121            
1122 42 100       107 return if $verbose == 0;
1123 14         22 $l = $par->{'cur_line'};
1124 14         23 for my $i (@txt) {
1125 14 50       94 push(@verb_save, "$func($l): $i\n"), next if $verbose < 0;
1126 0         0 print "$func($l): $i\n";
1127             }
1128             }
1129              
1130             sub reset_par {
1131 2     2 0 8 $par->{'max_lines'} = 100000;
1132 2         6 $par->{'file_name'} = '';
1133 2         5 $par->{'num_lines'} = 0;
1134 2         4 $par->{'lines'} = [];
1135 2         5 $par->{'cur_line'} = 0;
1136 2         3 $par->{'edit'} = 0;
1137 2         18 $par->{'mode'} = 0666 & umask;
1138 2         5 $par->{'uid'} = 0;
1139 2         4 $par->{'gid'} = 0;
1140 2         5 &set_comment;
1141             }
1142             sub get_filemode {
1143 1     1 0 3 my ($f) = @_;
1144              
1145 1         24 ($par->{'mode'}, $par->{'uid'}, $par->{'gid'}) = (stat($f))[2,4,5];
1146 1         3 $par->{'mode'} &= 0777;
1147 1         11 &verbose('get_filemode',
1148             sprintf("mode=0%o, uid=%d, gid=%d", $par->{'mode'},
1149             $par->{'uid'}, $par->{'gid'}));
1150             }
1151              
1152             sub BEGIN {
1153 1     1   3 my (%type, $uname, $r, $ostype, $version, $mach_type);
1154              
1155 1         4 $par = {};
1156 1         3 &reset_par;
1157 1         19766 chop($uname = `uname -srm`);
1158 1         33 ($ostype, $version, $mach_type) = split(/\s+/, $uname);
1159 1         12 ($r = $version) =~ tr/0-9//cd;
1160 1 50       34 $r .= '0' if length($r) < 3;
1161 1         12 $r *= 1.0;
1162              
1163 1         7 ($par->{'os_type'} = $ostype) =~ tr/A-Z/a-z/;
1164 1         10 $par->{'os_rev'} = $r;
1165 1         4 ($par->{'mach_type'} = $mach_type) =~ tr/A-Z/a-z/;
1166              
1167 1 50 33     17 $type{'sunos4'} = $ostype eq 'SunOS' && $version < 5 ? $r : 0;
1168 1 50 33     8 $type{'sunos5'} = $ostype eq 'SunOS' && $version > 5 ? $r : 0;
1169 1 50       6 $type{'freebsd'} = $ostype eq 'FreeBSD' ? $r : 0;
1170 1 50       8 $type{'sunos'} = $ostype eq 'SunOS' ? $r : 0;
1171 1 50       7 $type{'linux'} = $ostype eq 'Linux' ? $r : 0;
1172              
1173 1 50       20 $type{$1} = $mach_type =~ /^(i[3-9]86)/ ? $r : 0;
1174              
1175 1         11 $verbose = -1;
1176              
1177 1 50       41 if (-f '/etc/debian_version') {
1178 1         1 my $deb;
1179              
1180 1         47 open(IN, '/etc/debian_version');
1181 1         23 chop($deb = );
1182 1         11 close(IN);
1183 1         5 $deb =~ s/\.//;
1184 1 50       5 $deb .= '0' if length($deb) < 3;
1185 1         3 $type{'debian'} = $deb;
1186             }
1187 1         7 for my $i (keys %type) {
1188 7         43 &verbose('init', "sub is_$i\{return $type{$i};}");
1189 7     0 0 499 eval "sub is_$i\{ &verbose('is_$i','$type{$i}');".
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
1190             "return \"$type{$i}\";}";
1191             }
1192 1         3 for my $i (qw(os_type os_rev mach_type)) {
1193 3         12 &verbose('init', "sub par_$i { return \"$par->{$i}\"; }");
1194 3     0 0 138 eval "sub par_$i { ".
  0     0 0    
  0     0 0    
  0            
  0            
  0            
  0            
1195             "&verbose('par_$i','$par->{$i}');".
1196             "return \"$par->{$i}\"; }";
1197             }
1198 1         9 &set_comment;
1199 1         69 $verbose = 0;
1200             }