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, &timestamp);		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 + -
显示快捷键?