1 /* inputting files to be patched */
2
3 /* $Id: inp.c,v 1.18 1997/07/21 17:59:46 eggert Exp $ */
4
5 /*
6 Copyright 1986, 1988 Larry Wall
7 Copyright 1991, 1992, 1993, 1997 Free Software Foundation, Inc.
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2, or (at your option)
12 any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; see the file COPYING.
21 If not, write to the Free Software Foundation,
22 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 */
24
25 #define XTERN extern
26 #include <common.h>
27 #include <backupfile.h>
28 #include <pch.h>
29 #include <util.h>
30 #undef XTERN
31 #define XTERN
32 #include <inp.h>
33
34 /* Input-file-with-indexable-lines abstract type */
35
36 static char *i_buffer; /* plan A buffer */
37 static char const **i_ptr; /* pointers to lines in plan A buffer */
38
39 static size_t tibufsize; /* size of plan b buffers */
40 #ifndef TIBUFSIZE_MINIMUM
41 #define TIBUFSIZE_MINIMUM (8 * 1024) /* minimum value for tibufsize */
42 #endif
43 static int tifd = -1; /* plan b virtual string array */
44 static char *tibuf[2]; /* plan b buffers */
45 static LINENUM tiline[2] = {-1, -1}; /* 1st line in each buffer */
46 static LINENUM lines_per_buf; /* how many lines per buffer */
47 static size_t tireclen; /* length of records in tmp file */
48 static size_t last_line_size; /* size of last input line */
49
50 static bool plan_a PARAMS ((char const *));/* yield FALSE if memory runs out */
51 static void plan_b PARAMS ((char const *));
52 static void report_revision PARAMS ((int));
53 static void too_many_lines PARAMS ((char const *)) __attribute__((noreturn));
54
55 /* New patch--prepare to edit another file. */
56
57 void
re_input()58 re_input()
59 {
60 if (using_plan_a) {
61 free (i_buffer);
62 free (i_ptr);
63 }
64 else {
65 close (tifd);
66 tifd = -1;
67 free(tibuf[0]);
68 tibuf[0] = 0;
69 tiline[0] = tiline[1] = -1;
70 tireclen = 0;
71 }
72 }
73
74 /* Construct the line index, somehow or other. */
75
76 void
scan_input(filename)77 scan_input(filename)
78 char *filename;
79 {
80 using_plan_a = ! (debug & 16) && plan_a (filename);
81 if (!using_plan_a)
82 plan_b(filename);
83 switch (verbosity)
84 {
85 case SILENT:
86 break;
87
88 case VERBOSE:
89 say ("Patching file `%s' using Plan %s...\n",
90 filename, using_plan_a ? "A" : "B");
91 break;
92
93 case DEFAULT_VERBOSITY:
94 say ("patching file `%s'\n", filename);
95 break;
96 }
97 }
98
99 /* Report whether a desired revision was found. */
100
101 static void
report_revision(found_revision)102 report_revision (found_revision)
103 int found_revision;
104 {
105 if (found_revision)
106 {
107 if (verbosity == VERBOSE)
108 say ("Good. This file appears to be the %s version.\n", revision);
109 }
110 else if (force)
111 {
112 if (verbosity != SILENT)
113 say ("Warning: this file doesn't appear to be the %s version -- patching anyway.\n",
114 revision);
115 }
116 else if (batch)
117 {
118 fatal ("This file doesn't appear to be the %s version -- aborting.",
119 revision);
120 }
121 else
122 {
123 ask ("This file doesn't appear to be the %s version -- patch anyway? [n] ",
124 revision);
125 if (*buf != 'y')
126 fatal ("aborted");
127 }
128 }
129
130
131 static void
too_many_lines(filename)132 too_many_lines (filename)
133 char const *filename;
134 {
135 fatal ("File `%s' has too many lines.", filename);
136 }
137
138
139 void
get_input_file(filename,outname)140 get_input_file (filename, outname)
141 char const *filename;
142 char const *outname;
143 {
144 int elsewhere = strcmp (filename, outname);
145 char const *cs;
146 char *diffbuf;
147 char *getbuf;
148
149 if (inerrno == -1)
150 inerrno = stat (inname, &instat) == 0 ? 0 : errno;
151
152 /* Perhaps look for RCS or SCCS versions. */
153 if (patch_get
154 && invc != 0
155 && (inerrno
156 || (! elsewhere
157 && (/* No one can write to it. */
158 (instat.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) == 0
159 /* Only the owner (who's not me) can write to it. */
160 || ((instat.st_mode & (S_IWGRP|S_IWOTH)) == 0
161 && instat.st_uid != geteuid ()))))
162 && (invc = !! (cs = (version_controller
163 (filename, elsewhere,
164 inerrno ? (struct stat *) 0 : &instat,
165 &getbuf, &diffbuf))))) {
166
167 if (!inerrno) {
168 if (!elsewhere
169 && (instat.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) != 0)
170 /* Somebody can write to it. */
171 fatal ("file `%s' seems to be locked by somebody else under %s",
172 filename, cs);
173 /* It might be checked out unlocked. See if it's safe to
174 check out the default version locked. */
175 if (verbosity == VERBOSE)
176 say ("Comparing file `%s' to default %s version...\n",
177 filename, cs);
178 if (systemic (diffbuf) != 0)
179 {
180 say ("warning: patching file `%s', which does not match default %s version\n",
181 filename, cs);
182 cs = 0;
183 }
184 }
185
186 if (cs && version_get (filename, cs, ! inerrno, elsewhere, getbuf,
187 &instat))
188 inerrno = 0;
189
190 free (getbuf);
191 free (diffbuf);
192
193 } else if (inerrno && !pch_says_nonexistent (reverse))
194 {
195 errno = inerrno;
196 pfatal ("can't find file `%s'", filename);
197 }
198
199 if (inerrno)
200 {
201 instat.st_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
202 instat.st_size = 0;
203 }
204 else if (! S_ISREG (instat.st_mode))
205 fatal ("`%s' is not a regular file -- can't patch", filename);
206 }
207
208
209 /* Try keeping everything in memory. */
210
211 static bool
plan_a(filename)212 plan_a(filename)
213 char const *filename;
214 {
215 register char const *s;
216 register char const *lim;
217 register char const **ptr;
218 register char *buffer;
219 register LINENUM iline;
220 size_t size = instat.st_size;
221
222 /* Fail if the file size doesn't fit in a size_t,
223 or if storage isn't available. */
224 if (! (size == instat.st_size
225 && (buffer = malloc (size ? size : (size_t) 1))))
226 return FALSE;
227
228 /* Read the input file, but don't bother reading it if it's empty.
229 When creating files, the files do not actually exist. */
230 if (size)
231 {
232 int ifd = open (filename, O_RDONLY|binary_transput);
233 size_t buffered = 0, n;
234 if (ifd < 0)
235 pfatal ("can't open file `%s'", filename);
236
237 while (size - buffered != 0)
238 {
239 n = read (ifd, buffer + buffered, size - buffered);
240 if (n == 0)
241 {
242 /* Some non-POSIX hosts exaggerate st_size in text mode;
243 or the file may have shrunk! */
244 size = buffered;
245 break;
246 }
247 if (n == (size_t) -1)
248 {
249 /* Perhaps size is too large for this host. */
250 close (ifd);
251 free (buffer);
252 return FALSE;
253 }
254 buffered += n;
255 }
256
257 if (close (ifd) != 0)
258 read_fatal ();
259 }
260
261 /* Scan the buffer and build array of pointers to lines. */
262 lim = buffer + size;
263 iline = 3; /* 1 unused, 1 for SOF, 1 for EOF if last line is incomplete */
264 for (s = buffer; (s = (char *) memchr (s, '\n', lim - s)); s++)
265 if (++iline < 0)
266 too_many_lines (filename);
267 if (! (iline == (size_t) iline
268 && (size_t) iline * sizeof *ptr / sizeof *ptr == (size_t) iline
269 && (ptr = (char const **) malloc ((size_t) iline * sizeof *ptr))))
270 {
271 free (buffer);
272 return FALSE;
273 }
274 iline = 0;
275 for (s = buffer; ; s++)
276 {
277 ptr[++iline] = s;
278 if (! (s = (char *) memchr (s, '\n', lim - s)))
279 break;
280 }
281 if (size && lim[-1] != '\n')
282 ptr[++iline] = lim;
283 input_lines = iline - 1;
284
285 if (revision)
286 {
287 char const *rev = revision;
288 int rev0 = rev[0];
289 int found_revision = 0;
290 size_t revlen = strlen (rev);
291
292 if (revlen <= size)
293 {
294 char const *limrev = lim - revlen;
295
296 for (s = buffer; (s = (char *) memchr (s, rev0, limrev - s)); s++)
297 if (memcmp (s, rev, revlen) == 0
298 && (s == buffer || ISSPACE ((unsigned char) s[-1]))
299 && (s + 1 == limrev || ISSPACE ((unsigned char) s[revlen])))
300 {
301 found_revision = 1;
302 break;
303 }
304 }
305
306 report_revision (found_revision);
307 }
308
309 /* Plan A will work. */
310 i_buffer = buffer;
311 i_ptr = ptr;
312 return TRUE;
313 }
314
315 /* Keep (virtually) nothing in memory. */
316
317 static void
plan_b(filename)318 plan_b(filename)
319 char const *filename;
320 {
321 register FILE *ifp;
322 register int c;
323 register size_t len;
324 register size_t maxlen;
325 register int found_revision;
326 register size_t i;
327 register char const *rev;
328 register size_t revlen;
329 register LINENUM line = 1;
330
331 if (instat.st_size == 0)
332 filename = NULL_DEVICE;
333 if (! (ifp = fopen (filename, binary_transput ? "rb" : "r")))
334 pfatal ("can't open file `%s'", filename);
335 tifd = create_file (TMPINNAME, O_RDWR | O_BINARY, (mode_t) 0);
336 i = 0;
337 len = 0;
338 maxlen = 1;
339 rev = revision;
340 found_revision = !rev;
341 revlen = rev ? strlen (rev) : 0;
342
343 while ((c = getc (ifp)) != EOF)
344 {
345 len++;
346
347 if (c == '\n')
348 {
349 if (++line < 0)
350 too_many_lines (filename);
351 if (maxlen < len)
352 maxlen = len;
353 len = 0;
354 }
355
356 if (!found_revision)
357 {
358 if (i == revlen)
359 {
360 found_revision = ISSPACE ((unsigned char) c);
361 i = (size_t) -1;
362 }
363 else if (i != (size_t) -1)
364 i = rev[i]==c ? i + 1 : (size_t) -1;
365
366 if (i == (size_t) -1 && ISSPACE ((unsigned char) c))
367 i = 0;
368 }
369 }
370
371 if (revision)
372 report_revision (found_revision);
373 Fseek (ifp, (off_t) 0, SEEK_SET); /* rewind file */
374 for (tibufsize = TIBUFSIZE_MINIMUM; tibufsize < maxlen; tibufsize <<= 1)
375 continue;
376 lines_per_buf = tibufsize / maxlen;
377 tireclen = maxlen;
378 tibuf[0] = xmalloc (2 * tibufsize);
379 tibuf[1] = tibuf[0] + tibufsize;
380
381 for (line = 1; ; line++)
382 {
383 char *p = tibuf[0] + maxlen * (line % lines_per_buf);
384 char const *p0 = p;
385 if (! (line % lines_per_buf)) /* new block */
386 if (write (tifd, tibuf[0], tibufsize) != tibufsize)
387 write_fatal ();
388 if ((c = getc (ifp)) == EOF)
389 break;
390
391 for (;;)
392 {
393 *p++ = c;
394 if (c == '\n')
395 {
396 last_line_size = p - p0;
397 break;
398 }
399
400 if ((c = getc (ifp)) == EOF)
401 {
402 last_line_size = p - p0;
403 line++;
404 goto EOF_reached;
405 }
406 }
407 }
408 EOF_reached:
409 if (ferror (ifp) || fclose (ifp) != 0)
410 read_fatal ();
411
412 if (line % lines_per_buf != 0)
413 if (write (tifd, tibuf[0], tibufsize) != tibufsize)
414 write_fatal ();
415 input_lines = line - 1;
416 }
417
418 /* Fetch a line from the input file. */
419
420 char const *
ifetch(line,whichbuf,psize)421 ifetch (line, whichbuf, psize)
422 register LINENUM line;
423 int whichbuf; /* ignored when file in memory */
424 size_t *psize;
425 {
426 register char const *q;
427 register char const *p;
428
429 if (line < 1 || line > input_lines) {
430 *psize = 0;
431 return "";
432 }
433 if (using_plan_a) {
434 p = i_ptr[line];
435 *psize = i_ptr[line + 1] - p;
436 return p;
437 } else {
438 LINENUM offline = line % lines_per_buf;
439 LINENUM baseline = line - offline;
440
441 if (tiline[0] == baseline)
442 whichbuf = 0;
443 else if (tiline[1] == baseline)
444 whichbuf = 1;
445 else {
446 tiline[whichbuf] = baseline;
447 if (lseek (tifd, (off_t) (baseline/lines_per_buf * tibufsize),
448 SEEK_SET) == -1
449 || read (tifd, tibuf[whichbuf], tibufsize) < 0)
450 read_fatal ();
451 }
452 p = tibuf[whichbuf] + (tireclen*offline);
453 if (line == input_lines)
454 *psize = last_line_size;
455 else {
456 for (q = p; *q++ != '\n'; )
457 continue;
458 *psize = q - p;
459 }
460 return p;
461 }
462 }
463