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