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