head 1.14; access; symbols V1:1.1.1.1 HEIM-D:1.1.1; locks mah:1.1; strict; comment @# @; 1.14 date 2005.05.03.19.19.38; author mah; state Exp; branches; next 1.13; 1.13 date 2005.05.02.23.07.39; author mah; state Exp; branches; next 1.12; 1.12 date 2005.05.02.21.02.17; author mah; state Exp; branches; next 1.11; 1.11 date 2005.05.02.20.52.19; author mah; state Exp; branches; next 1.10; 1.10 date 2003.09.27.20.01.51; author mah; state Exp; branches; next 1.9; 1.9 date 2003.02.06.11.19.28; author mah; state Exp; branches; next 1.8; 1.8 date 2001.05.02.16.16.43; author mah; state Exp; branches; next 1.7; 1.7 date 2001.04.30.13.57.32; author mah; state Exp; branches; next 1.6; 1.6 date 2001.04.30.13.06.17; author mah; state Exp; branches; next 1.5; 1.5 date 2001.01.15.11.35.17; author mah; state Exp; branches; next 1.4; 1.4 date 2000.04.23.17.38.05; author mah; state Exp; branches; next 1.3; 1.3 date 2000.03.14.13.52.20; author mah; state Exp; branches; next 1.2; 1.2 date 2000.03.14.11.28.56; author mah; state Exp; branches; next 1.1; 1.1 date 2000.03.13.20.58.03; author mah; state Exp; branches 1.1.1.1; next ; 1.1.1.1 date 2000.03.13.20.58.03; author mah; state Exp; branches; next ; desc @@ 1.14 log @added bug section @ text @#!/usr/bin/perl # TODO option to read file names from STDIN # TODO options to control generation of backups # TODO -S, --suffix=SUFFIX override the usual backup suffix ? # TODO guard against mis-escaped lines use File::Temp qw(tempfile); use Getopt::Long; use Pod::Usage; GetOptions(\%options, qw( C|context=i editor=s help|h|? )) or pod2usage(2); pod2usage(1) if $options{help}; $options{C} ||= 2; $options{editor} ||= $ENV{EDITOR} || 'vim'; pod2usage "?Argument to --context must not be negative." if $options{C} < 0; $re = shift @@ARGV or pod2usage "?Missing regular expression."; $re = qr/$re/o; # TODO right thing? @@ARGV = grep { !m/\n/ or warn "!ignoring file `$_' with newline(s) in its name.\n" and 0 } @@ARGV; pod2usage "?Missing file names." unless @@ARGV; ($fh,$edits) = tempfile( 'matcheditXXXXX', DIR => File::Spec->tmpdir ); sub process_hit { # $HIT[0] is line $first # $HIT[$lasthit-$first] is line $lasthit # $HIT[$lasthit-$first+$options{C}$ is line $lasthit+$options{C} # we have to print the lines from $first to ($lasthit+$options{C}), possibly less if at end of file my $lastidx = $lasthit-$first+$options{C}; $anyhit = 1; $lastidx = $#HIT if $lastidx > $#HIT; my $last = $first+$lastidx; print $fh "LINES $first-$last\n", splice( @@HIT,0,$lastidx+1 ), "LINESEND\n"; $lasthit = undef; # $first is updated in main program... } while (<>) { if ($.==1) { process_hit if $lasthit; print $fh "FILEEND\n" if $fileheader; @@HIT = (); $fileheader = 0; $first = 1; # first line collected in @@HIT will have number 1 } push @@HIT, $_; # collect if( /$re/ ) { $lasthit = $.; unless( $fileheader ) { print $fh "FILE $ARGV\n"; $fileheader = 1; }; } if( $lasthit ) { # running hit if( $.-$lasthit > 2*$options{C} ) { # we now have 2*$options{C}+1 intervening lines between last hit and current line process_hit; shift @@HIT; # throw away one more, then we still have $options{C} lines of leading context $first = $.-$options{C}+1; } } elsif( @@HIT > $options{C} ) { # throw one line away shift @@HIT; $first++; } } continue { close ARGV if eof; # reset $. } process_hit if $lasthit; print $fh "FILEEND\n" if $fileheader; unless( $anyhit ) { close $fh; unlink $edits; die "!No matching file.\n"; } system $options{editor}, $edits; # TODO check time stamp if there was any modification at all print "Answer YES if you want the changes to take effect: "; $choice = ; if( $choice eq "YES\n" ) { seek $fh, 0, SEEK_SET; # start over while( <$fh> ) { last unless m/^FILE (.*)/; $fn = $1; system( "mv $fn $fn.bak" ); open( PROCIF, "<$fn.bak" ); open( OF, ">$fn" ); while( <$fh> ) { last unless m/^LINES (\d+)-(\d+)/; $from = $1; $to = $2; tell PROCIF; # to initialize $. while( $. < $from - 1 ) { # copy part before print OF scalar ; } while( $. < $to ) { # dummy read the part ; } while( <$fh> ) { # paste in new lines last if /^LINESEND/; print OF $_; } last if /^FILEEND/; } # must have read FILEEND on correct file structure print OF ; # copy rest of file close PROCIF; } } close $fh; unlink $edits; __END__ =head1 NAME matchedit - edit regions of files that match a (Perl) regular expression in a single session =head1 SYNOPSIS B [B<--context> I] [B<--editor> I] I I... =head1 DESCRIPTION B allows you to edit the regions of the specified files that match the (Perl) regular expression I. =head1 OPTIONS =over 4 =item B<--context> I, B<-C> I Print I lines of context before and after each match. =item B<--editor> I Use I to edit the matches. If this option is not given the editor defaults to the one specified with environment variable I or vim. =back =head1 BUGS AND CAVEATS The markup of the file holding the matches is neither nice nor fool-proof. This means that your edited section may contain markup that confuses this script. This is not guarded against (yet), but for each changed file the script generates a backup copy, so in case of unexpected trouble you may recover. =head1 COPYRIGHT (C) 2000-2005 Mark Hillebrand . This code is released under the BSD License. Before using this software, visit L for the full license text. @ 1.13 log @does not depend on external egrep anymore, takes Perl regexp as argument @ text @d120 1 d123 1 a123 1 matchedit - edit regions of files that match a (Perl) regular expression d152 7 @ 1.12 log @mostly cosmetic clean-up @ text @d3 3 a5 1 # TODO perlify it - no more egrep dependencies... d15 3 a17 1 $re = quotemeta shift @@ARGV or pod2usage "?Missing regular expression."; a18 2 # possibly: @@ARGV = glob("*") unless @@ARGV; # possibly: @@ARGV = grep { -f && -T } @@ARGV; d20 31 a50 19 $FILE = ""; open( IF, "egrep -C $options{C} -d skip -n -Z $re @@ARGV /|" ); # the '/' at the end forces my grep to prefix filenames even on single file input # the '-d skip' causes egrep to silently ignore the directory # the -Z option causes filenames to be terminated by \0 which simplifies parsing die "?Could not match anything." if eof( IF ); $edits = ".matchedit-$$"; open( OF, ">$edits" ); while( ) { last unless m/^(.*?)\0(\d+?)[:-](.*)/; $fn = $1; $from = $to = $2; $line = "$3\n"; if( $FILE eq "" ) { print OF "FILE $fn\n" } else { print OF "FILEEND\nFILE $fn\n" unless $fn eq $FILE; d52 12 a63 6 $FILE = $fn; while( ) { last if /^--/; $to++; m/^.*?\0\d+[:-](.*)/; $line .= "$1\n"; d65 11 a75 1 print OF "LINES $from-$to\n${line}LINESEND\n"; a76 2 print OF "FILEEND\n" unless $FILE eq ""; close IF; close OF; d80 1 d85 2 a86 2 open( IF, "<$edits" ); while( ) { d89 2 a90 2 system( "mv $fn ${fn}.bak" ); open( PROCIF, "<${fn}.bak" ); d92 1 a92 1 while( ) { d103 1 a103 1 while( ) { # paste in new lines d116 1 d122 1 a122 1 matchedit - edit regions of files that match a regular expression d134 1 a134 1 B allows you to edit the regions of the specified files that match the regular expression I. @ 1.11 log @The usual brush-up: more POD, Getopt::Long, Pod::Usage @ text @d3 1 d15 2 d24 4 a27 2 die "Could not match anything" if eof( IF ); open( OF, "> .matchedit-$$" ); d50 1 a50 3 my $EDITOR = $ENV{EDITOR} || "vi"; system( "$EDITOR .matchedit-$$" ); d56 1 a56 1 open( IF, "<.matchedit-$$" ); d69 10 a78 1 print OF scalar ; d80 4 a83 2 while( $. < $to ) { # dummy read the part ; a84 3 while( ) { # paste in new lines last if /^LINESEND/; print OF $_; a85 4 last if /^FILEEND/; } # must have read FILEEND on correct file structure print OF ; # copy rest of file d87 1 a87 3 close PROCIF; } } a88 1 system( "rm .matchedit-$$" ); @ 1.10 log @todo @ text @d2 12 a13 53 =head1 NAME matchedit - edit regions of files which match a regular expression =head1 SYNOPSIS B [-I] I I... =head1 DESCRIPTION B lets you edit all the regions of the specified files which match the regular expression I simultaenously. =head1 LOG $Id: matchedit,v 1.9 2003/02/06 11:19:28 mah Exp $ 20000313 mah first running version 20000314 mah use egrep instead of grep 20000314 mah program exit on no match 20000314 mah cmd line option for size of context presented 20010115 mah added perldoc 20030206 mah editor in $EDITOR TODO customize bracket structure TODO quoting of file names in grep call required TODO switch to a diff format and use patch to merge =head1 AUTHOR Mark A. Hillebrand =head1 LEGAL THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. =cut $context = ($ARGV[0] =~ /^-\d$/ ? shift @@ARGV : "-2" ); $re = quotemeta( shift @@ARGV ); d16 1 a16 1 open( IF, "egrep $context -d skip -n -Z $re @@ARGV /|" ); d85 38 @ 1.9 log @hmmm @ text @d20 1 a20 1 $Id: matchedit,v 1.8 2001/05/02 16:16:43 mah Exp $ d30 1 @ 1.8 log @some tidying up @ text @d20 1 a20 1 $Id: matchedit,v 1.7 2001/04/30 13:57:32 mah Exp $ d27 1 a27 1 TODO editor in $EDITOR d85 3 a87 1 system( "vim .matchedit-$$" ); @ 1.7 log @indentation @ text @a1 1 # cat-bits: editingtool d5 1 a5 1 matchedit.pl - edit regions of files which match a regular expression d9 1 a9 1 B d16 1 a16 1 B lets you edit all the regions of the specified files which match the regular expression I simultaenously. d20 1 a20 1 $Id: matchedit.pl,v 1.5 2001/01/15 11:35:17 mah Exp $ @ 1.6 log @removed some crap @ text @d52 1 a52 1 $context = ($ARGV[0] =~ /^-\d$/ ? shift : "-2" ); d54 1 a54 1 $re = quotemeta( shift ); d65 17 a81 17 last unless m/^(.*?)\0(\d+?)[:-](.*)/; $fn = $1; $from = $to = $2; $line = "$3\n"; if( $FILE eq "" ) { print OF "FILE $fn\n" } else { print OF "FILEEND\nFILE $fn\n" unless $fn eq $FILE; } $FILE = $fn; while( ) { last if /^--/; $to++; m/^.*?\0\d+[:-](.*)/; $line .= "$1\n"; } print OF "LINES $from-$to\n${line}LINESEND\n"; d92 19 a110 19 open( IF, "<.matchedit-$$" ); while( ) { last unless m/^FILE (.*)/; $fn = $1; system( "mv $fn ${fn}.bak" ); open( PROCIF, "<${fn}.bak" ); open( OF, ">$fn" ); while( ) { last unless m/^LINES (\d+)-(\d+)/; $from = $1; $to = $2; tell PROCIF; # to initialize $. while( $. < $from - 1 ) { # copy part before print OF scalar ; } while( $. < $to ) { # dummy read the part ; } while( ) { # paste in new lines d113 8 a120 8 } last if /^FILEEND/; } # must have read FILEEND on correct file structure print OF ; # copy rest of file close PROCIF; } @ 1.5 log @error fix, documentation update @ text @d2 1 d21 1 a21 1 $Id: matchedit.pl,v 1.4 2000/04/23 17:38:05 mah Exp $ d52 1 a52 1 $context = ($ARGV[0] =~ /^-\d$/ ? shift @@ARGV : "-2" ); d54 1 a54 1 $re = quotemeta( shift @@ARGV ); @ 1.4 log @added my old deep-thought $CVSROOT backup script. will be upgraded soon @ text @d2 48 a49 35 ############################################################################## # $Id: matchedit.pl,v 1.3 2000/03/14 13:52:20 mah Exp $ # TODO editor in $EDITOR # TODO customize bracket structure # TODO quoting of file names in grep call required # # 20000313 mah first running version # 20000314 mah use egrep instead of grep # 20000314 mah program exit on no match # 20000314 mah cmd line option for size of context presented ############################################################################## # Match Edit # # invocation: # matchedit.pl [-CNTX] RE FILES # # description: # allows editing of all contexts simultaneously in the FILES # matching the grep regular expression RE. Will make a # backup for every file edited. # The context size in lines before and ahead of the # matching line is defined by CNTX. It defaults to 2. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # ############################################################################## d56 5 a60 1 open( IF, "egrep $context -n $re @@ARGV /|" ); # '/' forces my grep to prefix filenames even on single file input d64 1 a64 1 last unless m/^(.*?)[:-](\d+?)[:-](.*)/; d77 1 a77 1 m/^.*?[:-]\d+[:-](.*)/; @ 1.3 log @new cmdlin option, some details @ text @d3 1 a3 1 # $Id: matchedit.pl,v 1.2 2000/03/14 11:28:56 mah Exp $ d87 1 a87 2 $JUNK = ; print OF $JUNK; @ 1.2 log @use egrep instead of grep @ text @d3 4 a6 5 # $Id: matchedit.pl,v 1.1.1.1 2000/03/13 20:58:03 mah Exp $ # TODO: cmd line option for size of context presented # TODO: editor in $EDITOR # TODO: customize bracket structure # TODO: quoting of file names in grep call required d10 2 d16 1 a16 1 # matchedit.pl RE FILES d22 2 d38 3 a40 1 $re = shift @@ARGV; d43 2 a44 1 open( IF, "egrep -2 -n $re @@ARGV /|" ); # / forces my grep version to always prefix filenames @ 1.1 log @Initial revision @ text @d3 1 a3 1 # $Id:$ d10 1 d38 1 a38 1 open( IF, "grep -2 -n $re @@ARGV /|" ); # / forces my grep version to always prefix filenames @ 1.1.1.1 log @initial import @ text @@