]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - gnu/usr.bin/patch/inp.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / gnu / usr.bin / patch / inp.c
1 /* $FreeBSD$
2  *
3  * $Log: inp.c,v $
4  * Revision 2.0.1.1  88/06/03  15:06:13  lwall
5  * patch10: made a little smarter about sccs files
6  *
7  * Revision 2.0  86/09/17  15:37:02  lwall
8  * Baseline for netwide release.
9  *
10  */
11
12 #include "EXTERN.h"
13 #include "common.h"
14 #include "util.h"
15 #include "pch.h"
16 #include "INTERN.h"
17 #include "inp.h"
18
19 /* Input-file-with-indexable-lines abstract type. */
20
21 static long i_size;                     /* Size of the input file */
22 static char *i_womp;                    /* Plan a buffer for entire file */
23 static char **i_ptr;                    /* Pointers to lines in i_womp */
24
25 static int tifd = -1;                   /* Plan b virtual string array */
26 static char *tibuf[2];                  /* Plan b buffers */
27 static LINENUM tiline[2] = {-1, -1};    /* 1st line in each buffer */
28 static LINENUM lines_per_buf;           /* How many lines per buffer */
29 static int tireclen;                    /* Length of records in tmp file */
30
31 /*
32  * New patch--prepare to edit another file.
33  */
34 void
35 re_input(void)
36 {
37         if (using_plan_a) {
38                 i_size = 0;
39 #ifndef lint
40                 if (i_ptr != Null(char**))
41                         free((char *)i_ptr);
42 #endif
43                 if (i_womp != Nullch)
44                         free(i_womp);
45                 i_womp = Nullch;
46                 i_ptr = Null(char **);
47         } else {
48                 using_plan_a = TRUE;    /* maybe the next one is smaller */
49                 Close(tifd);
50                 tifd = -1;
51                 free(tibuf[0]);
52                 free(tibuf[1]);
53                 tibuf[0] = tibuf[1] = Nullch;
54                 tiline[0] = tiline[1] = -1;
55                 tireclen = 0;
56         }
57 }
58
59 /*
60  * Constuct the line index, somehow or other.
61  */
62 void
63 scan_input(char *filename)
64 {
65         if (!plan_a(filename))
66                 plan_b(filename);
67         if (verbose) {
68                 say3("Patching file %s using Plan %s...\n", filename,
69                     (using_plan_a ? "A" : "B") );
70         }
71 }
72
73 /*
74  * Try keeping everything in memory.
75  */
76 bool
77 plan_a(char *filename)
78 {
79         int ifd, statfailed;
80         Reg1 char *s;
81         Reg2 LINENUM iline;
82         char lbuf[INITLINELEN];
83         int output_elsewhere = strcmp(filename, outname);
84         extern int check_patch;
85
86         statfailed = stat(filename, &filestat);
87         if (statfailed && ok_to_create_file) {
88                 if (verbose)
89                         say2("(Creating file %s...)\n",filename);
90                 if (check_patch)
91                         return TRUE;
92                 makedirs(filename, TRUE);
93                 close(creat(filename, 0666));
94                 statfailed = stat(filename, &filestat);
95         }
96         if (statfailed && check_patch) {
97                 fatal2("%s not found and in check_patch mode.", filename);
98         }
99         /*
100          * For nonexistent or read-only files, look for RCS or SCCS
101          * versions.
102          */
103         if (statfailed ||
104             (! output_elsewhere &&
105             (/* No one can write to it. */
106             (filestat.st_mode & 0222) == 0 ||
107             /* I can't write to it. */
108             ((filestat.st_mode & 0022) == 0 && filestat.st_uid != myuid)))) {
109                 struct stat cstat;
110                 char *cs = Nullch;
111                 char *filebase;
112                 int pathlen;
113
114                 filebase = basename(filename);
115                 pathlen = filebase - filename;
116
117                 /*
118                  * Put any leading path into `s'.
119                  * Leave room in lbuf for the diff command.
120                  */
121                 s = lbuf + 20;
122                 strncpy(s, filename, pathlen);
123
124 #define try(f,a1,a2) (Sprintf(s + pathlen, f, a1, a2), stat(s, &cstat) == 0)
125                 if ((try("RCS/%s%s", filebase, RCSSUFFIX) ||
126                     try("RCS/%s%s", filebase, "") ||
127                     try("%s%s", filebase, RCSSUFFIX)) &&
128                     /*
129                      * Check that RCS file is not working file.
130                      * Some hosts don't report file name length errors.
131                      */
132                     (statfailed ||
133                     ((filestat.st_dev ^ cstat.st_dev) |
134                     (filestat.st_ino ^ cstat.st_ino)))) {
135                         Sprintf(buf, output_elsewhere?CHECKOUT:CHECKOUT_LOCKED, filename);
136                         Sprintf(lbuf, RCSDIFF, filename);
137                         cs = "RCS";
138                 } else if (try("SCCS/%s%s", SCCSPREFIX, filebase) ||
139                     try("%s%s", SCCSPREFIX, filebase)) {
140                         Sprintf(buf, output_elsewhere?GET:GET_LOCKED, s);
141                         Sprintf(lbuf, SCCSDIFF, s, filename);
142                         cs = "SCCS";
143                 } else if (statfailed)
144                         fatal2("can't find %s\n", filename);
145                 /*
146                  * else we can't write to it but it's not under a version
147                  * control system, so just proceed.
148                  */
149                 if (cs) {
150                         if (!statfailed) {
151                                 if ((filestat.st_mode & 0222) != 0)
152                                         /* The owner can write to it.  */
153                                         fatal3(
154 "file %s seems to be locked by somebody else under %s\n",
155                                             filename, cs);
156                                 /*
157                                  * It might be checked out unlocked.  See if
158                                  * it's safe to check out the default version
159                                  * locked.
160                                  */
161                                 if (verbose)
162                                         say3(
163 "Comparing file %s to default %s version...\n",
164                                             filename, cs);
165                                 if (system(lbuf))
166                                         fatal3(
167 "can't check out file %s: differs from default %s version\n",
168                                             filename, cs);
169                         }
170                         if (verbose)
171                                 say3("Checking out file %s from %s...\n", filename, cs);
172                         if (system(buf) || stat(filename, &filestat))
173                                 fatal3("can't check out file %s from %s\n",
174                                     filename, cs);
175                 }
176         }
177         filemode = filestat.st_mode;
178         if (!S_ISREG(filemode))
179                 fatal2("%s is not a normal file--can't patch\n", filename);
180         i_size = filestat.st_size;
181         if (out_of_mem) {
182                 set_hunkmax();  /* make sure dynamic arrays are allocated */
183                 out_of_mem = FALSE;
184                 return FALSE;   /* force plan b because plan a bombed */
185         }
186 #ifdef lint
187         i_womp = Nullch;
188 #else
189         /*
190          * Lint says this may alloc less than i_size,
191          * but that's okay, I think.
192          */
193         i_womp = malloc((MEM)(i_size + 2));
194 #endif
195         if (i_womp == Nullch)
196                 return FALSE;
197         if ((ifd = open(filename, 0)) < 0)
198                 pfatal2("can't open file %s", filename);
199 #ifndef lint
200         if (read(ifd, i_womp, (int)i_size) != i_size) {
201                 /*
202                  * This probably means i_size > 15 or 16 bits worth.  At
203                  * this point it doesn't matter if i_womp was undersized.
204                  */
205                 Close(ifd);
206                 free(i_womp);
207                 return FALSE;
208         }
209 #endif
210         Close(ifd);
211         if (i_size && i_womp[i_size - 1] != '\n')
212                 i_womp[i_size++] = '\n';
213         i_womp[i_size] = '\0';
214
215         /*
216          * Count the lines in the buffer so we know how many pointers we
217          * need.
218          */
219         iline = 0;
220         for (s = i_womp; *s; s++) {
221                 if (*s == '\n')
222                         iline++;
223         }
224 #ifdef lint
225         i_ptr = Null(char**);
226 #else
227         i_ptr = (char **)malloc((MEM)((iline + 2) * sizeof(char *)));
228 #endif
229         if (i_ptr == Null(char **)) {   /* shucks, it was a near thing */
230                 free((char *)i_womp);
231                 return FALSE;
232         }
233
234         /* Now scan the buffer and build pointer array. */
235         iline = 1;
236         i_ptr[iline] = i_womp;
237         for (s = i_womp; *s; s++) {
238                 if (*s == '\n') {
239                         /* These are NOT null terminated. */
240                         i_ptr[++iline] = s + 1;
241                 }
242         }
243         input_lines = iline - 1;
244
245         /* Now check for revision, if any. */
246         if (revision != Nullch) {
247                 if (!rev_in_string(i_womp)) {
248                         if (force) {
249                                 if (verbose)
250                                     say2(
251 "Warning: this file doesn't appear to be the %s version--patching anyway.\n",
252                                         revision);
253                         } else if (batch) {
254                                 fatal2(
255 "this file doesn't appear to be the %s version--aborting.\n", revision);
256                         } else {
257                                 (void) ask2(
258 "This file doesn't appear to be the %s version--patch anyway? [n] ",
259                                     revision);
260                                 if (*buf != 'y')
261                                         fatal1("aborted\n");
262                         }
263                 } else if (verbose)
264                         say2("Good.  This file appears to be the %s version.\n",
265                             revision);
266         }
267
268         return TRUE;            /* Plan a will work. */
269 }
270
271 /*
272  * Keep (virtually) nothing in memory.
273  */
274 void
275 plan_b(char *filename)
276 {
277         Reg3 FILE *ifp;
278         Reg1 int i = 0;
279         Reg2 int maxlen = 1;
280         Reg4 bool found_revision = (revision == Nullch);
281
282         using_plan_a = FALSE;
283         if ((ifp = fopen(filename, "r")) == Nullfp)
284                 pfatal2("can't open file %s", filename);
285         if ((tifd = creat(TMPINNAME, 0666)) < 0)
286                 pfatal2("can't open file %s", TMPINNAME);
287         while (fgets(buf, buf_size, ifp) != Nullch) {
288                 if (revision != Nullch && !found_revision && rev_in_string(buf))
289                         found_revision = TRUE;
290                 if ((i = strlen(buf)) > maxlen)
291                         maxlen = i;             /* Find longest line. */
292         }
293         if (revision != Nullch) {
294                 if (!found_revision) {
295                         if (force) {
296                                 if (verbose)
297                                         say2(
298 "Warning: this file doesn't appear to be the %s version--patching anyway.\n",
299                                             revision);
300                         } else if (batch) {
301                                 fatal2(
302 "this file doesn't appear to be the %s version--aborting.\n", revision);
303                         } else {
304                                 (void) ask2(
305 "This file doesn't appear to be the %s version--patch anyway? [n] ",
306                                     revision);
307                                 if (*buf != 'y')
308                                         fatal1("aborted\n");
309                         }
310                 } else if (verbose)
311                         say2("Good.  This file appears to be the %s version.\n",
312                             revision);
313         }
314         Fseek(ifp, 0L, 0);              /* Rewind file. */
315         lines_per_buf = BUFFERSIZE / maxlen;
316         tireclen = maxlen;
317         tibuf[0] = malloc((MEM)(BUFFERSIZE + 1));
318         tibuf[1] = malloc((MEM)(BUFFERSIZE + 1));
319         if (tibuf[1] == Nullch)
320                 fatal1("out of memory\n");
321         for (i = 1; ; i++) {
322                 if (! (i % lines_per_buf))      /* New block. */
323                         if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
324                                 pfatal1("can't write temp file");
325                 if (fgets(tibuf[0] + maxlen * (i%lines_per_buf),
326                     maxlen + 1, ifp) == Nullch) {
327                         input_lines = i - 1;
328                         if (i % lines_per_buf)
329                                 if (write(tifd, tibuf[0], BUFFERSIZE) <
330                                     BUFFERSIZE)
331                                         pfatal1("can't write temp file");
332                         break;
333                 }
334         }
335         Fclose(ifp);
336         Close(tifd);
337         if ((tifd = open(TMPINNAME, 0)) < 0) {
338                 pfatal2("can't reopen file %s", TMPINNAME);
339         }
340 }
341
342 /*
343  * Fetch a line from the input file, \n terminated, not necessarily \0.
344  */
345 char *
346 ifetch(line,whichbuf)
347 Reg1 LINENUM line;
348 int whichbuf;                           /* ignored when file in memory */
349 {
350         if (line < 1 || line > input_lines)
351                 return "";
352         if (using_plan_a)
353                 return i_ptr[line];
354         else {
355                 LINENUM offline = line % lines_per_buf;
356                 LINENUM baseline = line - offline;
357
358                 if (tiline[0] == baseline)
359                         whichbuf = 0;
360                 else if (tiline[1] == baseline)
361                         whichbuf = 1;
362                 else {
363                         tiline[whichbuf] = baseline;
364 #ifndef lint            /* complains of long accuracy */
365                         Lseek(tifd, (long)baseline / lines_per_buf * BUFFERSIZE, 0);
366 #endif
367                         if (read(tifd, tibuf[whichbuf], BUFFERSIZE) < 0)
368                                 pfatal2("error reading tmp file %s", TMPINNAME);
369                 }
370                 return tibuf[whichbuf] + (tireclen * offline);
371         }
372 }
373
374 /*
375  * True if the string argument contains the revision number we want.
376  */
377 bool
378 rev_in_string(char *string)
379 {
380         Reg1 char *s;
381         Reg2 int patlen;
382
383         if (revision == Nullch)
384                 return TRUE;
385         patlen = strlen(revision);
386         if (strnEQ(string,revision,patlen) &&
387             isspace((unsigned char)string[patlen]))
388                 return TRUE;
389         for (s = string; *s; s++) {
390                 if (isspace((unsigned char)*s) &&
391                     strnEQ(s + 1, revision, patlen) &&
392                     isspace((unsigned char)s[patlen + 1] )) {
393                         return TRUE;
394                 }
395         }
396         return FALSE;
397 }