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