Alt-App-makepatch

 view release on metacpan or  search on metacpan

script/applypatch  view on Meta::CPAN

#!/usr/bin/perl -w
# applypatch -- apply a 'makepatch' generated patch kit.
# Author          : Johan Vromans
# Created On      : Sat Nov 14 14:34:28 1998
# Last Modified By: Johan Vromans
# Last Modified On: Fri Oct 26 21:52:01 2012
# Update Count    : 149
# Status          : Released

use strict;
use Getopt::Long 2.00;
use File::Basename;
use File::Spec;
use IO::File;
use Text::ParseWords;

################ Common stuff ################

my $my_package = 'Sciurix';
my $my_name    = "applypatch";
my $my_version = "2.05";
my $data_version = '1.0';

################ Globals ################

## Options and defaults.

my $dir;			# source directory
my $check = 0;			# check only
my $retain = 0;			# retain .orig files
my $patch = 'patch -p0 -N';	# patch command
my $verbose = 0;		# verbose processing
my $force = 0;			# allow continuation after trunc/corruption

# Development options (not shown with -help).
my $trace = 0;			# trace (show process)
my $test = 0;			# test (no actual processing)
my $debug = 0;			# extensive debugging info

## Misc

my $applypatch = 0;		# it's for us
my $timestamp;			# create date/time of patch kit
my @workq = ();			# work queue

## Subroutine prototypes

sub app_options ();
sub app_usage ($);
sub copy_input ();
sub execute_patch ();
sub post_patch ();
sub pre_patch ();
sub verify_files ();

################ Program parameters ################

app_options();
$trace ||= $debug;
$verbose ||= $trace;

################ Presets ################

$patch .= " -s" unless $verbose;
my $tmpfile = IO::File->new_tmpfile;

################ The Process ################

# Validate input and copy to temp file.
copy_input ();

# Change dir if requested.
(defined $dir) && (chdir ($dir) || die ("Cannot change to $dir: $!\n"));

# Verify that we are in the right place.
verify_files ();

# Exit if just checking.
die ("Okay\n") if $test && $check;
exit (0) if $check;

# Pre patch: create directories and files.
pre_patch ();

# Run the patch program.
execute_patch ();

# Post patch: adjust timestamps, remove obsolete files and directories.
post_patch ();

die ("Okay\n") if $test;
exit (0);

################ Subroutines ################

sub copy_input () {

    my $lines = 0;		# checksum: #lines
    my $bytes = 0;		# checksum: #bytes
    my $sum = 0;		# checksum: system V sum
    my $all_lines = 0;		# overall checksum: #lines
    my $all_bytes = 0;		# overall checksum: #bytes
    my $all_sum = 0;		# overall checksum: system V sum
    my $patchdata = 0;		# saw patch data
    my $pos = 0;		# start of patch data
    my $endkit = 0;		# saw end of kit
    my $fail = 0;		# failed
    my $patch_checksum_okay = 0;# checksum for the patch was okay

    print STDERR ("Validate input.\n") if $verbose;

    @ARGV = "-" if !@ARGV;
    for my $file (@ARGV) {
      my $argv = new IO::File;
      open($argv, $file) or die "Can't open $file: $!";
      binmode($argv);
      while ( <$argv> ) {
	chomp;
	if ( /^#### Patch data follows ####/ ) {
	    print STDERR (": $_\n") if $trace;
	    $patchdata |= 1;	# bit 0 means: start seen
	    $pos = $tmpfile->getpos;
	    $lines = $bytes = $sum = 0;
	}
	elsif ( /^#### End of Patch data ####/ ) {
	    print STDERR (": $_\n") if $trace;
	    $patchdata |= 2;	# bit 1 means: end seen
	}
	elsif ( /^#### ApplyPatch data follows ####/ ) {
	    print STDERR (": $_\n") if $trace;
	    $applypatch |= 1;
	}
	elsif ( /^#### End of ApplyPatch data ####/ ) {
	    print STDERR (": $_\n") if $trace;
	    $applypatch |= 2;
	}

script/applypatch  view on Meta::CPAN

	$all_bytes += length ($_);
	# System V 'sum' checksum
	$sum = ($sum + unpack ("%16C*", $_)) % 65535;
	$all_sum = ($all_sum + unpack ("%16C*", $_)) % 65535;

	# Copy the line to the temp file.
	print $tmpfile ($_);
      }
      close($argv);
    }

    # If we saw an ApplyPatch data section, it must be reliable.
    if ( $applypatch == 1 ) {
	warn ("ApplyPatch data section not properly terminated.\n");
	$fail = 1;
    }
    elsif ( $applypatch == 2 ) {
	warn ("ApplyPatch data section not reliable.\n");
	$fail = 1;
    }

    if ( $applypatch ) {
	# If we saw a Patch data section, it must be reliable.
	if ( $patchdata == 0 ) {
	    warn ("Patch data section not delimited.\n");
	    $fail = 1;
	}
	elsif ( $patchdata == 1 ) {
	    warn ("Patch data section not properly terminated.\n");
	    $fail = 1;
	}
	elsif ( $patchdata == 2 ) {
	    warn ("Patch data section not reliable.\n");
	    $fail = 1;
	}

	if ($endkit == 0 ) {
	    warn ("Missing \"#### End of Patch kit\" line.\n");
	    $fail = 1;
	}
    }

    if ( $fail ) {
	if ( $force ) {
	    warn ("WARNING: Verification of patch kit failed, ",
		  "continuing anyway.\n");
	}
	else {
	    die ("Verification of patch kit failed, aborting.\n",
		 "Use \"--force\" to override this.\n");
	}
    }

    print STDERR ($applypatch == 3 ? "Apply" : "",
		  "Patch kit apparently okay.\n") if $verbose;

    # Reset file to start of patch data.
    $tmpfile->setpos ($pos);
}

sub verify_files () {

    my $fail = 0;

    print STDERR ("Verify source directory.\n") if $verbose;

    foreach ( @workq ) {
	my ($op, $fn, @args) = @$_;

	if ( $op eq 'c' ) {
	    if ( -f $fn || -d _ ) {
		warn ("Verify error: file $fn must be created, ",
		      "but already exists.\n");
		$fail = 1;
	    }
	}
	elsif ( $op eq 'C' ) {
	    if ( -f $fn || -d _ ) {
		warn ("Verify error: directory $fn must be created, ",
		      "but already exists.\n");
		$fail = 1;
	    }
	}
	elsif ( $op eq 'r' || $op eq 'p' || $op eq 'v' ) {
	    my $sz = -s $fn;
	    if ( defined $sz ) {
		if ( $sz != $args[0] ) {
		    warn ("Verify error: size of $fn should be $args[0], but is ",
			  "$sz.\n");
		    $fail = 1;
		}
	    }
	    else {
		warn ("Verify error: file $fn is missing.\n");
		$fail = 1;
	    }
	}
	elsif ( $op eq 'R' ) {
	    unless ( -d $fn ) {
		warn ("Verify error: directory $fn must be removed, ",
		      "but does not exist.\n");
		$fail = 1;
	    }
	}
    }

    if ( $fail ) {
	if ( $force ) {
	    warn ("WARNING: This does not look like expected source ",
		  "directory, continuing anyway.\n");
	}
	else {
	    warn ("Apparently this is not the expected source directory, ",
		  "aborting.\n");
	    die ("Use \"--force\" to override this.\n");
	}
    }

    print STDERR ("Source directory apparently okay.\n") if $verbose;
}

script/applypatch  view on Meta::CPAN


    # Process options, if any.
    # Make sure defaults are set before returning!
    return unless @ARGV > 0;
    my @opts = ('check'		=> \$check,
		'dir|d=s'	=> \$dir,
		'retain'	=> \$retain,
		'force'		=> \$force,
		'verbose'	=> \$verbose,
		'quiet'		=> sub { $verbose = 0; },
		'patch=s'	=> \$patch,
		'test'		=> \$test,
		'trace'		=> \$trace,
		'debug'		=> \$debug,
		'help'		=> \$help);
    
    (!GetOptions (@opts) || $help) && app_usage (2);

}

sub app_usage ($) {
    my ($exit) = @_;
    print STDERR <<EndOfUsage;
Usage: $0 [options] patch-kit

    -help		this message
    -dir XXX		change to this directory before executing
    -check              check, but does not execute
    -retain		retain .orig file after patching
    -force              continue after verification failures
    -patch XXX		the patch command, default "$patch"
    -quiet		no information
    -verbose		verbose information
EndOfUsage
    exit $exit if defined $exit && $exit != 0;
}

1;

__END__

################ Documentation ################

=head1 NAME

applypatch - apply 'makepatch' generated script to update a source tree

=head1 SYNOPSIS

B<applypatch> [ I<options> ] I<patch-kit>

=head1 DESCRIPTION

B<Applypatch> applies a patch kit as generated by the B<makepatch>
program. It performs the following actions:

=over 4

=item *

First, it will extensively verify that the patch kit is complete and
did not get corrupted during transfer.

=item *

Then it will apply some heuristics to verify that the directory in
which the patch will be applied does indeed contain the expected
sources.

If a corruption or verification error is detected, B<applypatch> exits
without making changes.

=item *

If the kit is okay, and the directory seems to be the right one: it
creates new files and directories as necessary.

=item *

Then it runs the B<patch> program to apply the patch to the source files.

=item *

Upon completion, obsolete files, directories and .orig files are
removed, file modes of new files are set, and the timestamps of
all patched files are adjusted.

=back

=head1 Applypatch arguments

B<Applypatch> takes one argument, the name of the patch kit as
generated by B<makepatch>. If no name is specified, the patch kit is
read from standard input.

=head1 Applypatch options

Options are matched case insensitive, and may be abbreviated to uniqueness.

=over 4

=item B<-directory> I<dir>

The name of the source directory to be patched.

=item B<-check>

Perform the checks on the patch kit and patch directory, but do not
make any changes.

=item B<-force>

Force continuation of the patch process even when corruption or
verification errors are detected. This is very dangerous!

=item B<-retain>

Do not remove patch backup files (with extension C<.orig>) upon
completion. 

=item B<-patch> I<cmd>

The patch command to be used. Default is "C<patch -p0 -N>".
Additionally, a "C<-s>" will be added unless option B<-verbose> was
specified.



( run in 0.826 second using v1.01-cache-2.11-cpan-39bf76dae61 )