xref: /plan9/sys/src/ape/cmd/patch/inp.c (revision 0b459c2cb92b7c9d88818e9a2f72e678e5bc4553)
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