-my ($name, $usage);
-
-($name = $0) =~ s%.*/%%; # Looks nicer without the slashes
-$usage = "Usage:\n";
-$usage .= "$name [-r] [-m] [-i AFTER|-t] [-b START -e STOP] [-c CHAR] PACKAGE FILE\n";
-$usage .= " -r Remove entry PACKAGE from FILE\n";
-$usage .= " Default is to add lines from stdin.\n";
-$usage .= " -m Allow multiple blocks of the same type\n";
-$usage .= " By default, old blocks are replaced with the new one.\n";
-$usage .= "\n";
-$usage .= " Special placement of config blocks:\n";
-$usage .= " -i Insert after this line\n";
-$usage .= " -t Insert on top\n";
-$usage .= " The default is to append at the bottom.\n";
-$usage .= "\n";
-$usage .= " Manipulating block marks:\n";
-$usage .= " -c Use alternative comment character (default: #)\n";
-$usage .= " Alternative block marks:\n";
-$usage .= " -b Start mark (ie 'service ftp')\n";
-$usage .= " -e Stop mark (ie. '}')\n";
-$usage .= " These will delete to the end of the file if no end mark\n";
-$usage .= " is found. The deletion is otherwise not greedy and\n";
-$usage .= " stops at the first end mark found. You can use\n";
-$usage .= " %P to insert the PACKAGE argument, and\n";
-$usage .= " %% to insert a literal % sign into the mark.\n";
-
-
-
-
-my ($begin, $end, $trailer);
-my ($parambegin, $paramend, $insert);
-my ($pkg, $file);
-my ($block, $multi, $addafter, $addontop);
-
-my $add = 1;
-my $comment = "#";
-my $SEEK_END = 2;
-
-while (@ARGV)
-{
- $_ = shift;
- if (/^-c$/ || /^--comment$/)
- {
- defined ($comment = shift)
- || die "$name: `-c' must be followed by an argument\n";
- }
- elsif (/^-r$/ || /^--remove$/)
- {
- $add = 0;
- }
- elsif (/^-m$/ || /^--allow-multiple$/)
- {
- $multi = 1;
- }
- elsif (/^-b$/ || /^--begin$/)
- {
- defined ($parambegin = shift)
- || die "$name: '-b' must be followed by an argument\n";
- $block = 1;
- }
- elsif (/^-e$/ || /^--end$/)
- {
- defined ($paramend = shift)
- || die "$name: '-e' must be followed by an argument\n";
- $block = 1;
- }
- elsif (/^-i$/ || /^--insert-after$/)
- {
- defined($insert = shift)
- || die "$name: '-i' must be followed by an anrgument\n";
- #$multi = 1; # uncomment for -i backward compatibility
- $addafter = 1;
- }
- elsif (/^-t$/ || /^--insert-on-top$/)
- {
- $insert = "";
- $addafter = 1;
- }
- elsif (/^-/)
- {
- die "$name: Unrecognized option \`$_'\n";
- }
- else
- {
- unshift(@ARGV, $_);
- last;
- }
+sub DEBUG () { 0 };
+
+# coding convention: CamelCase vars are global here
+#
+my ($ProgramName, $UsageLong, $UsageShort, $VERSION);
+
+$VERSION = '2.1';
+
+# Looks nicer without the slashes and dots
+($ProgramName = $0) =~ s!.*/!!; # strip dir
+$ProgramName =~ s!\.[^.]+$!!; # strip last ext
+
+# 34567890 34567890 34567890 34567890 34567890 34567890 34567890 345678
+
+$UsageLong = "
+ $ProgramName -- versatile line-based file updating tool
+ usually used by package configuration scripts
+
+ Usage: $ProgramName [options] PACKAGE FILE < stdin-content
+
+
+ - General options:
+
+ -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:
+
+ -t | --insert-on-top Insert stdin-content block on top.
+ The default is to add it at the bottom.
+
+ -i | --insert-after x Insert after this/matching line.
+ -f | --insert-before x Insert before this/matching line.
+
+ - Manipulating block marks:
+
+ -c | --comment x Use alternative comment char/string.
+ The default is shell-style \#-sign.
+
+ --comment-end x Use this marker for comment ending.
+ The default is none. Ie. '-->', '*/'.
+
+ -b | --begin-mark x Block starting mark (ie. 'service ftp')
+ -e | --end-mark x Block ending mark (ie. '}')
+
+ These will delete to the end of the file if no end mark
+ is found. The deletion is otherwise not greedy and stops
+ at the first end mark found. You can use \%P to insert
+ the PACKAGE argument, and \%\% to insert a literal \% sign
+ into the mark.
+
+ - File handling options:
+
+ -p | --in-place Try to preserve original inode.
+ -n | --no-close Do not close and reopen file when
+ editing it in place.
+
+ Options marked with 'x' take single argument. Others do not.
+
+\n";
+
+$UsageShort = "$ProgramName -- versatile line-based file updating tool\n";
+$UsageShort .= " Options: [-r] [-m] [-i AFTER|-f BEFORE|-t] [-b START -e STOP] [-c CHAR] PACKAGE FILE\n";
+$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, $LinesCount);
+
+# Placement modes
+use constant APPEND_AT_END => 0;
+use constant INSERT_BEFORE => 1;
+use constant INSERT_AFTER => 2;
+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;
+$Placement = APPEND_AT_END;
+$RegexpMatch = 1;
+$Comment = '#';
+$CommentEnd = '';
+$MatchLine = '';
+$InPlace = 0;
+$NoClose = 0;
+
+while (@ARGV) {
+ $_ = shift;
+ if (/^-c$/ || /^--comment$/) {
+ defined ($Comment = shift)
+ || die "$ProgramName: `-c|--comment' must be followed by an argument\n";
+ }
+ elsif (/^--comment-end$/) {
+ defined ($CommentEnd = shift)
+ || die "$ProgramName: `--comment-end' must be followed by an argument\n";
+ }
+ elsif (/^-r$/ || /^--remove$/) {
+ $InsertRemove = DO_REMOVE;
+ }
+ elsif (/^-x$/ || /^--change$/) {
+ $InsertRemove = DO_CHANGE;
+ }
+ elsif (/^-m$/ || /^--allow-multi(?:ple)?$/) {
+ $Multi = 1;
+ }
+ elsif (/^-b$/ || /^--begin(?:-mark)?$/) {
+ defined ($ParamBegin = shift)
+ || die "$ProgramName: '-b|--begin-mark' must be followed by an argument\n";
+ $Block = 1;
+ }
+ elsif (/^-e$/ || /^--end(?:-mark)?$/) {
+ defined ($ParamEnd = shift)
+ || die "$ProgramName: '-e|--end-mark' must be followed by an argument\n";
+ $Block = 1;
+ }
+ elsif (/^-i$/ || /^--insert-after$/) {
+ defined($MatchLine = shift)
+ || die "$ProgramName: '-i|--insert-after' must be followed by an argument\n";
+ $Placement = INSERT_AFTER;
+ }
+ elsif (/^-f$/ || /^--insert-before$/) {
+ defined($MatchLine = shift)
+ || die "$ProgramName: '-f|--insert-before' must be followed by an argument\n";
+ $Placement = INSERT_BEFORE;
+ }
+ elsif (/^-t$/ || /^--insert-on-top$/) {
+ $Placement = INSERT_ON_TOP;
+ }
+ elsif (/^-R$/ || /^--regexp(?:-match|-mode)?$/) {
+ $RegexpMatch = 1; # it's the default
+ }
+ elsif (/^-h$/ || /^--help$/) {
+ die $UsageLong;
+ }
+ elsif (/^-p$/ || /^--in-place$/) {
+ $InPlace = 1;
+ }
+ elsif (/^-n$/ || /^--no-close$/) {
+ $NoClose = 1;
+ }
+ elsif (/^--version$/) {
+ die "$ProgramName (CARNet Packaging file update) $VERSION\n"
+ . "Copyright (C) 1998-2005 Free Software Foundation, Inc.\n"
+ . "This is free software; see the source for copying conditions. There is NO\n"
+ . "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n";
+ }
+ elsif (/^-/) {
+ die "$ProgramName: Unrecognized option \`$_'\n";
+ }
+ else {
+ unshift(@ARGV, $_);
+ last;
+ }
+
+ if ($Multi and DO_CHANGE == $InsertRemove) {
+ die "$ProgramName: Cannot use both `--change' and `--allow-multiple'\n";
+ }
+}
+
+$Package = shift or die $UsageShort;
+$File = shift or die $UsageShort;
+
+# prepare block begin and end marks
+#
+(! $Block || ($ParamBegin && $ParamEnd))
+ or die ("$ProgramName: must provide both begin and end marks.\n");
+#
+if ($Block) {
+ $ParamBegin =~ s, %P , $Package ,gx;
+ $ParamBegin =~ s, %% , % ,gx;
+ $ParamEnd =~ s, %P , $Package ,gx;
+ $ParamEnd =~ s, %% , % ,gx;
+ $MarkBegin = $ParamBegin;
+ $MarkEnd = $ParamEnd;
+ $Trailer = '';
+} else {
+ $MarkBegin = "$Comment Begin update by CARNet package $Package";
+ $MarkEnd = "$Comment End update by CARNet package $Package";
+ $Trailer = " -- DO NOT DELETE THIS LINE!".$CommentEnd;