Annotation of src/usr.bin/patch/inp.c, Revision 1.23
1.23 ! otto 1: /* $OpenBSD: inp.c,v 1.22 2003/08/01 20:30:48 otto Exp $ */
1.2 niklas 2:
1.1 deraadt 3: #ifndef lint
1.23 ! otto 4: static const char rcsid[] = "$OpenBSD: inp.c,v 1.22 2003/08/01 20:30:48 otto Exp $";
1.17 deraadt 5: #endif /* not lint */
1.1 deraadt 6:
1.16 otto 7: #include <sys/types.h>
8: #include <sys/file.h>
9: #include <sys/stat.h>
1.23 ! otto 10: #include <sys/mman.h>
1.16 otto 11:
12: #include <ctype.h>
13: #include <libgen.h>
14: #include <limits.h>
1.23 ! otto 15: #include <stddef.h>
1.19 otto 16: #include <stdio.h>
1.16 otto 17: #include <stdlib.h>
18: #include <string.h>
19: #include <unistd.h>
20:
1.1 deraadt 21: #include "common.h"
22: #include "util.h"
23: #include "pch.h"
24: #include "inp.h"
25:
1.7 espie 26:
1.1 deraadt 27: /* Input-file-with-indexable-lines abstract type */
28:
1.12 deraadt 29: static off_t i_size; /* size of the input file */
30: static char *i_womp; /* plan a buffer for entire file */
31: static char **i_ptr; /* pointers to lines in i_womp */
32:
33: static int tifd = -1; /* plan b virtual string array */
34: static char *tibuf[2]; /* plan b buffers */
35: static LINENUM tiline[2] = {-1, -1}; /* 1st line in each buffer */
36: static LINENUM lines_per_buf; /* how many lines per buffer */
37: static int tireclen; /* length of records in tmp file */
1.1 deraadt 38:
1.19 otto 39: static bool rev_in_string(const char *);
40:
41: /* returns false if insufficient memory */
42: static bool plan_a(const char *);
43:
44: static void plan_b(const char *);
1.11 deraadt 45:
1.1 deraadt 46: /* New patch--prepare to edit another file. */
47:
48: void
1.11 deraadt 49: re_input(void)
1.1 deraadt 50: {
1.12 deraadt 51: if (using_plan_a) {
52: i_size = 0;
1.16 otto 53: free(i_ptr);
54: i_ptr = NULL;
1.23 ! otto 55: if (i_womp != NULL) {
! 56: munmap(i_womp, i_size);
! 57: i_womp = NULL;
! 58: }
1.12 deraadt 59: } else {
1.22 otto 60: using_plan_a = true; /* maybe the next one is smaller */
1.12 deraadt 61: close(tifd);
62: tifd = -1;
63: free(tibuf[0]);
64: free(tibuf[1]);
1.16 otto 65: tibuf[0] = tibuf[1] = NULL;
1.12 deraadt 66: tiline[0] = tiline[1] = -1;
67: tireclen = 0;
68: }
1.1 deraadt 69: }
70:
71: /* Constuct the line index, somehow or other. */
72:
73: void
1.19 otto 74: scan_input(const char *filename)
1.1 deraadt 75: {
1.12 deraadt 76: if (!plan_a(filename))
77: plan_b(filename);
78: if (verbose) {
79: say("Patching file %s using Plan %s...\n", filename,
80: (using_plan_a ? "A" : "B"));
81: }
1.1 deraadt 82: }
83:
84: /* Try keeping everything in memory. */
85:
1.16 otto 86: static bool
1.19 otto 87: plan_a(const char *filename)
1.1 deraadt 88: {
1.19 otto 89: int ifd, statfailed;
1.23 ! otto 90: char *p, *s, lbuf[MAXLINELEN];
1.19 otto 91: LINENUM iline;
1.20 deraadt 92: struct stat filestat;
1.23 ! otto 93: off_t i;
! 94: ptrdiff_t sz;
! 95:
! 96: #ifdef DEBUGGING
! 97: if (debug & 8)
! 98: return false;
! 99: #endif
1.1 deraadt 100:
1.19 otto 101: if (filename == NULL || *filename == '\0')
1.22 otto 102: return false;
1.8 millert 103:
1.1 deraadt 104: statfailed = stat(filename, &filestat);
1.12 deraadt 105: if (statfailed && ok_to_create_file) {
1.1 deraadt 106: if (verbose)
1.12 deraadt 107: say("(Creating file %s...)\n", filename);
108:
109: /*
110: * in check_patch case, we still display `Creating file' even
111: * though we're not. The rule is that -C should be as similar
112: * to normal patch behavior as possible
113: */
114: if (check_only)
1.22 otto 115: return true;
116: makedirs(filename, true);
1.12 deraadt 117: close(creat(filename, 0666));
118: statfailed = stat(filename, &filestat);
119: }
120: if (statfailed && check_only)
121: fatal("%s not found, -C mode, can't probe further\n", filename);
122: /* For nonexistent or read-only files, look for RCS or SCCS versions. */
123: if (statfailed ||
124: /* No one can write to it. */
125: (filestat.st_mode & 0222) == 0 ||
126: /* I can't write to it. */
1.19 otto 127: ((filestat.st_mode & 0022) == 0 && filestat.st_uid != getuid())) {
1.16 otto 128: char *cs = NULL, *filebase, *filedir;
1.12 deraadt 129: struct stat cstat;
130:
131: filebase = basename(filename);
132: filedir = dirname(filename);
133:
134: /* Leave room in lbuf for the diff command. */
135: s = lbuf + 20;
136:
137: #define try(f, a1, a2, a3) \
138: (snprintf(s, sizeof lbuf - 20, f, a1, a2, a3), stat(s, &cstat) == 0)
139:
140: if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) ||
141: try("%s/RCS/%s%s", filedir, filebase, "") ||
142: try("%s/%s%s", filedir, filebase, RCSSUFFIX)) {
143: snprintf(buf, sizeof buf, CHECKOUT, filename);
144: snprintf(lbuf, sizeof lbuf, RCSDIFF, filename);
145: cs = "RCS";
146: } else if (try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) ||
147: try("%s/%s%s", filedir, SCCSPREFIX, filebase)) {
148: snprintf(buf, sizeof buf, GET, s);
149: snprintf(lbuf, sizeof lbuf, SCCSDIFF, s, filename);
150: cs = "SCCS";
151: } else if (statfailed)
152: fatal("can't find %s\n", filename);
153: /*
154: * else we can't write to it but it's not under a version
155: * control system, so just proceed.
156: */
157: if (cs) {
158: if (!statfailed) {
159: if ((filestat.st_mode & 0222) != 0)
160: /* The owner can write to it. */
161: fatal("file %s seems to be locked "
162: "by somebody else under %s\n",
163: filename, cs);
164: /*
165: * It might be checked out unlocked. See if
166: * it's safe to check out the default version
167: * locked.
168: */
169: if (verbose)
170: say("Comparing file %s to default "
171: "%s version...\n",
172: filename, cs);
173: if (system(lbuf))
174: fatal("can't check out file %s: "
175: "differs from default %s version\n",
176: filename, cs);
177: }
178: if (verbose)
179: say("Checking out file %s from %s...\n",
180: filename, cs);
181: if (system(buf) || stat(filename, &filestat))
182: fatal("can't check out file %s from %s\n",
183: filename, cs);
184: }
185: }
186: filemode = filestat.st_mode;
187: if (!S_ISREG(filemode))
188: fatal("%s is not a normal file--can't patch\n", filename);
189: i_size = filestat.st_size;
190: if (out_of_mem) {
191: set_hunkmax(); /* make sure dynamic arrays are allocated */
1.22 otto 192: out_of_mem = false;
193: return false; /* force plan b because plan a bombed */
1.12 deraadt 194: }
1.23 ! otto 195: if (i_size > SIZE_MAX) {
! 196: say("block too large to mmap\n");
! 197: return false;
! 198: }
1.12 deraadt 199: if ((ifd = open(filename, O_RDONLY)) < 0)
200: pfatal("can't open file %s", filename);
1.16 otto 201:
1.23 ! otto 202: i_womp = mmap(NULL, i_size, PROT_READ, MAP_FILE, ifd, 0);
! 203: if (i_womp == MAP_FAILED) {
! 204: perror("mmap failed");
! 205: i_womp = NULL;
! 206: return false;
1.12 deraadt 207: }
1.16 otto 208:
1.12 deraadt 209: close(ifd);
210:
211: /* count the lines in the buffer so we know how many pointers we need */
1.23 ! otto 212: iline = 0;
1.12 deraadt 213:
1.23 ! otto 214: /* test for NUL too, to maintain the behavior of the original code */
! 215: for (i = 0; i < i_size && i_womp[i] != '\0'; i++) {
! 216: if (i_womp[i] == '\n')
! 217: iline++;
1.12 deraadt 218: }
1.23 ! otto 219: if (i_size > 0 && i_womp[i_size - 1] != '\n')
! 220: iline++;
! 221:
1.16 otto 222:
1.13 deraadt 223: i_ptr = (char **) malloc((iline + 2) * sizeof(char *));
1.16 otto 224:
1.23 ! otto 225: if (i_ptr == NULL) { /* shucks, it was a near thing */
! 226: munmap(i_womp, i_size);
! 227: i_womp = NULL;
! 228: return false;
! 229: }
! 230:
1.12 deraadt 231: /* now scan the buffer and build pointer array */
232: iline = 1;
233: i_ptr[iline] = i_womp;
1.23 ! otto 234: /* test for NUL too, to maintain the behavior of the original code */
! 235: for (s = i_womp, i = 0; i < i_size && *s != '\0'; s++, i++) {
1.12 deraadt 236: if (*s == '\n')
1.20 deraadt 237: i_ptr[++iline] = s + 1; /* these are NOT NUL terminated */
1.12 deraadt 238: }
1.23 ! otto 239: /* if the last line contains no EOL, append one */
! 240: if (i_size > 0 && i_womp[i_size - 1] != '\n') {
! 241: /* fix last line */
! 242: sz = s - i_ptr[iline];
! 243: p = malloc(sz + 1);
! 244: if (p == NULL) {
! 245: free(i_ptr);
! 246: i_ptr = NULL;
! 247: munmap(i_womp, i_size);
! 248: i_womp = NULL;
! 249: return false;
! 250: }
! 251:
! 252: memcpy(p, i_ptr[iline], sz);
! 253: p[sz] = '\n';
! 254: i_ptr[iline] = p;
! 255: /* count the extra line and make it point to some valid mem */
! 256: i_ptr[++iline] = "";
! 257: }
! 258:
1.12 deraadt 259: input_lines = iline - 1;
1.11 deraadt 260:
1.12 deraadt 261: /* now check for revision, if any */
1.1 deraadt 262:
1.16 otto 263: if (revision != NULL) {
1.12 deraadt 264: if (!rev_in_string(i_womp)) {
265: if (force) {
266: if (verbose)
267: say("Warning: this file doesn't appear "
268: "to be the %s version--patching anyway.\n",
269: revision);
270: } else if (batch) {
271: fatal("this file doesn't appear to be the "
272: "%s version--aborting.\n",
273: revision);
274: } else {
275: ask("This file doesn't appear to be the "
276: "%s version--patch anyway? [n] ",
277: revision);
278: if (*buf != 'y')
279: fatal("aborted\n");
280: }
281: } else if (verbose)
282: say("Good. This file appears to be the %s version.\n",
283: revision);
284: }
1.22 otto 285: return true; /* plan a will work */
1.1 deraadt 286: }
287:
288: /* Keep (virtually) nothing in memory. */
289:
1.16 otto 290: static void
1.19 otto 291: plan_b(const char *filename)
1.1 deraadt 292: {
1.12 deraadt 293: FILE *ifp;
294: int i = 0, maxlen = 1;
1.16 otto 295: bool found_revision = (revision == NULL);
1.12 deraadt 296:
1.22 otto 297: using_plan_a = false;
1.16 otto 298: if ((ifp = fopen(filename, "r")) == NULL)
1.12 deraadt 299: pfatal("can't open file %s", filename);
300: (void) unlink(TMPINNAME);
301: if ((tifd = open(TMPINNAME, O_EXCL | O_CREAT | O_WRONLY, 0666)) < 0)
302: pfatal("can't open file %s", TMPINNAME);
1.16 otto 303: while (fgets(buf, sizeof buf, ifp) != NULL) {
304: if (revision != NULL && !found_revision && rev_in_string(buf))
1.22 otto 305: found_revision = true;
1.12 deraadt 306: if ((i = strlen(buf)) > maxlen)
307: maxlen = i; /* find longest line */
308: }
1.16 otto 309: if (revision != NULL) {
1.12 deraadt 310: if (!found_revision) {
311: if (force) {
312: if (verbose)
313: say("Warning: this file doesn't appear "
314: "to be the %s version--patching anyway.\n",
315: revision);
316: } else if (batch) {
317: fatal("this file doesn't appear to be the "
318: "%s version--aborting.\n",
319: revision);
320: } else {
321: ask("This file doesn't appear to be the %s "
322: "version--patch anyway? [n] ",
323: revision);
324: if (*buf != 'y')
325: fatal("aborted\n");
326: }
327: } else if (verbose)
328: say("Good. This file appears to be the %s version.\n",
329: revision);
330: }
1.16 otto 331: fseek(ifp, 0L, SEEK_SET); /* rewind file */
1.12 deraadt 332: lines_per_buf = BUFFERSIZE / maxlen;
333: tireclen = maxlen;
1.13 deraadt 334: tibuf[0] = malloc(BUFFERSIZE + 1);
1.16 otto 335: if (tibuf[0] == NULL)
1.12 deraadt 336: fatal("out of memory\n");
1.13 deraadt 337: tibuf[1] = malloc(BUFFERSIZE + 1);
1.16 otto 338: if (tibuf[1] == NULL)
1.12 deraadt 339: fatal("out of memory\n");
340: for (i = 1;; i++) {
341: if (!(i % lines_per_buf)) /* new block */
342: if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
343: pfatal("can't write temp file");
344: if (fgets(tibuf[0] + maxlen * (i % lines_per_buf),
1.16 otto 345: maxlen + 1, ifp) == NULL) {
1.12 deraadt 346: input_lines = i - 1;
347: if (i % lines_per_buf)
348: if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
349: pfatal("can't write temp file");
350: break;
351: }
352: }
353: fclose(ifp);
354: close(tifd);
355: if ((tifd = open(TMPINNAME, O_RDONLY)) < 0)
356: pfatal("can't reopen file %s", TMPINNAME);
1.1 deraadt 357: }
358:
1.12 deraadt 359: /*
360: * Fetch a line from the input file, \n terminated, not necessarily \0.
361: */
1.1 deraadt 362: char *
1.12 deraadt 363: ifetch(LINENUM line, int whichbuf)
1.1 deraadt 364: {
1.12 deraadt 365: if (line < 1 || line > input_lines) {
1.21 otto 366: if (warn_on_invalid_line) {
367: say("No such line %ld in input file, ignoring\n", line);
1.22 otto 368: warn_on_invalid_line = false;
1.21 otto 369: }
1.18 otto 370: return NULL;
1.12 deraadt 371: }
372: if (using_plan_a)
373: return i_ptr[line];
1.1 deraadt 374: else {
1.12 deraadt 375: LINENUM offline = line % lines_per_buf;
376: LINENUM baseline = line - offline;
377:
378: if (tiline[0] == baseline)
379: whichbuf = 0;
380: else if (tiline[1] == baseline)
381: whichbuf = 1;
382: else {
383: tiline[whichbuf] = baseline;
1.16 otto 384:
1.12 deraadt 385: lseek(tifd, (off_t) (baseline / lines_per_buf *
1.16 otto 386: BUFFERSIZE), SEEK_SET);
387:
1.12 deraadt 388: if (read(tifd, tibuf[whichbuf], BUFFERSIZE) < 0)
389: pfatal("error reading tmp file %s", TMPINNAME);
390: }
391: return tibuf[whichbuf] + (tireclen * offline);
1.1 deraadt 392: }
393: }
394:
1.12 deraadt 395: /*
396: * True if the string argument contains the revision number we want.
397: */
1.16 otto 398: static bool
1.19 otto 399: rev_in_string(const char *string)
1.1 deraadt 400: {
1.19 otto 401: const char *s;
402: int patlen;
1.1 deraadt 403:
1.16 otto 404: if (revision == NULL)
1.22 otto 405: return true;
1.12 deraadt 406: patlen = strlen(revision);
407: if (strnEQ(string, revision, patlen) && isspace(string[patlen]))
1.22 otto 408: return true;
1.12 deraadt 409: for (s = string; *s; s++) {
410: if (isspace(*s) && strnEQ(s + 1, revision, patlen) &&
411: isspace(s[patlen + 1])) {
1.22 otto 412: return true;
1.12 deraadt 413: }
1.1 deraadt 414: }
1.22 otto 415: return false;
1.1 deraadt 416: }