pch.c
来自「这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易」· C语言 代码 · 共 1,866 行 · 第 1/4 页
C
1,866 行
/* reading patches *//* $Id: pch.c,v 1.26 1997/07/21 17:59:46 eggert Exp $ *//*Copyright 1986, 1987, 1988 Larry WallCopyright 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 extern#include <common.h>#include <backupfile.h>#include <inp.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; value is 0 for nonempty, 1 for 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 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 enum diff intuit_diff_type PARAMS ((void));static enum nametype best_name PARAMS ((char * const *, int const *));static int prefix_components PARAMS ((char *, int));static size_t pget_line PARAMS ((int, int));static size_t get_line PARAMS ((void));static bool incomplete_line PARAMS ((void));static bool grow_hunkmax PARAMS ((void));static void malformed PARAMS ((void)) __attribute__ ((noreturn));static void next_intuit_at PARAMS ((file_offset, LINENUM));static void skip_to PARAMS ((file_offset, LINENUM));/* Prepare to look for the next patch in the patch file. */voidre_patch(){ p_first = 0; p_newfirst = 0; p_ptrn_lines = 0; p_repl_lines = 0; p_end = -1; p_max = 0; p_indent = 0;}/* Open the patch file at the beginning of time. */voidopen_patch_file(filename) char const *filename;{ file_offset file_pos = 0; struct stat st; if (!filename || !*filename || strEQ (filename, "-")) { file_offset stdin_pos;#if HAVE_SETMODE 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; pfp = fopen (TMPPATNAME, "w+b"); if (!pfp) pfatal ("can't create `%s'", 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'", 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(){ 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(){ 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(){ 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 (! inname) { say ("can't find file to patch at input line %ld\n", p_sline); say (strippath == INT_MAX ? "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); 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(){ register char *s; register char *t; register int indent; register file_offset this_line = 0; register file_offset previous_line; register file_offset first_command_line = -1; LINENUM fcl_line = 0; /* Pacify `gcc -W'. */ register bool last_line_was_command = FALSE; register bool this_is_a_command = FALSE; register bool stars_last_line = 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 (;;) { previous_line = this_line; last_line_was_command = this_is_a_command; stars_last_line = stars_this_line; this_line = file_tell (pfp); indent = 0; if (! pget_line (0, 0)) { if (first_command_line >= 0) { /* 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; } } 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 && this_is_a_command) { first_command_line = this_line; fcl_line = p_input_line; p_indent = indent; /* assume this for now */ } 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 && !ISSPACE ((unsigned char) *t); t++) continue; 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_indent = indent; 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 (! atol (s)) p_says_nonexistent[OLD] = 1 + ! p_timestamp[OLD]; while (*s != ' ' && *s != '\n') s++; while (*s == ' ') s++; if (! atol (s)) 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]))) say ("missing header for unified diff at line %ld of patch\n", 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 (! atol (s)) 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_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, 0) && ! 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]))) say ("missing header for context diff at line %ld of patch\n", 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; retval = NORMAL_DIFF; goto scan_exit; } } scan_exit: /* To intuit `inname', the name of the file to patch, use the algorithm specified by POSIX 1003.2b/D11 section 5.22.7.2 (with some modifications if posixly_correct is zero): - Take the old and new names from the context header if present,
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?