: # use perl
    eval 'exec perl -S $0 "$@"'
	if $runnning_under_some_shell;

# $Id: replace,v 1.5 2000/02/07 10:58:15 nakahiro Exp $

require 5.001;

$TMPDIR = '/var/tmp';
( $PROGRAM = $0 ) =~ s!^.*/!!o;
$USAGE = <<'EOM';
Usage: $PROGRAM src dest [Options]...

    replace for hns: Replace specified string in file(s).

    src .... A string you want to replace.
    dest ... A string that the strings are replaced with.
              To delete the src string, specify null string like "".

    Options:
      -h .......... Show this help.
      -i .......... Ignore upper/lower case during string comparisons.
      -d dir. ..... Target directory. '.' is in default.
      -r .......... Recursively descend through directories.
      -p pattern .. Matching pattern of target files.
                     e.g. -p '\.html$', -p '^index\.html$|^99*'
      -n .......... Replace without creating backup file.
      -x postfix .. Postfix of backup file. Default is 'rp_bak'.
      -R .......... Parse 'src' as regexp string.
EOM

$| = 1;

MAIN:
{
    # parse command line.
    $SRC_STRING = shift;
    $DEST_STRING = shift;
    my( @optAllowed ) = ( 'h', 'd', 'p', 'i', 'r', 'n', 'x', 'R' );
    my( @optRequired ) = ();	# no required option
    my( %opts );
    &getOpt( \@ARGV, \@optAllowed, \@optRequired, \%opts );

    my( $dir ) = $opts{'d'} || '.';
    $TARGET_FILES = $opts{'p'};

    $TARGET_FILES = '\.hnf$' if $TARGET_FILES eq "";

    my( $ignoreCase ) = defined( $opts{'i'} )? 1 : 0;
    my( $recursive ) = defined( $opts{'r'} )? 1 : 0;
    $DO_BACKUP = defined( $opts{'n'} )? 0 : 1;
    $BK_POSTFIX = $opts{'x'} || 'rp_bak';
    $BK_POSTFIX = ".$BK_POSTFIX" if ( $BK_POSTFIX !~ /^\./ );
    $SRC_STRING = quotemeta( $SRC_STRING ) unless defined( $opts{'R'} );
    if ( !defined( $SRC_STRING ) or !defined( $DEST_STRING ) or
	defined( $opts{'h'} ) or !$dir )
    {
	&myMsg( $USAGE );
	exit;
    }

    &myMsg( 'Replace started.' );
    my( $do, $cand ) = &traverse( $dir, $recursive, $ignoreCase );
    &myMsg( 'Replace finished.' );

    &myMsg( "Replaced $do file(s) of $cand file(s)." );
}

exit(0);

sub traverse
{
    local( $dir, $recursive, $ignoreCase ) = @_;
    local( $do, $cand ) = ( 0, 0 );

    opendir( DIR, $dir ) or
	&myMsg( "ERROR - Cannot opendir, '$dir'." ), return 0;

    # 'readdir' below is in list context.
    # So, all entries are read at once.
    # You need NOT wonder, about backup files created while loop.
    foreach ( readdir DIR )
    {
	if ( /^\.\.*$/ or ( -l "$dir/$_" ))
	{
	    next;
	}
	elsif ( -f "$dir/$_" )
	{
	    next if ( $TARGET_FILES and !/$TARGET_FILES/o );
	    my( $nDo, $nCand ) = &doReplace( "$dir/$_", $ignoreCase );
	    $do += $nDo;
	    $cand += $nCand;
	}
	elsif (( -d "$dir/$_" ) and $recursive )
	{
	    my( $nDo, $nCand ) = &traverse( "$dir/$_", $recursive,
		$ignoreCase );
	    $do += $nDo;
	    $cand += $nCand;
	}
    }
    closedir DIR;

    ( $do, $cand );
}

sub doReplace
{
    my( $file, $ignoreCase ) = @_;

    my( $mode, $acTime, $modTime ) = ( stat( $file ))[ 2, 8, 9 ];

    my( $tmpFile ) = "$TMPDIR/$PROGRAM.$$";
    my( $f_rep ) = 0;

    open( SRCFILE, "<$file" ) or
	&myMsg( "ERROR - Open file '$file' failed." ), return( 0, 0 );
    open( DESTFILE, ">$tmpFile" ) or
	&myMsg( "ERROR - Create file '$tmpFile' failed." ), return( 0, 0 );

    if ( $ignoreCase )
    {
        while ( <SRCFILE> )
        {
	    if (/$SRC_STRING/i) {
		print "\n- $_"; 
		s/$SRC_STRING/$DEST_STRING/gio and $f_rep = 1;
		print "+ $_";	
	    }
	    print DESTFILE;
        }
    }
    else
    {
        while ( <SRCFILE> )
        {
	    if (/$SRC_STRING/) {
		print "\n- $_";
		s/$SRC_STRING/$DEST_STRING/go and $f_rep = 1;
		print "+ $_";
	    }
	    print DESTFILE;
        }
    }

    close DESTFILE;
    close SRCFILE;

    if ( $f_rep == 0 )
    {
	unlink $tmpFile or &myMsg( "WARN - Unlink '$tmpFile' failed." );
	return( 0, 0 );
    }

    # DO REPLACE!

    my( $backupFile );
    if ( $DO_BACKUP )
    {
        $backupFile = "$file$BK_POSTFIX";
        if ( -f $backupFile )
        {
	    my( $gen ) = 1;
	    while ( -f "$backupFile.$gen" ) { $gen++; }
	    $backupFile .= ".$gen";
        }

        ( system( "mv $file $backupFile" ) == 0 ) or
	    &myMsg( "ERROR - Do mv '$file' to '$backupFile' failed." ),
	    return( 0, 1 );
    }

    ( system( "cp -p $tmpFile $file" ) == 0 ) or
	&myMsg( "ERROR - Copy '$tmpFile' to '$file' failed." ), return( 0, 1 );

    utime( $acTime, $modTime, $file ) or
	&myMsg( "ERROR - Set utime of '$file' failed." ), return( 0, 1 );

    chmod(( $mode & 0777 ), $file ) or
	&myMsg( "ERROR - Set mode of '$file' failed." ), return( 0, 1 );

    unlink $tmpFile or &myMsg( "WARN - Unlink '$tmpFile' failed." );

    if ( $DO_BACKUP )
    {
        &myMsg( "Replaced '$file'. Original version is kept as '$backupFile'." );
    }
    else
    {
        &myMsg( "Replaced '$file'." );
    }

    ( 1, 1 );
}

sub myDie
{
    my( $msg ) = shift;
    die "[Msg from $PROGRAM] $msg";
}

sub myMsg
{
    my( $msg ) = shift;
    print "[Msg from $PROGRAM] $msg\n";
}

sub getOpt
{
    my( $pWords, $pAllowed, $pRequired, $pOpts ) = @_;

    my( $word );
    my( @required ) = @$pRequired;
    while ( defined( $word = shift( @$pWords )))
    {
	my( $opt );
	if (( $opt ) = ( $word =~ /^-(.*)$/o ))
	{
	    if ( grep( /^${opt}$/, @$pAllowed ))
	    {
	        my( $nextOpt ) = shift( @$pWords );
		if ( !$nextOpt )
		{
		    $$pOpts{$opt} = '';
		}
		elsif ( $nextOpt !~ /^-.*$/o )
	        {
	    	    $$pOpts{$opt} = $nextOpt;
		    @required = grep( !/^${opt}$/, @required );
	        }
		else
		{
		    $$pOpts{$opt} = '';
		    unshift( @$pWords, $nextOpt );
		}
	    }
	    else
	    {
		&myMsg( $USAGE );
		die;
	    }
	}
	else
	{
	    &myMsg( $USAGE );
	    die;
	}
    }

    if ( @required )
    {
	&myMsg( $USAGE );
	die;
    }
}


# replace: Replace specified string in file(s).
# Copyright (C) 1999 NAKAMURA Hiroshi.
#
# 2000/2/7 Patched by Kenji Suzuki <kenji@h14m.org>
#
# 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
# 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 PRATICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
