source: vendor/patch/current/inp.c

Last change on this file was 3444, checked in by bird, 18 years ago

patch 2.5.9

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