diff options
Diffstat (limited to 'inp.c')
-rw-r--r-- | inp.c | 672 |
1 files changed, 402 insertions, 270 deletions
@@ -1,32 +1,64 @@ -/* $Header: inp.c,v 2.0.1.1 88/06/03 15:06:13 lwall Locked $ - * - * $Log: inp.c,v $ - * Revision 2.0.1.1 88/06/03 15:06:13 lwall - * patch10: made a little smarter about sccs files - * - * Revision 2.0 86/09/17 15:37:02 lwall - * Baseline for netwide release. - * - */ - -#include "EXTERN.h" -#include "common.h" -#include "util.h" -#include "pch.h" -#include "INTERN.h" -#include "inp.h" +/* inputting files to be patched */ + +/* $Id: inp.c,v 1.9 1997/04/17 16:15:48 eggert Exp $ */ + +/* +Copyright 1986, 1988 Larry Wall +Copyright 1991, 1992, 1993, 1997 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 <pch.h> +#include <util.h> +#undef XTERN +#define XTERN +#include <inp.h> + +#define SCCSPREFIX "s." +#define GET "get '%s'" +#define GET_LOCKED "get -e '%s'" +#define SCCSDIFF "get -p '%s' | diff - '%s%s' >/dev/null" + +#define RCSSUFFIX ",v" +#define CHECKOUT "co '%s%s'" +#define CHECKOUT_LOCKED "co -l '%s%s'" +#define RCSDIFF "rcsdiff '%s%s' > /dev/null" /* Input-file-with-indexable-lines abstract type */ -static long i_size; /* size of the input file */ -static char *i_womp; /* plan a buffer for entire file */ -static char **i_ptr; /* pointers to lines in i_womp */ +static char const **i_ptr; /* pointers to lines in plan A buffer */ +static size_t tibufsize; /* size of plan b buffers */ +#ifndef TIBUFSIZE_MINIMUM +#define TIBUFSIZE_MINIMUM (8 * 1024) /* minimum value for tibufsize */ +#endif static int tifd = -1; /* plan b virtual string array */ static char *tibuf[2]; /* plan b buffers */ static LINENUM tiline[2] = {-1, -1}; /* 1st line in each buffer */ static LINENUM lines_per_buf; /* how many lines per buffer */ -static int tireclen; /* length of records in tmp file */ +static size_t tireclen; /* length of records in tmp file */ +static size_t last_line_size; /* size of last input line */ + +static bool plan_a PARAMS ((char const *));/* yield FALSE if memory runs out */ +static void plan_b PARAMS ((char const *)); +static void report_revision PARAMS ((int)); /* New patch--prepare to edit another file. */ @@ -34,23 +66,16 @@ void re_input() { if (using_plan_a) { - i_size = 0; -#ifndef lint - if (i_ptr != Null(char**)) - free((char *)i_ptr); -#endif - if (i_womp != Nullch) - free(i_womp); - i_womp = Nullch; - i_ptr = Null(char **); + if (i_ptr) { + free (i_ptr); + i_ptr = 0; + } } else { - using_plan_a = TRUE; /* maybe the next one is smaller */ - Close(tifd); + close (tifd); tifd = -1; free(tibuf[0]); - free(tibuf[1]); - tibuf[0] = tibuf[1] = Nullch; + tibuf[0] = 0; tiline[0] = tiline[1] = -1; tireclen = 0; } @@ -62,270 +87,393 @@ void scan_input(filename) char *filename; { - if (!plan_a(filename)) + using_plan_a = plan_a (filename); + if (!using_plan_a) plan_b(filename); - if (verbose) { - say3("Patching file %s using Plan %s...\n", filename, - (using_plan_a ? "A" : "B") ); + switch (verbosity) + { + case SILENT: + break; + + case VERBOSE: + say ("Patching file %s using Plan %s...\n", + filename, using_plan_a ? "A" : "B"); + break; + + case DEFAULT_VERBOSITY: + say ("patching file %s\n", filename); + break; + } +} + +/* Report whether a desired revision was found. */ + +static void +report_revision (found_revision) + int found_revision; +{ + if (found_revision) + { + if (verbosity == VERBOSE) + say ("Good. This file appears to be the %s version.\n", revision); + } + else if (force) + { + if (verbosity != SILENT) + say ("Warning: this file doesn't appear to be the %s version--patching anyway.\n", + revision); + } + else if (batch) + { + fatal ("this file doesn't appear to be the %s version--aborting.", + revision); + } + else + { + ask ("This file doesn't appear to be the %s version--patch anyway? [n] ", + revision); + if (*buf != 'y') + fatal ("aborted"); } } -/* Try keeping everything in memory. */ -bool -plan_a(filename) -char *filename; +void +get_input_file (filename, outname) + char const *filename; + char const *outname; { - int ifd, statfailed; - Reg1 char *s; - Reg2 LINENUM iline; - char lbuf[MAXLINELEN]; - int output_elsewhere = strcmp(filename, outname); - - statfailed = stat(filename, &filestat); - if (statfailed && ok_to_create_file) { - if (verbose) - say2("(Creating file %s...)\n",filename); - makedirs(filename, TRUE); - close(creat(filename, 0666)); - statfailed = stat(filename, &filestat); + int elsewhere = strcmp (filename, outname); + char const *dotslash; + + if (inerrno == -1) + inerrno = stat (inname, &instat) == 0 ? 0 : errno; + if (inerrno && ok_to_create_file) { + int fd; + if (verbosity == VERBOSE) + say ("(Creating file %s...)\n", inname); + if (dry_run) { + inerrno = 0; + instat.st_mode = 0; + instat.st_size = 0; + return; + } + makedirs (inname); + fd = creat (inname, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); + if (fd < 0) + inerrno = errno; + else { + inerrno = fstat (fd, &instat) == 0 ? 0 : errno; + if (close (fd) != 0) + inerrno = errno; + } } + /* For nonexistent or read-only files, look for RCS or SCCS versions. */ - if (statfailed - || (! output_elsewhere - && (/* No one can write to it. */ - (filestat.st_mode & 0222) == 0 - /* I can't write to it. */ - || ((filestat.st_mode & 0022) == 0 - && filestat.st_uid != myuid)))) { + if (backup_type == numbered_existing + && (inerrno + || (! elsewhere + && (/* No one can write to it. */ + (instat.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) == 0 + /* I can't write to it. */ + || ((instat.st_mode & (S_IWGRP|S_IWOTH)) == 0 + && instat.st_uid != getuid ()))))) { + register char *s; struct stat cstat; - char *cs = Nullch; - char *filebase; - int pathlen; + char const *cs = 0; + char const *filebase; + size_t dir_len; + char *lbuf = xmalloc (strlen (filename) + 100); - filebase = basename(filename); - pathlen = filebase - filename; + strcpy (lbuf, filename); + dir_len = base_name (lbuf) - lbuf; + filebase = filename + dir_len; /* Put any leading path into `s'. Leave room in lbuf for the diff command. */ s = lbuf + 20; - strncpy(s, filename, pathlen); + memcpy (s, filename, dir_len); + dotslash = *filename=='-' ? "./" : ""; -#define try(f,a1,a2) (Sprintf(s + pathlen, f, a1, a2), stat(s, &cstat) == 0) - if (( try("RCS/%s%s", filebase, RCSSUFFIX) - || try("RCS/%s" , filebase, 0) - || try( "%s%s", filebase, RCSSUFFIX)) +#define try1(f,a1) (sprintf (s + dir_len, f, a1), stat (s, &cstat) == 0) +#define try2(f,a1,a2) (sprintf (s + dir_len, f, a1,a2), stat (s, &cstat) == 0) + if (( try2 ("RCS/%s%s", filebase, RCSSUFFIX) + || try1 ("RCS/%s" , filebase) + || try2 ( "%s%s", filebase, RCSSUFFIX)) && /* Check that RCS file is not working file. Some hosts don't report file name length errors. */ - (statfailed - || ( (filestat.st_dev ^ cstat.st_dev) - | (filestat.st_ino ^ cstat.st_ino)))) { - Sprintf(buf, output_elsewhere?CHECKOUT:CHECKOUT_LOCKED, filename); - Sprintf(lbuf, RCSDIFF, filename); + (inerrno + || ( (instat.st_dev ^ cstat.st_dev) + | (instat.st_ino ^ cstat.st_ino)))) { + sprintf (buf, elsewhere ? CHECKOUT : CHECKOUT_LOCKED, + dotslash, filename); + sprintf (lbuf, RCSDIFF, dotslash, filename); cs = "RCS"; - } else if ( try("SCCS/%s%s", SCCSPREFIX, filebase) - || try( "%s%s", SCCSPREFIX, filebase)) { - Sprintf(buf, output_elsewhere?GET:GET_LOCKED, s); - Sprintf(lbuf, SCCSDIFF, s, filename); + } else if ( try2 ("SCCS/%s%s", SCCSPREFIX, filebase) + || try2 ( "%s%s", SCCSPREFIX, filebase)) { + sprintf (buf, elsewhere ? GET : GET_LOCKED, s); + sprintf (lbuf, SCCSDIFF, s, dotslash, filename); cs = "SCCS"; - } else if (statfailed) - fatal2("can't find %s\n", filename); + } else if (inerrno) + fatal ("can't find %s", filename); /* else we can't write to it but it's not under a version control system, so just proceed. */ if (cs) { - if (!statfailed) { - if ((filestat.st_mode & 0222) != 0) + if (!inerrno) { + if ((instat.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) != 0) /* The owner can write to it. */ - fatal3("file %s seems to be locked by somebody else under %s\n", + fatal ("file %s seems to be locked by somebody else under %s", filename, cs); /* It might be checked out unlocked. See if it's safe to check out the default version locked. */ - if (verbose) - say3("Comparing file %s to default %s version...\n", + if (verbosity == VERBOSE) + say ("Comparing file %s to default %s version...\n", filename, cs); - if (system(lbuf)) - fatal3("can't check out file %s: differs from default %s version\n", - filename, cs); + if (system (lbuf) != 0) + { + say ("warning: patching file %s, which does not match default %s version\n", + filename, cs); + cs = 0; + } } - if (verbose) - say3("Checking out file %s from %s...\n", filename, cs); - if (system(buf) || stat(filename, &filestat)) - fatal3("can't check out file %s from %s\n", filename, cs); + if (cs) + { + if (dry_run) + { + if (inerrno) + fatal ("Cannot dry run on nonexistent version-controlled file `%s'; invoke `%s' and try again.", + filename, buf); + } + else + { + if (verbosity == VERBOSE) + say ("Checking out file %s from %s...\n", filename, cs); + if (system (buf) != 0 || stat (filename, &instat) != 0) + fatal ("can't check out file %s from %s", filename, cs); + inerrno = 0; + } + } } + free (lbuf); } - filemode = filestat.st_mode; - if (!S_ISREG(filemode)) - fatal2("%s is not a normal file--can't patch\n", filename); - i_size = filestat.st_size; - if (out_of_mem) { - set_hunkmax(); /* make sure dynamic arrays are allocated */ - out_of_mem = FALSE; - return FALSE; /* force plan b because plan a bombed */ - } -#ifdef lint - i_womp = Nullch; -#else - i_womp = malloc((MEM)(i_size+2)); /* lint says this may alloc less than */ - /* i_size, but that's okay, I think. */ -#endif - if (i_womp == Nullch) - return FALSE; - if ((ifd = open(filename, 0)) < 0) - pfatal2("can't open file %s", filename); -#ifndef lint - if (read(ifd, i_womp, (int)i_size) != i_size) { - Close(ifd); /* probably means i_size > 15 or 16 bits worth */ - free(i_womp); /* at this point it doesn't matter if i_womp was */ - return FALSE; /* undersized. */ - } -#endif - Close(ifd); - if (i_size && i_womp[i_size-1] != '\n') - i_womp[i_size++] = '\n'; - i_womp[i_size] = '\0'; + if (!S_ISREG (instat.st_mode)) + fatal ("%s is not a regular file--can't patch", filename); +} - /* count the lines in the buffer so we know how many pointers we need */ - iline = 0; - for (s=i_womp; *s; s++) { - if (*s == '\n') - iline++; - } -#ifdef lint - i_ptr = Null(char**); -#else - i_ptr = (char **)malloc((MEM)((iline + 2) * sizeof(char *))); -#endif - if (i_ptr == Null(char **)) { /* shucks, it was a near thing */ - free((char *)i_womp); - return FALSE; +/* Try keeping everything in memory. */ + +static bool +plan_a(filename) + char const *filename; +{ + register char const *s; + register char const *lim; + register char const **ptr; + register char *buffer; + register LINENUM iline; + size_t size = instat.st_size; + size_t allocated_bytes_per_input_byte = sizeof *i_ptr + sizeof (char); + size_t allocated_bytes = (size + 2) * allocated_bytes_per_input_byte; + + /* Fail if arithmetic overflow occurs during size calculations, + or if storage isn't available. */ + if (size != instat.st_size + || size + 2 < 2 + || allocated_bytes / allocated_bytes_per_input_byte != size + 2 + || ! (ptr = (char const **) malloc (allocated_bytes))) + return FALSE; + + buffer = (char *) (ptr + (size + 2)); + + /* Read the input file, but don't bother reading it if it's empty. + During dry runs, empty files may not actually exist. */ + if (size) + { + int ifd = open (filename, O_RDONLY); + if (ifd < 0) + pfatal ("can't open file %s", filename); + if (read (ifd, buffer, size) != size) + { + /* Perhaps size is too large for this host. */ + close (ifd); + free (ptr); + return FALSE; + } + if (close (ifd) != 0) + read_fatal (); } - - /* now scan the buffer and build pointer array */ - - iline = 1; - i_ptr[iline] = i_womp; - for (s=i_womp; *s; s++) { - if (*s == '\n') - i_ptr[++iline] = s+1; /* these are NOT null terminated */ + + /* Scan the buffer and build array of pointers to lines. */ + iline = 0; + lim = buffer + size; + for (s = buffer; ; s++) + { + ptr[++iline] = s; + if (! (s = (char *) memchr (s, '\n', lim - s))) + break; } - input_lines = iline - 1; + if (size && lim[-1] != '\n') + ptr[++iline] = lim; + input_lines = iline - 1; - /* now check for revision, if any */ + if (revision) + { + char const *rev = revision; + int rev0 = rev[0]; + int found_revision = 0; + size_t revlen = strlen (rev); - if (revision != Nullch) { - if (!rev_in_string(i_womp)) { - if (force) { - if (verbose) - say2( -"Warning: this file doesn't appear to be the %s version--patching anyway.\n", - revision); - } - else if (batch) { - fatal2( -"this file doesn't appear to be the %s version--aborting.\n", revision); - } - else { - ask2( -"This file doesn't appear to be the %s version--patch anyway? [n] ", - revision); - if (*buf != 'y') - fatal1("aborted\n"); - } + if (revlen <= size) + { + char const *limrev = lim - revlen; + + for (s = buffer; (s = (char *) memchr (s, rev0, limrev - s)); s++) + if (memcmp (s, rev, revlen) == 0 + && (s == buffer || ISSPACE ((unsigned char) s[-1])) + && (s + 1 == limrev || ISSPACE ((unsigned char) s[revlen]))) + { + found_revision = 1; + break; + } } - else if (verbose) - say2("Good. This file appears to be the %s version.\n", - revision); + + report_revision (found_revision); } - return TRUE; /* plan a will work */ + + /* Plan A will work. */ + i_ptr = ptr; + return TRUE; } /* Keep (virtually) nothing in memory. */ -void +static void plan_b(filename) -char *filename; + char const *filename; { - Reg3 FILE *ifp; - Reg1 int i = 0; - Reg2 int maxlen = 1; - Reg4 bool found_revision = (revision == Nullch); - - using_plan_a = FALSE; - if ((ifp = fopen(filename, "r")) == Nullfp) - pfatal2("can't open file %s", filename); - if ((tifd = creat(TMPINNAME, 0666)) < 0) - pfatal2("can't open file %s", TMPINNAME); - while (fgets(buf, sizeof buf, ifp) != Nullch) { - if (revision != Nullch && !found_revision && rev_in_string(buf)) - found_revision = TRUE; - if ((i = strlen(buf)) > maxlen) - maxlen = i; /* find longest line */ - } - if (revision != Nullch) { - if (!found_revision) { - if (force) { - if (verbose) - say2( -"Warning: this file doesn't appear to be the %s version--patching anyway.\n", - revision); - } - else if (batch) { - fatal2( -"this file doesn't appear to be the %s version--aborting.\n", revision); - } - else { - ask2( -"This file doesn't appear to be the %s version--patch anyway? [n] ", - revision); - if (*buf != 'y') - fatal1("aborted\n"); + register FILE *ifp; + register int c; + register size_t len; + register size_t maxlen; + register int found_revision; + register size_t i; + register char const *rev; + register size_t revlen; + register LINENUM line; + + if (dry_run) + filename = "/dev/null"; + if (! (ifp = fopen (filename, "r"))) + pfatal ("can't open file %s", filename); + tifd = creat (TMPINNAME, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); + if (tifd < 0) + pfatal ("can't open file %s", TMPINNAME); + i = 0; + len = 0; + maxlen = 1; + rev = revision; + found_revision = !rev; + revlen = rev ? strlen (rev) : 0; + + while ((c = getc (ifp)) != EOF) + { + len++; + + if (c == '\n') + { + if (maxlen < len) + maxlen = len; + len = 0; + } + + if (!found_revision) + { + if (i == revlen) + { + found_revision = ISSPACE ((unsigned char) c); + i = (size_t) -1; } + else if (i != (size_t) -1) + i = rev[i]==c ? i + 1 : (size_t) -1; + + if (i == (size_t) -1 && ISSPACE ((unsigned char) c)) + i = 0; } - else if (verbose) - say2("Good. This file appears to be the %s version.\n", - revision); } - Fseek(ifp, 0L, 0); /* rewind file */ - lines_per_buf = BUFFERSIZE / maxlen; - tireclen = maxlen; - tibuf[0] = malloc((MEM)(BUFFERSIZE + 1)); - tibuf[1] = malloc((MEM)(BUFFERSIZE + 1)); - if (tibuf[1] == Nullch) - fatal1("out of memory\n"); - for (i=1; ; i++) { - if (! (i % lines_per_buf)) /* new block */ - if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE) - pfatal1("can't write temp file"); - if (fgets(tibuf[0] + maxlen * (i%lines_per_buf), maxlen + 1, ifp) - == Nullch) { - input_lines = i - 1; - if (i % lines_per_buf) - if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE) - pfatal1("can't write temp file"); - break; + + if (revision) + report_revision (found_revision); + Fseek (ifp, (off_t) 0, SEEK_SET); /* rewind file */ + for (tibufsize = TIBUFSIZE_MINIMUM; tibufsize < maxlen; tibufsize <<= 1) + continue; + lines_per_buf = tibufsize / maxlen; + tireclen = maxlen; + tibuf[0] = xmalloc (2 * tibufsize); + tibuf[1] = tibuf[0] + tibufsize; + + for (line = 1; ; line++) + { + char *p = tibuf[0] + maxlen * (line % lines_per_buf); + char const *p0 = p; + if (! (line % lines_per_buf)) /* new block */ + if (write (tifd, tibuf[0], tibufsize) != tibufsize) + write_fatal (); + if ((c = getc (ifp)) == EOF) + break; + + for (;;) + { + *p++ = c; + if (c == '\n') + { + last_line_size = p - p0; + break; + } + + if ((c = getc (ifp)) == EOF) + { + last_line_size = p - p0; + line++; + goto EOF_reached; + } } } - Fclose(ifp); - Close(tifd); - if ((tifd = open(TMPINNAME, 0)) < 0) { - pfatal2("can't reopen file %s", TMPINNAME); - } + EOF_reached: + if (ferror (ifp) || fclose (ifp) != 0) + read_fatal (); + + if (line % lines_per_buf != 0) + if (write (tifd, tibuf[0], tibufsize) != tibufsize) + write_fatal (); + input_lines = line - 1; + if (close (tifd) != 0) + write_fatal (); + if ((tifd = open (TMPINNAME, O_RDONLY)) < 0) + pfatal ("can't reopen file %s", TMPINNAME); } -/* Fetch a line from the input file, \n terminated, not necessarily \0. */ +/* Fetch a line from the input file. */ -char * -ifetch(line,whichbuf) -Reg1 LINENUM line; +char const * +ifetch (line, whichbuf, psize) +register LINENUM line; int whichbuf; /* ignored when file in memory */ +size_t *psize; { - if (line < 1 || line > input_lines) + register char const *q; + register char const *p; + + if (line < 1 || line > input_lines) { + *psize = 0; return ""; - if (using_plan_a) - return i_ptr[line]; - else { + } + if (using_plan_a) { + p = i_ptr[line]; + *psize = i_ptr[line + 1] - p; + return p; + } else { LINENUM offline = line % lines_per_buf; LINENUM baseline = line - offline; @@ -335,35 +483,19 @@ int whichbuf; /* ignored when file in memory */ whichbuf = 1; else { tiline[whichbuf] = baseline; -#ifndef lint /* complains of long accuracy */ - Lseek(tifd, (long)baseline / lines_per_buf * BUFFERSIZE, 0); -#endif - if (read(tifd, tibuf[whichbuf], BUFFERSIZE) < 0) - pfatal2("error reading tmp file %s", TMPINNAME); + if (lseek (tifd, (off_t) (baseline/lines_per_buf * tibufsize), + SEEK_SET) == -1 + || read (tifd, tibuf[whichbuf], tibufsize) < 0) + read_fatal (); } - return tibuf[whichbuf] + (tireclen*offline); - } -} - -/* True if the string argument contains the revision number we want. */ - -bool -rev_in_string(string) -char *string; -{ - Reg1 char *s; - Reg2 int patlen; - - if (revision == Nullch) - return TRUE; - patlen = strlen(revision); - if (strnEQ(string,revision,patlen) && isspace(string[patlen])) - return TRUE; - for (s = string; *s; s++) { - if (isspace(*s) && strnEQ(s+1, revision, patlen) && - isspace(s[patlen+1] )) { - return TRUE; + p = tibuf[whichbuf] + (tireclen*offline); + if (line == input_lines) + *psize = last_line_size; + else { + for (q = p; *q++ != '\n'; ) + continue; + *psize = q - p; } + return p; } - return FALSE; } |