📄 patch.c
字号:
/* patch - a program to apply diffs to original files *//* $Id: patch.c,v 1.44 2003/05/20 13:55:03 eggert Exp $ *//* Copyright (C) 1984, 1985, 1986, 1987, 1988 Larry Wall Copyright (C) 1989, 1990, 1991, 1992, 1993, 1997, 1998, 1999, 2002, 2003 Free Software Foundation, Inc. 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, 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 PARTICULAR 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; 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 <quotearg.h>#include <util.h>#include <version.h>#include <xalloc.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; bool after_newline; bool zero_output;};/* procedures */static FILE *create_output_file (char const *, int);static LINENUM locate_hunk (LINENUM);static bool apply_hunk (struct outstate *, LINENUM);static bool copy_till (struct outstate *, LINENUM);static bool patch_match (LINENUM, LINENUM, LINENUM, LINENUM);static bool similar (char const *, size_t, char const *, size_t);static bool spew_output (struct outstate *);static char const *make_temp (char);static int numeric_string (char const *, bool, char const *);static void abort_hunk (void);static void cleanup (void);static void get_some_switches (void);static void init_output (char const *, int, struct outstate *);static void init_reject (void);static void reinitialize_almost_everything (void);static void remove_if_needed (char const *, int volatile *);static void usage (FILE *, int) __attribute__((noreturn));static bool make_backups;static bool backup_if_mismatch;static char const *version_control;static char const *version_control_context;static bool remove_empty_files;/* true if -R was specified on command line. */static bool 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[] = "\n#ifndef %s\n";static char const else_defined[] = "\n#else\n";static char const end_defined[] = "\n#endif\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 int volatile TMPREJNAME_needs_removal;static LINENUM last_offset;static LINENUM maxfuzz = 2;static char serrbuf[BUFSIZ];/* Apply a set of diffs as appropriate. */intmain (int argc, char **argv){ char const *val; bool somefailed = false; struct outstate outstate; char numbuf[LINENUM_LENGTH_BOUND + 1]; xalloc_exit_failure = 2; program_name = argv[0]; init_time (); setbuf(stderr, serrbuf); xalloc_fail_func = memory_fatal; bufsize = 8 * 1024; buf = xmalloc (bufsize); strippath = -1; val = getenv ("QUOTING_STYLE"); { int i = val ? argmatch (val, quoting_style_args, 0, 0) : -1; set_quoting_style ((struct quoting_options *) 0, i < 0 ? shell_quoting_style : (enum quoting_style) i); } posixly_correct = getenv ("POSIXLY_CORRECT") != 0; backup_if_mismatch = ! posixly_correct; patch_get = ((val = getenv ("PATCH_GET")) ? numeric_string (val, true, "PATCH_GET value") : posixly_correct - 1); val = getenv ("SIMPLE_BACKUP_SUFFIX"); simple_backup_suffix = val && *val ? val : ".orig"; if ((version_control = getenv ("PATCH_VERSION_CONTROL"))) version_control_context = "$PATCH_VERSION_CONTROL"; else if ((version_control = getenv ("VERSION_CONTROL"))) version_control_context = "$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_context, version_control); init_output (outfile, 0, &outstate); /* Make sure we clean up in case of disaster. */ set_signals (false); for ( open_patch_file (patchname); there_is_another_patch(); reinitialize_almost_everything() ) { /* for each patch in patch file */ int hunk = 0; int failed = 0; bool mismatch = false; char *outname = outfile ? outfile : inname; if (!skip_rest_of_patch) get_input_file (inname, outname); if (diff_type == ED_DIFF) { outstate.zero_output = false; somefailed |= skip_rest_of_patch; do_ed_script (outstate.ofp); if (! dry_run && ! outfile && ! skip_rest_of_patch) { struct stat statbuf; if (stat (TMPOUTNAME, &statbuf) != 0) pfatal ("%s", TMPOUTNAME); outstate.zero_output = statbuf.st_size == 0; } } else { int got_hunk; bool apply_anyway = false; /* initialize the patched file */ if (! skip_rest_of_patch && ! outfile) { int exclusive = TMPOUTNAME_needs_removal ? 0 : O_EXCL; TMPOUTNAME_needs_removal = 1; init_output (TMPOUTNAME, exclusive, &outstate); } /* initialize reject file */ init_reject (); /* 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 = true; 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 = ! reverse; else { /* Put it back to normal. */ if (! pch_swap ()) fatal ("lost hunk on alloc error!"); if (where) { apply_anyway = true; 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 %s.\n", hunk, format_linenum (numbuf, newwhere)); } else if (!where || (where == 1 && pch_says_nonexistent (reverse) == 2 && instat.st_size)) { if (where) say ("Patch attempted to create file %s, which already exists.\n", quotearg (inname)); abort_hunk(); failed++; if (verbosity != SILENT) say ("Hunk #%d FAILED at %s.\n", hunk, format_linenum (numbuf, newwhere)); } else if (! apply_hunk (&outstate, where)) { abort_hunk (); failed++; if (verbosity != SILENT) say ("Hunk #%d FAILED at %s.\n", hunk, format_linenum (numbuf, newwhere)); } else { if (verbosity == VERBOSE || (verbosity != SILENT && (fuzz || last_offset))) { say ("Hunk #%d succeeded at %s", hunk, format_linenum (numbuf, newwhere)); if (fuzz) say (" with fuzz %s", format_linenum (numbuf, fuzz)); if (last_offset) say (" (offset %s line%s)", format_linenum (numbuf, last_offset), "s" + (last_offset == 1)); say (".\n"); } } } if (!skip_rest_of_patch) { 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. */ 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) == 2 && ! posixly_correct))) { if (verbosity == VERBOSE) say ("Removing file %s%s\n", quotearg (outname), dry_run ? " and any empty ancestor directories" : ""); if (! dry_run) { move_file ((char *) 0, (int *) 0, outname, (mode_t) 0, (make_backups || (backup_if_mismatch && (mismatch | failed)))); removedirs (outname); } } else { if (! outstate.zero_output && pch_says_nonexistent (! reverse)) { mismatch = true; if (verbosity != SILENT) say ("File %s is not empty after patch, as expected\n", quotearg (outname)); } if (! dry_run) { time_t t; move_file (TMPOUTNAME, &TMPOUTNAME_needs_removal, outname, instat.st_mode, (make_backups || (backup_if_mismatch && (mismatch | failed)))); if ((set_time | set_utc) && (t = pch_timestamp (! reverse)) != (time_t) -1) { struct utimbuf utimbuf; utimbuf.actime = utimbuf.modtime = t; if (! force && ! inerrno && pch_says_nonexistent (reverse) != 2 && (t = pch_timestamp (reverse)) != (time_t) -1 && t != instat.st_mtime) say ("Not setting time of file %s (time mismatch)\n", quotearg (outname)); else if (! force && (mismatch | failed)) say ("Not setting time of file %s (contents mismatch)\n", quotearg (outname)); else if (utime (outname, &utimbuf) != 0) pfatal ("Can't set timestamp on file %s", quotearg (outname)); } if (! inerrno && chmod (outname, instat.st_mode) != 0) pfatal ("Can't set permissions on file %s", quotearg (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 file %s", quotearg (rej)); if (! dry_run) { move_file (TMPREJNAME, &TMPREJNAME_needs_removal, 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", quotearg (rej)); } if (!rejname) free (rej); } say ("\n"); } } set_signals (true); } 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. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -