diff options
Diffstat (limited to 'inp.c')
-rw-r--r-- | inp.c | 462 |
1 files changed, 462 insertions, 0 deletions
diff --git a/inp.c b/inp.c new file mode 100644 index 000000000000..880b90e5dc7c --- /dev/null +++ b/inp.c @@ -0,0 +1,462 @@ +/* inputting files to be patched */ + +/* $Id: inp.c,v 1.18 1997/07/21 17:59:46 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> + +/* Input-file-with-indexable-lines abstract type */ + +static char *i_buffer; /* plan A buffer */ +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 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)); +static void too_many_lines PARAMS ((char const *)) __attribute__((noreturn)); + +/* New patch--prepare to edit another file. */ + +void +re_input() +{ + if (using_plan_a) { + free (i_buffer); + free (i_ptr); + } + else { + close (tifd); + tifd = -1; + free(tibuf[0]); + tibuf[0] = 0; + tiline[0] = tiline[1] = -1; + tireclen = 0; + } +} + +/* Construct the line index, somehow or other. */ + +void +scan_input(filename) +char *filename; +{ + using_plan_a = ! (debug & 16) && plan_a (filename); + if (!using_plan_a) + plan_b(filename); + 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"); + } +} + + +static void +too_many_lines (filename) + char const *filename; +{ + fatal ("File `%s' has too many lines.", filename); +} + + +void +get_input_file (filename, outname) + char const *filename; + char const *outname; +{ + int elsewhere = strcmp (filename, outname); + char const *cs; + char *diffbuf; + char *getbuf; + + if (inerrno == -1) + inerrno = stat (inname, &instat) == 0 ? 0 : errno; + + /* Perhaps look for RCS or SCCS versions. */ + if (patch_get + && invc != 0 + && (inerrno + || (! elsewhere + && (/* No one can write to it. */ + (instat.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) == 0 + /* Only the owner (who's not me) can write to it. */ + || ((instat.st_mode & (S_IWGRP|S_IWOTH)) == 0 + && instat.st_uid != geteuid ())))) + && (invc = !! (cs = (version_controller + (filename, elsewhere, + inerrno ? (struct stat *) 0 : &instat, + &getbuf, &diffbuf))))) { + + if (!inerrno) { + if (!elsewhere + && (instat.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) != 0) + /* Somebody can write to it. */ + 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 (verbosity == VERBOSE) + say ("Comparing file `%s' to default %s version...\n", + filename, cs); + if (systemic (diffbuf) != 0) + { + say ("warning: patching file `%s', which does not match default %s version\n", + filename, cs); + cs = 0; + } + } + + if (cs && version_get (filename, cs, ! inerrno, elsewhere, getbuf, + &instat)) + inerrno = 0; + + free (getbuf); + free (diffbuf); + + } else if (inerrno && !pch_says_nonexistent (reverse)) + { + errno = inerrno; + pfatal ("can't find file `%s'", filename); + } + + if (inerrno) + { + instat.st_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH; + instat.st_size = 0; + } + else if (! S_ISREG (instat.st_mode)) + fatal ("`%s' is not a regular file -- can't patch", filename); +} + + +/* 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; + + /* Fail if the file size doesn't fit in a size_t, + or if storage isn't available. */ + if (! (size == instat.st_size + && (buffer = malloc (size ? size : (size_t) 1)))) + return FALSE; + + /* Read the input file, but don't bother reading it if it's empty. + When creating files, the files do not actually exist. */ + if (size) + { + int ifd = open (filename, O_RDONLY|binary_transput); + size_t buffered = 0, n; + if (ifd < 0) + pfatal ("can't open file `%s'", filename); + + while (size - buffered != 0) + { + n = read (ifd, buffer + buffered, size - buffered); + if (n == 0) + { + /* Some non-POSIX hosts exaggerate st_size in text mode; + or the file may have shrunk! */ + size = buffered; + break; + } + if (n == (size_t) -1) + { + /* Perhaps size is too large for this host. */ + close (ifd); + free (buffer); + return FALSE; + } + buffered += n; + } + + if (close (ifd) != 0) + read_fatal (); + } + + /* Scan the buffer and build array of pointers to lines. */ + lim = buffer + size; + iline = 3; /* 1 unused, 1 for SOF, 1 for EOF if last line is incomplete */ + for (s = buffer; (s = (char *) memchr (s, '\n', lim - s)); s++) + if (++iline < 0) + too_many_lines (filename); + if (! (iline == (size_t) iline + && (size_t) iline * sizeof *ptr / sizeof *ptr == (size_t) iline + && (ptr = (char const **) malloc ((size_t) iline * sizeof *ptr)))) + { + free (buffer); + return FALSE; + } + iline = 0; + for (s = buffer; ; s++) + { + ptr[++iline] = s; + if (! (s = (char *) memchr (s, '\n', lim - s))) + break; + } + if (size && lim[-1] != '\n') + ptr[++iline] = lim; + input_lines = iline - 1; + + if (revision) + { + char const *rev = revision; + int rev0 = rev[0]; + int found_revision = 0; + size_t revlen = strlen (rev); + + 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; + } + } + + report_revision (found_revision); + } + + /* Plan A will work. */ + i_buffer = buffer; + i_ptr = ptr; + return TRUE; +} + +/* Keep (virtually) nothing in memory. */ + +static void +plan_b(filename) + char const *filename; +{ + 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 = 1; + + if (instat.st_size == 0) + filename = NULL_DEVICE; + if (! (ifp = fopen (filename, binary_transput ? "rb" : "r"))) + pfatal ("can't open file `%s'", filename); + tifd = create_file (TMPINNAME, O_RDWR | O_BINARY, (mode_t) 0); + 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 (++line < 0) + too_many_lines (filename); + 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; + } + } + + 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; + } + } + } + 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; +} + +/* Fetch a line from the input file. */ + +char const * +ifetch (line, whichbuf, psize) +register LINENUM line; +int whichbuf; /* ignored when file in memory */ +size_t *psize; +{ + register char const *q; + register char const *p; + + if (line < 1 || line > input_lines) { + *psize = 0; + return ""; + } + 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; + + if (tiline[0] == baseline) + whichbuf = 0; + else if (tiline[1] == baseline) + whichbuf = 1; + else { + tiline[whichbuf] = baseline; + if (lseek (tifd, (off_t) (baseline/lines_per_buf * tibufsize), + SEEK_SET) == -1 + || read (tifd, tibuf[whichbuf], tibufsize) < 0) + read_fatal (); + } + 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; + } +} |