📄 patch.c
字号:
/* patch - a program to apply diffs to original files *//* $Id: patch.c,v 1.23 1997/07/05 10:32:23 eggert Exp $ *//*Copyright 1984, 1985, 1986, 1987, 1988 Larry WallCopyright 1989, 1990, 1991, 1992, 1993, 1997 Free Software Foundation, Inc.This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation; either version 2, 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with this program; see the file COPYING.If not, write to the Free Software Foundation,59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*/#define XTERN#include <common.h>#undef XTERN#define XTERN extern#include <argmatch.h>#include <backupfile.h>#include <getopt.h>#include <inp.h>#include <pch.h>#include <util.h>#include <version.h>#if HAVE_UTIME_H# include <utime.h>#endif/* Some nonstandard hosts don't declare this structure even in <utime.h>. */#if ! HAVE_STRUCT_UTIMBUFstruct utimbuf{ time_t actime; time_t modtime;};#endif/* Output stream state. */struct outstate{ FILE *ofp; int after_newline; int zero_output;};/* procedures */static FILE *create_output_file PARAMS ((char const *));static LINENUM locate_hunk PARAMS ((LINENUM));static bool apply_hunk PARAMS ((struct outstate *, LINENUM));static bool copy_till PARAMS ((struct outstate *, LINENUM));static bool patch_match PARAMS ((LINENUM, LINENUM, LINENUM, LINENUM));static bool similar PARAMS ((char const *, size_t, char const *, size_t));static bool spew_output PARAMS ((struct outstate *));static char const *make_temp PARAMS ((int));static int numeric_string PARAMS ((char const *, int, char const *));static void abort_hunk PARAMS ((void));static void cleanup PARAMS ((void));static void get_some_switches PARAMS ((void));static void init_output PARAMS ((char const *, struct outstate *));static void init_reject PARAMS ((char const *));static void reinitialize_almost_everything PARAMS ((void));static void usage PARAMS ((FILE *, int)) __attribute__((noreturn));static int make_backups;static int backup_if_mismatch;static char const *version_control;static int remove_empty_files;/* TRUE if -R was specified on command line. */static int reverse_flag_specified;/* how many input lines have been irretractably output */static LINENUM last_frozen_line;static char const *do_defines; /* symbol to patch using ifdef, ifndef, etc. */static char const if_defined[] = "\n#ifdef %s\n";static char const not_defined[] = "#ifndef %s\n";static char const else_defined[] = "\n#else\n";static char const end_defined[] = "\n#endif /* %s */\n";static int Argc;static char * const *Argv;static FILE *rejfp; /* reject file pointer */static char const *patchname;static char *rejname;static char const * volatile TMPREJNAME;static LINENUM last_offset;static LINENUM maxfuzz = 2;static char serrbuf[BUFSIZ];char const program_name[] = "patch";/* Apply a set of diffs as appropriate. */int main PARAMS ((int, char **));intmain(argc,argv)int argc;char **argv;{ char const *val; bool somefailed = FALSE; struct outstate outstate; init_time (); setbuf(stderr, serrbuf); bufsize = 8 * 1024; buf = xmalloc (bufsize); strippath = INT_MAX; posixly_correct = getenv ("POSIXLY_CORRECT") != 0; backup_if_mismatch = ! posixly_correct; patch_get = ((val = getenv ("PATCH_GET")) ? numeric_string (val, 1, "PATCH_GET value") : posixly_correct - 1); { char const *v = getenv ("SIMPLE_BACKUP_SUFFIX"); if (v && *v) simple_backup_suffix = v; } version_control = getenv ("PATCH_VERSION_CONTROL"); if (! version_control) version_control = getenv ("VERSION_CONTROL"); /* Cons up the names of the global temporary files. Do this before `cleanup' can possibly be called (e.g. by `pfatal'). */ TMPOUTNAME = make_temp ('o'); TMPINNAME = make_temp ('i'); TMPREJNAME = make_temp ('r'); TMPPATNAME = make_temp ('p'); /* parse switches */ Argc = argc; Argv = argv; get_some_switches(); if (make_backups | backup_if_mismatch) backup_type = get_version (version_control); init_output (outfile, &outstate); /* Make sure we clean up in case of disaster. */ set_signals(0); for ( open_patch_file (patchname); there_is_another_patch(); reinitialize_almost_everything() ) { /* for each patch in patch file */ int hunk = 0; int failed = 0; int mismatch = 0; char *outname = outfile ? outfile : inname; if (!skip_rest_of_patch) get_input_file (inname, outname); if (diff_type == ED_DIFF) { outstate.zero_output = 0; if (! dry_run) { do_ed_script (outstate.ofp); if (! outfile) { struct stat statbuf; if (stat (TMPOUTNAME, &statbuf) != 0) pfatal ("%s", TMPOUTNAME); outstate.zero_output = statbuf.st_size == 0; } } } else { int got_hunk; int apply_anyway = 0; /* initialize the patched file */ if (! skip_rest_of_patch && ! outfile) init_output (TMPOUTNAME, &outstate); /* initialize reject file */ init_reject(TMPREJNAME); /* find out where all the lines are */ if (!skip_rest_of_patch) scan_input (inname); /* from here on, open no standard i/o files, because malloc */ /* might misfire and we can't catch it easily */ /* apply each hunk of patch */ while (0 < (got_hunk = another_hunk (diff_type, reverse))) { LINENUM where = 0; /* Pacify `gcc -Wall'. */ LINENUM newwhere; LINENUM fuzz = 0; LINENUM prefix_context = pch_prefix_context (); LINENUM suffix_context = pch_suffix_context (); LINENUM context = (prefix_context < suffix_context ? suffix_context : prefix_context); LINENUM mymaxfuzz = (maxfuzz < context ? maxfuzz : context); hunk++; if (!skip_rest_of_patch) { do { where = locate_hunk(fuzz); if (! where || fuzz || last_offset) mismatch = 1; if (hunk == 1 && ! where && ! (force | apply_anyway) && reverse == reverse_flag_specified) { /* dwim for reversed patch? */ if (!pch_swap()) { say ("Not enough memory to try swapped hunk! Assuming unswapped.\n"); continue; } /* Try again. */ where = locate_hunk (fuzz); if (where && (ok_to_reverse ("%s patch detected!", (reverse ? "Unreversed" : "Reversed (or previously applied)")))) reverse ^= 1; else { /* Put it back to normal. */ if (! pch_swap ()) fatal ("lost hunk on alloc error!"); if (where) { apply_anyway = 1; fuzz--; /* Undo `++fuzz' below. */ where = 0; } } } } while (!skip_rest_of_patch && !where && ++fuzz <= mymaxfuzz); if (skip_rest_of_patch) { /* just got decided */ if (outstate.ofp && ! outfile) { fclose (outstate.ofp); outstate.ofp = 0; } } } newwhere = pch_newfirst() + last_offset; if (skip_rest_of_patch) { abort_hunk(); failed++; if (verbosity == VERBOSE) say ("Hunk #%d ignored at %ld.\n", hunk, newwhere); } else if (!where || (where == 1 && pch_says_nonexistent (reverse) && instat.st_size)) { if (where) say ("Patch attempted to create file `%s', which already exists.\n", inname); abort_hunk(); failed++; if (verbosity != SILENT) say ("Hunk #%d FAILED at %ld.\n", hunk, newwhere); } else if (! apply_hunk (&outstate, where)) { abort_hunk (); failed++; if (verbosity != SILENT) say ("Hunk #%d FAILED at %ld.\n", hunk, newwhere); } else { if (verbosity == VERBOSE || (verbosity != SILENT && (fuzz || last_offset))) { say ("Hunk #%d succeeded at %ld", hunk, newwhere); if (fuzz) say (" with fuzz %ld", fuzz); if (last_offset) say (" (offset %ld line%s)", last_offset, last_offset==1?"":"s"); say (".\n"); } } } if (got_hunk < 0 && using_plan_a) { if (outfile) fatal ("out of memory using Plan A"); say ("\n\nRan out of memory using Plan A -- trying again...\n\n"); if (outstate.ofp) { fclose (outstate.ofp); outstate.ofp = 0; } fclose (rejfp); continue; } /* finish spewing out the new file */ if (!skip_rest_of_patch) { assert (hunk); if (! spew_output (&outstate)) { say ("Skipping patch.\n"); skip_rest_of_patch = TRUE; } } } /* and put the output where desired */ ignore_signals (); if (! skip_rest_of_patch && ! outfile) { if (outstate.zero_output && (remove_empty_files || (pch_says_nonexistent (reverse ^ 1) == 2 && ! posixly_correct))) { if (verbosity == VERBOSE) say ("Removing file `%s'%s.\n", outname, dry_run ? " and any empty ancestor directories" : ""); if (! dry_run) { move_file ((char *) 0, outname, (mode_t) 0, (make_backups || (backup_if_mismatch && (mismatch | failed)))); removedirs (outname); } } else { if (! outstate.zero_output && pch_says_nonexistent (reverse ^ 1)) { mismatch = 1; if (verbosity != SILENT) say ("File `%s' is not empty after patch, as expected.\n", outname); } if (! dry_run) { time_t t; move_file (TMPOUTNAME, outname, instat.st_mode, (make_backups || (backup_if_mismatch && (mismatch | failed)))); if ((set_time | set_utc) && (t = pch_timestamp (reverse ^ 1)) != (time_t) -1) { struct utimbuf utimbuf; utimbuf.actime = utimbuf.modtime = t; if (! force && ! inerrno && ! pch_says_nonexistent (reverse) && (t = pch_timestamp (reverse)) != (time_t) -1 && t != instat.st_mtime) say ("not setting time of file `%s' (time mismatch)\n", outname); else if (! force && (mismatch | failed)) say ("not setting time of file `%s' (contents mismatch)\n", outname); else if (utime (outname, &utimbuf) != 0) pfatal ("can't set timestamp on file `%s'", outname); } if (! inerrno && chmod (outname, instat.st_mode) != 0) pfatal ("can't set permissions on file `%s'", outname); } } } if (diff_type != ED_DIFF) { if (fclose (rejfp) != 0) write_fatal (); if (failed) { somefailed = TRUE; say ("%d out of %d hunk%s %s", failed, hunk, "s" + (hunk == 1), skip_rest_of_patch ? "ignored" : "FAILED"); if (outname) { char *rej = rejname; if (!rejname) { rej = xmalloc (strlen (outname) + 5); strcpy (rej, outname); addext (rej, ".rej", '#'); } say (" -- saving rejects to %s", rej); if (! dry_run) { move_file (TMPREJNAME, rej, instat.st_mode, FALSE); if (! inerrno && (chmod (rej, (instat.st_mode & ~(S_IXUSR|S_IXGRP|S_IXOTH))) != 0)) pfatal ("can't set permissions on file `%s'", rej); } if (!rejname) free (rej); } say ("\n"); } } set_signals (1); } if (outstate.ofp && (ferror (outstate.ofp) || fclose (outstate.ofp) != 0)) write_fatal (); cleanup (); if (somefailed) exit (1); return 0;}/* Prepare to find the next patch to do in the patch file. */static voidreinitialize_almost_everything()
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -