source: trunk/third/patch/inp.c @ 11119

Revision 11119, 11.0 KB checked in by ghudson, 27 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r11118, which included commits to RCS files with non-trunk default branches.
Line 
1/* inputting files to be patched */
2
3/* $Id: inp.c,v 1.1.1.2 1998-02-12 21:43:14 ghudson Exp $ */
4
5/*
6Copyright 1986, 1988 Larry Wall
7Copyright 1991, 1992, 1993, 1997 Free Software Foundation, Inc.
8
9This program is free software; you can redistribute it and/or modify
10it under the terms of the GNU General Public License as published by
11the Free Software Foundation; either version 2, or (at your option)
12any later version.
13
14This program is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17GNU General Public License for more details.
18
19You should have received a copy of the GNU General Public License
20along with this program; see the file COPYING.
21If not, write to the Free Software Foundation,
2259 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
36static char *i_buffer;                  /* plan A buffer */
37static char const **i_ptr;              /* pointers to lines in plan A buffer */
38
39static size_t tibufsize;                /* size of plan b buffers */
40#ifndef TIBUFSIZE_MINIMUM
41#define TIBUFSIZE_MINIMUM (8 * 1024)    /* minimum value for tibufsize */
42#endif
43static int tifd = -1;                   /* plan b virtual string array */
44static char *tibuf[2];                  /* plan b buffers */
45static LINENUM tiline[2] = {-1, -1};    /* 1st line in each buffer */
46static LINENUM lines_per_buf;           /* how many lines per buffer */
47static size_t tireclen;                 /* length of records in tmp file */
48static size_t last_line_size;           /* size of last input line */
49
50static bool plan_a PARAMS ((char const *));/* yield FALSE if memory runs out */
51static void plan_b PARAMS ((char const *));
52static void report_revision PARAMS ((int));
53static void too_many_lines PARAMS ((char const *)) __attribute__((noreturn));
54
55/* New patch--prepare to edit another file. */
56
57void
58re_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
76void
77scan_input(filename)
78char *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
101static void
102report_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
131static void
132too_many_lines (filename)
133     char const *filename;
134{
135  fatal ("File `%s' has too many lines.", filename);
136}
137
138
139void
140get_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
211static bool
212plan_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
317static void
318plan_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
420char const *
421ifetch (line, whichbuf, psize)
422register LINENUM line;
423int whichbuf;                           /* ignored when file in memory */
424size_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}
Note: See TracBrowser for help on using the repository browser.