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