X-Git-Url: http://ftp.carnet.hr/carnet-debian/scm?p=carnet-tools-cn.git;a=blobdiff_plain;f=cp-update;h=5514ee650c07b37d685e878133999c6c0b30726a;hp=d5b2ab24f80e7413ce0b39af4296e185fa9a9c4d;hb=refs%2Fheads%2Flenny;hpb=31ce029c7042dc34e1c9edb4de361a3db36d1724 diff --git a/cp-update b/cp-update index d5b2ab2..5514ee6 100755 --- a/cp-update +++ b/cp-update @@ -3,6 +3,7 @@ ## Copyright (C) 1998 Hrvoje Niksic ## Modification by Zeljko Boros (block entries, removing old entries) ## More options and use strict by Zoran Dzelajlija on 2004-02-24 +## Quite a few improvements by Damir Dzeko on 2005-03-18 (see bellow) ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -22,7 +23,7 @@ ## -> reformated code, introduced coding conventions ## -> increased reliability through atomic actualization ## -> everything is done in the memory - more mem, less i/o -## -> removed append mode - in-place editing considered harmfull +## -> removed append mode - in-place editing considered harmful ## -> added in-place mode as option use strict; @@ -34,7 +35,7 @@ sub DEBUG () { 0 }; # my ($ProgramName, $UsageLong, $UsageShort, $VERSION); -$VERSION = '2.0'; +$VERSION = '2.1'; # Looks nicer without the slashes and dots ($ProgramName = $0) =~ s!.*/!!; # strip dir @@ -54,12 +55,16 @@ $UsageLong = " -r | --remove Remove entry PACKAGE from FILE. Default is to add lines from stdin. + -x | --change Modify existing block, or add it if + it does not exist but the begin mark + can be found. + -m | --allow-multiple Allow multiple blocks of the same type. By default, old blocks are replaced with the new one. -h | --help Print this message (usage reference). - + --version Print version message. - Placement control: @@ -104,7 +109,7 @@ $UsageShort .= " or type $ProgramName --help to be choked with help.\n"; my ($MarkBegin, $MarkEnd, $Trailer, $ParamBegin, $ParamEnd, $Placement, $Package, $File, $Block, $Multi, $InsertRemove, $Comment, $CommentEnd, $MatchLine, $RegexpMatch, $StdinContent, $NewContent, $InPlace, - $NoClose, $FileHandle, @Lines); + $NoClose, $FileHandle, @Lines, $LinesCount); # Placement modes use constant APPEND_AT_END => 0; @@ -115,6 +120,7 @@ use constant INSERT_ON_TOP => 3; # InsertRemove modes use constant DO_REMOVE => 0; use constant DO_INSERT => 1; +use constant DO_CHANGE => 2; # Operation defaults $InsertRemove = DO_INSERT; @@ -139,6 +145,9 @@ while (@ARGV) { elsif (/^-r$/ || /^--remove$/) { $InsertRemove = DO_REMOVE; } + elsif (/^-x$/ || /^--change$/) { + $InsertRemove = DO_CHANGE; + } elsif (/^-m$/ || /^--allow-multi(?:ple)?$/) { $Multi = 1; } @@ -190,6 +199,10 @@ while (@ARGV) { unshift(@ARGV, $_); last; } + + if ($Multi and DO_CHANGE == $InsertRemove) { + die "$ProgramName: Cannot use both `--change' and `--allow-multiple'\n"; + } } $Package = shift or die $UsageShort; @@ -238,8 +251,12 @@ do_it() and exit(0); # -------------- sub do_it { - slurp(); - if (DO_INSERT == $InsertRemove) { + $LinesCount = slurp(); + if (DO_CHANGE == $InsertRemove) { + $StdinContent = &stdin_content; + change() or add(); + } + elsif (DO_INSERT == $InsertRemove) { $StdinContent = &stdin_content; del() unless $Multi; add(); @@ -261,10 +278,11 @@ sub slurp() { # If FILE does not have a trailing newline, be sure to add it # before appending anything else. - if (@Lines and $Lines[$#Lines] !~ m/\n$/s) { $Lines[$#Lines] .= "\n"; } + + return scalar @Lines; } sub add() { @@ -304,7 +322,9 @@ sub add() { push(@Lines, $StdinContent); } } - return scalar(@Lines); # whatever + # add the number of lines in added content to safety counter + $LinesCount += ($StdinContent =~ tr/\n//); + return $LinesCount; # whatever true } sub del() { @@ -320,9 +340,12 @@ sub del() { my (@filtered); foreach (@Lines) { - push (@filtered, $_) - unless (/^$mybegin(?:$mytrailer)?$/o .. /^$myend(?:$mytrailer)?$/o); - + if (/^$mybegin(?:$mytrailer)?$/o .. /^$myend(?:$mytrailer)?$/o) { + # additional safety counter for double-check + -- $LinesCount; + } else { + push (@filtered, $_); + } # for safety check: $bm_found = 1 if (/^$mybegin(?:$mytrailer)?$/o); $em_found = 1 if (/^$myend(?:$mytrailer)?$/o); @@ -335,7 +358,54 @@ sub del() { # safety exit die "$ProgramName: no end-mark after begin-mark!\n"; } - return scalar(@Lines); # whatever + return $LinesCount; # whatever +} + +sub change() { + my ($mytrailer, $mybegin, $myend) = + ($Trailer, $MarkBegin, $MarkEnd); + + my ($bm_found, $em_found); # begin/end mark found indicator + + # Make the strings regexp-friendly by quoting non-word chars. + $mybegin =~ s/\W/\\$&/g; + $myend =~ s/\W/\\$&/g; + $mytrailer =~ s/\W/\\$&/g; + + my (@filtered, $done, $skip); + $done = 0; # job done once + $skip = 0; # skip original block + foreach (@Lines) { + if (! $done and $skip > 0) { + -- $LinesCount; + if (/^$myend(?:$mytrailer)?$/o) { + ++ $done; # skipped all that was to skip + } else { + ++ $skip; # count lines being skipped + } + } + elsif (0 == $skip and $_ =~ /^$mybegin(?:$mytrailer)?$/o) { + push (@filtered, $StdinContent); + $LinesCount += ($StdinContent =~ tr/\n//); + $skip = 1; + } + else { + push (@filtered, $_) + } + # for safety check: + $bm_found = 1 if (/^$mybegin(?:$mytrailer)?$/o); + $em_found = 1 if (/^$myend(?:$mytrailer)?$/o); + } + if ($bm_found and $em_found) { + -- $skip; # correct the counter + DEBUG and print STDERR "Replaced block of $skip lines\n"; + @Lines = @filtered; + } + elsif ($bm_found and ! $em_found) { + # safety exit + die "$ProgramName: no end-mark after begin-mark!\n"; + } + return $done; } # written by ddzeko@srce.hr, 2005-03-18 @@ -345,7 +415,7 @@ sub actualize() { # put it all thogether my $newContent = join('', @Lines); - unless (length($newContent)) { + unless (length($newContent) or 0 == $LinesCount) { # safety exit in last second :) die "$ProgramName: New content empty -- aborting file alteration!\n"; } @@ -368,10 +438,13 @@ sub actualize() { sysseek(*$FileHandle, 0, SEEK_SET) or die "$ProgramName: Failed to seek to the begining of file ($!)\n"; } - my $wb = syswrite($FileHandle, $newContent); - if (! $wb or length($newContent) != $wb) { - # FIXME: try restoring backup copy - die "$ProgramName: Failed to write the content to '$File' ($!)\n"; + if (length($newContent)) { + my $wb = syswrite($FileHandle, $newContent); + if (! $wb or length($newContent) != $wb) { + # FIXME: try restoring backup copy + my $ncl = length($newContent); + die "$ProgramName: Failed to write the content to '$File' (wb=$wb, len=$ncl, err:$!)\n"; + } } if ($NoClose) { # this could be handy for files that had stuff appended @@ -386,12 +459,15 @@ sub actualize() { $file_new .= '.cp-update.new'; # our .new file $file_old .= '.cp-update.old'; # our .old file # write content in new file in single write op - sysopen ($FileHandle, $file_new, O_CREAT|O_TRUNC|O_WRONLY) + sysopen ($FileHandle, $file_new, O_CREAT|O_TRUNC|O_WRONLY, (stat($File))[2]) or die "$ProgramName: Failed to open file '$File' for writing ($!)\n"; - my $wb = syswrite($FileHandle, $newContent); - if (! $wb or length($newContent) != $wb) { - unlink($file_new); - die "$ProgramName: Failed to write the content to '$File' ($!)\n"; + if (length($newContent)) { + my $wb = syswrite($FileHandle, $newContent); + if (! $wb or length($newContent) != $wb) { + unlink($file_new); + my ($ncl) = length($newContent); + die "$ProgramName: Failed to write the content to '$File' (wb=$wb, len=$ncl, err:$!)\n"; + } } close($FileHandle); # do the moving (should be atomic) @@ -416,6 +492,7 @@ sub actualize() { unlink($file_old) or warn "$ProgramName: Failed to remove file '$file_old' ($!)\n"; } + DEBUG and print STDERR "actualize: LinesCount=$LinesCount\n"; } # return content from standard input