]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - gnu/usr.bin/patch/inp.c
This commit was generated by cvs2svn to compensate for changes in r57093,
[FreeBSD/FreeBSD.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 /* New patch--prepare to edit another file. */
32
33 void
34 re_input()
35 {
36     if (using_plan_a) {
37         i_size = 0;
38 #ifndef lint
39         if (i_ptr != Null(char**))
40             free((char *)i_ptr);
41 #endif
42         if (i_womp != Nullch)
43             free(i_womp);
44         i_womp = Nullch;
45         i_ptr = Null(char **);
46     }
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 /* Constuct the line index, somehow or other. */
60
61 void
62 scan_input(filename)
63 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 /* Try keeping everything in memory. */
74
75 bool
76 plan_a(filename)
77 char *filename;
78 {
79     int ifd, statfailed;
80     Reg1 char *s;
81     Reg2 LINENUM iline;
82     char lbuf[MAXLINELEN];
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     /* For nonexistent or read-only files, look for RCS or SCCS versions.  */
100     if (statfailed
101         || (! output_elsewhere
102             && (/* No one can write to it.  */
103                 (filestat.st_mode & 0222) == 0
104                 /* I can't write to it.  */
105                 || ((filestat.st_mode & 0022) == 0
106                     && filestat.st_uid != myuid)))) {
107         struct stat cstat;
108         char *cs = Nullch;
109         char *filebase;
110         int pathlen;
111
112         filebase = basename(filename);
113         pathlen = filebase - filename;
114
115         /* Put any leading path into `s'.
116            Leave room in lbuf for the diff command.  */
117         s = lbuf + 20;
118         strncpy(s, filename, pathlen);
119
120 #define try(f,a1,a2) (Sprintf(s + pathlen, f, a1, a2), stat(s, &cstat) == 0)
121         if ((   try("RCS/%s%s", filebase, RCSSUFFIX)
122              || try("RCS/%s%s", filebase,        "")
123              || try(    "%s%s", filebase, RCSSUFFIX))
124             &&
125             /* Check that RCS file is not working file.
126                Some hosts don't report file name length errors.  */
127             (statfailed
128              || (  (filestat.st_dev ^ cstat.st_dev)
129                  | (filestat.st_ino ^ cstat.st_ino)))) {
130             Sprintf(buf, output_elsewhere?CHECKOUT:CHECKOUT_LOCKED, filename);
131             Sprintf(lbuf, RCSDIFF, filename);
132             cs = "RCS";
133         } else if (   try("SCCS/%s%s", SCCSPREFIX, filebase)
134                    || try(     "%s%s", SCCSPREFIX, filebase)) {
135             Sprintf(buf, output_elsewhere?GET:GET_LOCKED, s);
136             Sprintf(lbuf, SCCSDIFF, s, filename);
137             cs = "SCCS";
138         } else if (statfailed)
139             fatal2("can't find %s\n", filename);
140         /* else we can't write to it but it's not under a version
141            control system, so just proceed.  */
142         if (cs) {
143             if (!statfailed) {
144                 if ((filestat.st_mode & 0222) != 0)
145                     /* The owner can write to it.  */
146                     fatal3("file %s seems to be locked by somebody else under %s\n",
147                            filename, cs);
148                 /* It might be checked out unlocked.  See if it's safe to
149                    check out the default version locked.  */
150                 if (verbose)
151                     say3("Comparing file %s to default %s version...\n",
152                          filename, cs);
153                 if (system(lbuf))
154                     fatal3("can't check out file %s: differs from default %s version\n",
155                            filename, cs);
156             }
157             if (verbose)
158                 say3("Checking out file %s from %s...\n", filename, cs);
159             if (system(buf) || stat(filename, &filestat))
160                 fatal3("can't check out file %s from %s\n", filename, cs);
161         }
162     }
163     filemode = filestat.st_mode;
164     if (!S_ISREG(filemode))
165         fatal2("%s is not a normal file--can't patch\n", filename);
166     i_size = filestat.st_size;
167     if (out_of_mem) {
168         set_hunkmax();          /* make sure dynamic arrays are allocated */
169         out_of_mem = FALSE;
170         return FALSE;                   /* force plan b because plan a bombed */
171     }
172 #ifdef lint
173     i_womp = Nullch;
174 #else
175     i_womp = malloc((MEM)(i_size+2));   /* lint says this may alloc less than */
176                                         /* i_size, but that's okay, I think. */
177 #endif
178     if (i_womp == Nullch)
179         return FALSE;
180     if ((ifd = open(filename, 0)) < 0)
181         pfatal2("can't open file %s", filename);
182 #ifndef lint
183     if (read(ifd, i_womp, (int)i_size) != i_size) {
184         Close(ifd);     /* probably means i_size > 15 or 16 bits worth */
185         free(i_womp);   /* at this point it doesn't matter if i_womp was */
186         return FALSE;   /*   undersized. */
187     }
188 #endif
189     Close(ifd);
190     if (i_size && i_womp[i_size-1] != '\n')
191         i_womp[i_size++] = '\n';
192     i_womp[i_size] = '\0';
193
194     /* count the lines in the buffer so we know how many pointers we need */
195
196     iline = 0;
197     for (s=i_womp; *s; s++) {
198         if (*s == '\n')
199             iline++;
200     }
201 #ifdef lint
202     i_ptr = Null(char**);
203 #else
204     i_ptr = (char **)malloc((MEM)((iline + 2) * sizeof(char *)));
205 #endif
206     if (i_ptr == Null(char **)) {       /* shucks, it was a near thing */
207         free((char *)i_womp);
208         return FALSE;
209     }
210
211     /* now scan the buffer and build pointer array */
212
213     iline = 1;
214     i_ptr[iline] = i_womp;
215     for (s=i_womp; *s; s++) {
216         if (*s == '\n')
217             i_ptr[++iline] = s+1;       /* these are NOT null terminated */
218     }
219     input_lines = iline - 1;
220
221     /* now check for revision, if any */
222
223     if (revision != Nullch) {
224         if (!rev_in_string(i_womp)) {
225             if (force) {
226                 if (verbose)
227                     say2(
228 "Warning: this file doesn't appear to be the %s version--patching anyway.\n",
229                         revision);
230             }
231             else if (batch) {
232                 fatal2(
233 "this file doesn't appear to be the %s version--aborting.\n", revision);
234             }
235             else {
236                 (void) ask2(
237 "This file doesn't appear to be the %s version--patch anyway? [n] ",
238                     revision);
239             if (*buf != 'y')
240                 fatal1("aborted\n");
241             }
242         }
243         else if (verbose)
244             say2("Good.  This file appears to be the %s version.\n",
245                 revision);
246     }
247     return TRUE;                        /* plan a will work */
248 }
249
250 /* Keep (virtually) nothing in memory. */
251
252 void
253 plan_b(filename)
254 char *filename;
255 {
256     Reg3 FILE *ifp;
257     Reg1 int i = 0;
258     Reg2 int maxlen = 1;
259     Reg4 bool found_revision = (revision == Nullch);
260
261     using_plan_a = FALSE;
262     if ((ifp = fopen(filename, "r")) == Nullfp)
263         pfatal2("can't open file %s", filename);
264     if ((tifd = creat(TMPINNAME, 0666)) < 0)
265         pfatal2("can't open file %s", TMPINNAME);
266     while (fgets(buf, sizeof buf, ifp) != Nullch) {
267         if (revision != Nullch && !found_revision && rev_in_string(buf))
268             found_revision = TRUE;
269         if ((i = strlen(buf)) > maxlen)
270             maxlen = i;                 /* find longest line */
271     }
272     if (revision != Nullch) {
273         if (!found_revision) {
274             if (force) {
275                 if (verbose)
276                     say2(
277 "Warning: this file doesn't appear to be the %s version--patching anyway.\n",
278                         revision);
279             }
280             else if (batch) {
281                 fatal2(
282 "this file doesn't appear to be the %s version--aborting.\n", revision);
283             }
284             else {
285                 (void) ask2(
286 "This file doesn't appear to be the %s version--patch anyway? [n] ",
287                     revision);
288                 if (*buf != 'y')
289                     fatal1("aborted\n");
290             }
291         }
292         else if (verbose)
293             say2("Good.  This file appears to be the %s version.\n",
294                 revision);
295     }
296     Fseek(ifp, 0L, 0);          /* rewind file */
297     lines_per_buf = BUFFERSIZE / maxlen;
298     tireclen = maxlen;
299     tibuf[0] = malloc((MEM)(BUFFERSIZE + 1));
300     tibuf[1] = malloc((MEM)(BUFFERSIZE + 1));
301     if (tibuf[1] == Nullch)
302         fatal1("out of memory\n");
303     for (i=1; ; i++) {
304         if (! (i % lines_per_buf))      /* new block */
305             if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
306                 pfatal1("can't write temp file");
307         if (fgets(tibuf[0] + maxlen * (i%lines_per_buf), maxlen + 1, ifp)
308           == Nullch) {
309             input_lines = i - 1;
310             if (i % lines_per_buf)
311                 if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
312                     pfatal1("can't write temp file");
313             break;
314         }
315     }
316     Fclose(ifp);
317     Close(tifd);
318     if ((tifd = open(TMPINNAME, 0)) < 0) {
319         pfatal2("can't reopen file %s", TMPINNAME);
320     }
321 }
322
323 /* Fetch a line from the input file, \n terminated, not necessarily \0. */
324
325 char *
326 ifetch(line,whichbuf)
327 Reg1 LINENUM line;
328 int whichbuf;                           /* ignored when file in memory */
329 {
330     if (line < 1 || line > input_lines)
331         return "";
332     if (using_plan_a)
333         return i_ptr[line];
334     else {
335         LINENUM offline = line % lines_per_buf;
336         LINENUM baseline = line - offline;
337
338         if (tiline[0] == baseline)
339             whichbuf = 0;
340         else if (tiline[1] == baseline)
341             whichbuf = 1;
342         else {
343             tiline[whichbuf] = baseline;
344 #ifndef lint            /* complains of long accuracy */
345             Lseek(tifd, (long)baseline / lines_per_buf * BUFFERSIZE, 0);
346 #endif
347             if (read(tifd, tibuf[whichbuf], BUFFERSIZE) < 0)
348                 pfatal2("error reading tmp file %s", TMPINNAME);
349         }
350         return tibuf[whichbuf] + (tireclen*offline);
351     }
352 }
353
354 /* True if the string argument contains the revision number we want. */
355
356 bool
357 rev_in_string(string)
358 char *string;
359 {
360     Reg1 char *s;
361     Reg2 int patlen;
362
363     if (revision == Nullch)
364         return TRUE;
365     patlen = strlen(revision);
366     if (strnEQ(string,revision,patlen) && isspace((unsigned char)string[patlen]))
367         return TRUE;
368     for (s = string; *s; s++) {
369         if (isspace((unsigned char)*s) && strnEQ(s+1, revision, patlen) &&
370                 isspace((unsigned char)s[patlen+1] )) {
371             return TRUE;
372         }
373     }
374     return FALSE;
375 }