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