📄 pch.c
字号:
/* reading patches *//* $Id: pch.c,v 1.44 2003/05/20 14:03:17 eggert Exp $ *//* Copyright (C) 1986, 1987, 1988 Larry Wall Copyright (C) 1990, 1991, 1992, 1993, 1997, 1998, 1999, 2000, 2001, 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 extern#include <common.h>#include <backupfile.h>#include <dirname.h>#include <inp.h>#include <quotearg.h>#include <util.h>#undef XTERN#define XTERN#include <pch.h>#define INITHUNKMAX 125 /* initial dynamic allocation size *//* Patch (diff listing) abstract type. */static FILE *pfp; /* patch file pointer */static int p_says_nonexistent[2]; /* [0] for old file, [1] for new: 0 for existent and nonempty, 1 for existent and probably (but not necessarily) empty, 2 for nonexistent */static int p_rfc934_nesting; /* RFC 934 nesting level */static time_t p_timestamp[2]; /* timestamps in patch headers */static off_t p_filesize; /* size of the patch file */static LINENUM p_first; /* 1st line number */static LINENUM p_newfirst; /* 1st line number of replacement */static LINENUM p_ptrn_lines; /* # lines in pattern */static LINENUM p_repl_lines; /* # lines in replacement text */static LINENUM p_end = -1; /* last line in hunk */static LINENUM p_max; /* max allowed value of p_end */static LINENUM p_prefix_context; /* # of prefix context lines */static LINENUM p_suffix_context; /* # of suffix context lines */static LINENUM p_input_line; /* current line # from patch file */static char **p_line; /* the text of the hunk */static size_t *p_len; /* line length including \n if any */static char *p_Char; /* +, -, and ! */static LINENUM hunkmax = INITHUNKMAX; /* size of above arrays */static int p_indent; /* indent to patch */static bool p_strip_trailing_cr; /* true if stripping trailing \r */static bool p_pass_comments_through; /* true if not ignoring # lines */static file_offset p_base; /* where to intuit this time */static LINENUM p_bline; /* line # of p_base */static file_offset p_start; /* where intuit found a patch */static LINENUM p_sline; /* and the line number for it */static LINENUM p_hunk_beg; /* line number of current hunk */static LINENUM p_efake = -1; /* end of faked up lines--don't free */static LINENUM p_bfake = -1; /* beg of faked up lines */enum nametype { OLD, NEW, INDEX, NONE };static char *scan_linenum (char *, LINENUM *);static enum diff intuit_diff_type (void);static enum nametype best_name (char * const *, int const *);static int prefix_components (char *, bool);static size_t pget_line (int, int, bool, bool);static size_t get_line (void);static bool incomplete_line (void);static bool grow_hunkmax (void);static void malformed (void) __attribute__ ((noreturn));static void next_intuit_at (file_offset, LINENUM);static void skip_to (file_offset, LINENUM);static char get_ed_command_letter (char const *);/* Prepare to look for the next patch in the patch file. */voidre_patch (void){ p_first = 0; p_newfirst = 0; p_ptrn_lines = 0; p_repl_lines = 0; p_end = -1; p_max = 0; p_indent = 0; p_strip_trailing_cr = false;}/* Open the patch file at the beginning of time. */voidopen_patch_file (char const *filename){ file_offset file_pos = 0; struct stat st; if (!filename || !*filename || strEQ (filename, "-")) { file_offset stdin_pos;#if HAVE_SETMODE_DOS if (binary_transput) { if (isatty (STDIN_FILENO)) fatal ("cannot read binary data from tty on this platform"); setmode (STDIN_FILENO, O_BINARY); }#endif if (fstat (STDIN_FILENO, &st) != 0) pfatal ("fstat"); if (S_ISREG (st.st_mode) && (stdin_pos = file_tell (stdin)) != -1) { pfp = stdin; file_pos = stdin_pos; } else { size_t charsread; int exclusive = TMPPATNAME_needs_removal ? 0 : O_EXCL; TMPPATNAME_needs_removal = 1; pfp = fdopen (create_file (TMPPATNAME, O_RDWR | O_BINARY | exclusive, (mode_t) 0), "w+b"); if (!pfp) pfatal ("Can't open stream for file %s", quotearg (TMPPATNAME)); for (st.st_size = 0; (charsread = fread (buf, 1, bufsize, stdin)) != 0; st.st_size += charsread) if (fwrite (buf, 1, charsread, pfp) != charsread) write_fatal (); if (ferror (stdin) || fclose (stdin) != 0) read_fatal (); if (fflush (pfp) != 0 || file_seek (pfp, (file_offset) 0, SEEK_SET) != 0) write_fatal (); } } else { pfp = fopen (filename, binary_transput ? "rb" : "r"); if (!pfp) pfatal ("Can't open patch file %s", quotearg (filename)); if (fstat (fileno (pfp), &st) != 0) pfatal ("fstat"); } p_filesize = st.st_size; if (p_filesize != (file_offset) p_filesize) fatal ("patch file is too long"); next_intuit_at (file_pos, (LINENUM) 1); set_hunkmax();}/* Make sure our dynamically realloced tables are malloced to begin with. */voidset_hunkmax (void){ if (!p_line) p_line = (char **) malloc (hunkmax * sizeof *p_line); if (!p_len) p_len = (size_t *) malloc (hunkmax * sizeof *p_len); if (!p_Char) p_Char = malloc (hunkmax * sizeof *p_Char);}/* Enlarge the arrays containing the current hunk of patch. */static boolgrow_hunkmax (void){ hunkmax *= 2; assert (p_line && p_len && p_Char); if ((p_line = (char **) realloc (p_line, hunkmax * sizeof (*p_line))) && (p_len = (size_t *) realloc (p_len, hunkmax * sizeof (*p_len))) && (p_Char = realloc (p_Char, hunkmax * sizeof (*p_Char)))) return true; if (!using_plan_a) memory_fatal (); /* Don't free previous values of p_line etc., since some broken implementations free them for us. Whatever is null will be allocated again from within plan_a (), of all places. */ return false;}/* True if the remainder of the patch file contains a diff of some sort. */boolthere_is_another_patch (void){ if (p_base != 0 && p_base >= p_filesize) { if (verbosity == VERBOSE) say ("done\n"); return false; } if (verbosity == VERBOSE) say ("Hmm..."); diff_type = intuit_diff_type(); if (diff_type == NO_DIFF) { if (verbosity == VERBOSE) say (p_base ? " Ignoring the trailing garbage.\ndone\n" : " I can't seem to find a patch in there anywhere.\n"); if (! p_base && p_filesize) fatal ("Only garbage was found in the patch input."); return false; } if (skip_rest_of_patch) { Fseek (pfp, p_start, SEEK_SET); p_input_line = p_sline - 1; return true; } if (verbosity == VERBOSE) say (" %sooks like %s to me...\n", (p_base == 0 ? "L" : "The next patch l"), diff_type == UNI_DIFF ? "a unified diff" : diff_type == CONTEXT_DIFF ? "a context diff" : diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" : diff_type == NORMAL_DIFF ? "a normal diff" : "an ed script" ); if (verbosity != SILENT) { if (p_indent) say ("(Patch is indented %d space%s.)\n", p_indent, p_indent==1?"":"s"); if (p_strip_trailing_cr) say ("(Stripping trailing CRs from patch.)\n"); if (! inname) { char numbuf[LINENUM_LENGTH_BOUND + 1]; say ("can't find file to patch at input line %s\n", format_linenum (numbuf, p_sline)); if (diff_type != ED_DIFF) say (strippath == -1 ? "Perhaps you should have used the -p or --strip option?\n" : "Perhaps you used the wrong -p or --strip option?\n"); } } skip_to(p_start,p_sline); while (!inname) { if (force | batch) { say ("No file to patch. Skipping patch.\n"); skip_rest_of_patch = true; return true; } ask ("File to patch: "); inname = fetchname (buf, 0, (time_t *) 0); if (inname) { if (stat (inname, &instat) == 0) { inerrno = 0; invc = -1; } else { perror (inname); fflush (stderr); free (inname); inname = 0; } } if (!inname) { ask ("Skip this patch? [y] "); if (*buf != 'n') { if (verbosity != SILENT) say ("Skipping patch.\n"); skip_rest_of_patch = true; return true; } } } return true;}/* Determine what kind of diff is in the remaining part of the patch file. */static enum diffintuit_diff_type (void){ register file_offset this_line = 0; register file_offset first_command_line = -1; char first_ed_command_letter = 0; LINENUM fcl_line = 0; /* Pacify `gcc -W'. */ register bool this_is_a_command = false; register bool stars_this_line = false; enum nametype i; char *name[3]; struct stat st[3]; int stat_errno[3]; int version_controlled[3]; register enum diff retval; name[OLD] = name[NEW] = name[INDEX] = 0; version_controlled[OLD] = -1; version_controlled[NEW] = -1; version_controlled[INDEX] = -1; p_rfc934_nesting = 0; p_timestamp[OLD] = p_timestamp[NEW] = (time_t) -1; p_says_nonexistent[OLD] = p_says_nonexistent[NEW] = 0; Fseek (pfp, p_base, SEEK_SET); p_input_line = p_bline - 1; for (;;) { register char *s; register char *t; register file_offset previous_line = this_line; register bool last_line_was_command = this_is_a_command; register bool stars_last_line = stars_this_line; register int indent = 0; char ed_command_letter; bool strip_trailing_cr; size_t chars_read; this_line = file_tell (pfp); chars_read = pget_line (0, 0, false, false); if (chars_read == (size_t) -1) memory_fatal (); if (! chars_read) { if (first_ed_command_letter) { /* nothing but deletes!? */ p_start = first_command_line; p_sline = fcl_line; retval = ED_DIFF; goto scan_exit; } else { p_start = this_line; p_sline = p_input_line; return NO_DIFF; } } strip_trailing_cr = 2 <= chars_read && buf[chars_read - 2] == '\r'; for (s = buf; *s == ' ' || *s == '\t' || *s == 'X'; s++) { if (*s == '\t') indent = (indent + 8) & ~7; else indent++; } for (t = s; ISDIGIT (*t) || *t == ','; t++) continue; this_is_a_command = (ISDIGIT (*s) && (*t == 'd' || *t == 'c' || *t == 'a') ); if (first_command_line < 0 && ((ed_command_letter = get_ed_command_letter (s)) || this_is_a_command)) { first_command_line = this_line; first_ed_command_letter = ed_command_letter; fcl_line = p_input_line; p_indent = indent; /* assume this for now */ p_strip_trailing_cr = strip_trailing_cr; } if (!stars_last_line && strnEQ(s, "*** ", 4)) name[OLD] = fetchname (s+4, strippath, &p_timestamp[OLD]); else if (strnEQ(s, "+++ ", 4)) /* Swap with NEW below. */ name[OLD] = fetchname (s+4, strippath, &p_timestamp[OLD]); else if (strnEQ(s, "Index:", 6)) name[INDEX] = fetchname (s+6, strippath, (time_t *) 0); else if (strnEQ(s, "Prereq:", 7)) { for (t = s + 7; ISSPACE ((unsigned char) *t); t++) continue; revision = t; for (t = revision; *t; t++) if (ISSPACE ((unsigned char) *t)) { char const *u; for (u = t + 1; ISSPACE ((unsigned char) *u); u++) continue; if (*u) { char numbuf[LINENUM_LENGTH_BOUND + 1]; say ("Prereq: with multiple words at line %s of patch\n", format_linenum (numbuf, this_line)); } break; } if (t == revision) revision = 0; else { char oldc = *t; *t = '\0'; revision = savestr (revision); *t = oldc; } } else { for (t = s; t[0] == '-' && t[1] == ' '; t += 2) continue; if (strnEQ(t, "--- ", 4)) { time_t timestamp = (time_t) -1; name[NEW] = fetchname (t+4, strippath, ×tamp); if (timestamp != (time_t) -1) { p_timestamp[NEW] = timestamp; p_rfc934_nesting = (t - s) >> 1; } } } if ((diff_type == NO_DIFF || diff_type == ED_DIFF) && first_command_line >= 0 && strEQ(s, ".\n") ) { p_start = first_command_line; p_sline = fcl_line; retval = ED_DIFF; goto scan_exit; } if ((diff_type == NO_DIFF || diff_type == UNI_DIFF) && strnEQ(s, "@@ -", 4)) { /* `name' and `p_timestamp' are backwards; swap them. */ time_t ti = p_timestamp[OLD]; p_timestamp[OLD] = p_timestamp[NEW]; p_timestamp[NEW] = ti; t = name[OLD]; name[OLD] = name[NEW]; name[NEW] = t; s += 4; if (s[0] == '0' && !ISDIGIT (s[1])) p_says_nonexistent[OLD] = 1 + ! p_timestamp[OLD]; while (*s != ' ' && *s != '\n') s++; while (*s == ' ') s++; if (s[0] == '+' && s[1] == '0' && !ISDIGIT (s[2])) p_says_nonexistent[NEW] = 1 + ! p_timestamp[NEW]; p_indent = indent; p_start = this_line; p_sline = p_input_line; retval = UNI_DIFF; if (! ((name[OLD] || ! p_timestamp[OLD]) && (name[NEW] || ! p_timestamp[NEW])) && ! name[INDEX]) { char numbuf[LINENUM_LENGTH_BOUND + 1]; say ("missing header for unified diff at line %s of patch\n", format_linenum (numbuf, p_sline)); } goto scan_exit; } stars_this_line = strnEQ(s, "********", 8); if ((diff_type == NO_DIFF || diff_type == CONTEXT_DIFF || diff_type == NEW_CONTEXT_DIFF) && stars_last_line && strnEQ (s, "*** ", 4)) { s += 4; if (s[0] == '0' && !ISDIGIT (s[1])) p_says_nonexistent[OLD] = 1 + ! p_timestamp[OLD]; /* if this is a new context diff the character just before */ /* the newline is a '*'. */ while (*s != '\n') s++; p_indent = indent; p_strip_trailing_cr = strip_trailing_cr; p_start = previous_line; p_sline = p_input_line - 1; retval = (*(s-1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF); { /* Scan the first hunk to see whether the file contents appear to have been deleted. */ file_offset saved_p_base = p_base; LINENUM saved_p_bline = p_bline; Fseek (pfp, previous_line, SEEK_SET); p_input_line -= 2; if (another_hunk (retval, false) && ! p_repl_lines && p_newfirst == 1) p_says_nonexistent[NEW] = 1 + ! p_timestamp[NEW]; next_intuit_at (saved_p_base, saved_p_bline); } if (! ((name[OLD] || ! p_timestamp[OLD]) && (name[NEW] || ! p_timestamp[NEW])) && ! name[INDEX]) { char numbuf[LINENUM_LENGTH_BOUND + 1]; say ("missing header for context diff at line %s of patch\n", format_linenum (numbuf, p_sline)); } goto scan_exit; } if ((diff_type == NO_DIFF || diff_type == NORMAL_DIFF) && last_line_was_command && (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2)) ) { p_start = previous_line; p_sline = p_input_line - 1; p_indent = indent;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -