Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
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
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
132
too_many_lines (filename)
133
     char const *filename;
134
{
135
  fatal ("File `%s' has too many lines.", filename);
136
}
137
 
138
 
139
void
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
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
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 *
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
}