xref: /original-bsd/usr.bin/patch/inp.c (revision 333da485)
1 /* $Header: inp.c,v 2.0 86/09/17 15:37:02 lwall Exp $
2  *
3  * $Log:	inp.c,v $
4  * Revision 2.0  86/09/17  15:37:02  lwall
5  * Baseline for netwide release.
6  *
7  */
8 
9 #include "EXTERN.h"
10 #include "common.h"
11 #include "util.h"
12 #include "pch.h"
13 #include "INTERN.h"
14 #include "inp.h"
15 
16 /* Input-file-with-indexable-lines abstract type */
17 
18 static long i_size;			/* size of the input file */
19 static char *i_womp;			/* plan a buffer for entire file */
20 static char **i_ptr;			/* pointers to lines in i_womp */
21 
22 static int tifd = -1;			/* plan b virtual string array */
23 static char *tibuf[2];			/* plan b buffers */
24 static LINENUM tiline[2] = {-1, -1};	/* 1st line in each buffer */
25 static LINENUM lines_per_buf;		/* how many lines per buffer */
26 static int tireclen;			/* length of records in tmp file */
27 
28 /* New patch--prepare to edit another file. */
29 
30 void
31 re_input()
32 {
33     if (using_plan_a) {
34 	i_size = 0;
35 #ifndef lint
36 	if (i_ptr != Null(char**))
37 	    free((char *)i_ptr);
38 #endif
39 	if (i_womp != Nullch)
40 	    free(i_womp);
41 	i_womp = Nullch;
42 	i_ptr = Null(char **);
43     }
44     else {
45 	using_plan_a = TRUE;		/* maybe the next one is smaller */
46 	Close(tifd);
47 	tifd = -1;
48 	free(tibuf[0]);
49 	free(tibuf[1]);
50 	tibuf[0] = tibuf[1] = Nullch;
51 	tiline[0] = tiline[1] = -1;
52 	tireclen = 0;
53     }
54 }
55 
56 /* Constuct the line index, somehow or other. */
57 
58 void
59 scan_input(filename)
60 char *filename;
61 {
62     if (!plan_a(filename))
63 	plan_b(filename);
64     if (verbose) {
65 	say3("Patching file %s using Plan %s...\n", filename,
66 	  (using_plan_a ? "A" : "B") );
67     }
68 }
69 
70 /* Try keeping everything in memory. */
71 
72 bool
73 plan_a(filename)
74 char *filename;
75 {
76     int ifd;
77     Reg1 char *s;
78     Reg2 LINENUM iline;
79 
80     if (ok_to_create_file && stat(filename, &filestat) < 0) {
81 	if (verbose)
82 	    say2("(Creating file %s...)\n",filename);
83 	makedirs(filename, TRUE);
84 	close(creat(filename, 0666));
85     }
86     if (stat(filename, &filestat) < 0) {
87 	Sprintf(buf, "RCS/%s%s", filename, RCSSUFFIX);
88 	if (stat(buf, &filestat) >= 0 || stat(buf+4, &filestat) >= 0) {
89 	    Sprintf(buf, CHECKOUT, filename);
90 	    if (verbose)
91 		say2("Can't find %s--attempting to check it out from RCS.\n",
92 		    filename);
93 	    if (system(buf) || stat(filename, &filestat))
94 		fatal2("Can't check out %s.\n", filename);
95 	}
96 	else {
97 	    Sprintf(buf, "SCCS/%s%s", SCCSPREFIX, filename);
98 	    if (stat(buf, &filestat) >= 0 || stat(buf+5, &filestat) >= 0) {
99 		Sprintf(buf, GET, filename);
100 		if (verbose)
101 		    say2("Can't find %s--attempting to get it from SCCS.\n",
102 			filename);
103 		if (system(buf) || stat(filename, &filestat))
104 		    fatal2("Can't get %s.\n", filename);
105 	    }
106 	    else
107 		fatal2("Can't find %s.\n", filename);
108 	}
109     }
110     filemode = filestat.st_mode;
111     if ((filemode & S_IFMT) & ~S_IFREG)
112 	fatal2("%s is not a normal file--can't patch.\n", filename);
113     i_size = filestat.st_size;
114     if (out_of_mem) {
115 	set_hunkmax();		/* make sure dynamic arrays are allocated */
116 	out_of_mem = FALSE;
117 	return FALSE;			/* force plan b because plan a bombed */
118     }
119 #ifdef lint
120     i_womp = Nullch;
121 #else
122     i_womp = malloc((MEM)(i_size+2));	/* lint says this may alloc less than */
123 					/* i_size, but that's okay, I think. */
124 #endif
125     if (i_womp == Nullch)
126 	return FALSE;
127     if ((ifd = open(filename, 0)) < 0)
128 	fatal2("Can't open file %s\n", filename);
129 #ifndef lint
130     if (read(ifd, i_womp, (int)i_size) != i_size) {
131 	Close(ifd);	/* probably means i_size > 15 or 16 bits worth */
132 	free(i_womp);	/* at this point it doesn't matter if i_womp was */
133 	return FALSE;	/*   undersized. */
134     }
135 #endif
136     Close(ifd);
137     if (i_size && i_womp[i_size-1] != '\n')
138 	i_womp[i_size++] = '\n';
139     i_womp[i_size] = '\0';
140 
141     /* count the lines in the buffer so we know how many pointers we need */
142 
143     iline = 0;
144     for (s=i_womp; *s; s++) {
145 	if (*s == '\n')
146 	    iline++;
147     }
148 #ifdef lint
149     i_ptr = Null(char**);
150 #else
151     i_ptr = (char **)malloc((MEM)((iline + 2) * sizeof(char *)));
152 #endif
153     if (i_ptr == Null(char **)) {	/* shucks, it was a near thing */
154 	free((char *)i_womp);
155 	return FALSE;
156     }
157 
158     /* now scan the buffer and build pointer array */
159 
160     iline = 1;
161     i_ptr[iline] = i_womp;
162     for (s=i_womp; *s; s++) {
163 	if (*s == '\n')
164 	    i_ptr[++iline] = s+1;	/* these are NOT null terminated */
165     }
166     input_lines = iline - 1;
167 
168     /* now check for revision, if any */
169 
170     if (revision != Nullch) {
171 	if (!rev_in_string(i_womp)) {
172 	    if (force) {
173 		if (verbose)
174 		    say2("\
175 Warning: this file doesn't appear to be the %s version--patching anyway.\n",
176 			revision);
177 	    }
178 	    else {
179 		ask2("\
180 This file doesn't appear to be the %s version--patch anyway? [n] ",
181 		    revision);
182 	    if (*buf != 'y')
183 		fatal1("Aborted.\n");
184 	    }
185 	}
186 	else if (verbose)
187 	    say2("Good.  This file appears to be the %s version.\n",
188 		revision);
189     }
190     return TRUE;			/* plan a will work */
191 }
192 
193 /* Keep (virtually) nothing in memory. */
194 
195 void
196 plan_b(filename)
197 char *filename;
198 {
199     Reg3 FILE *ifp;
200     Reg1 int i = 0;
201     Reg2 int maxlen = 1;
202     Reg4 bool found_revision = (revision == Nullch);
203 
204     using_plan_a = FALSE;
205     if ((ifp = fopen(filename, "r")) == Nullfp)
206 	fatal2("Can't open file %s\n", filename);
207     if ((tifd = creat(TMPINNAME, 0666)) < 0)
208 	fatal2("Can't open file %s\n", TMPINNAME);
209     while (fgets(buf, sizeof buf, ifp) != Nullch) {
210 	if (revision != Nullch && !found_revision && rev_in_string(buf))
211 	    found_revision = TRUE;
212 	if ((i = strlen(buf)) > maxlen)
213 	    maxlen = i;			/* find longest line */
214     }
215     if (revision != Nullch) {
216 	if (!found_revision) {
217 	    if (force) {
218 		if (verbose)
219 		    say2("\
220 Warning: this file doesn't appear to be the %s version--patching anyway.\n",
221 			revision);
222 	    }
223 	    else {
224 		ask2("\
225 This file doesn't appear to be the %s version--patch anyway? [n] ",
226 		    revision);
227 		if (*buf != 'y')
228 		    fatal1("Aborted.\n");
229 	    }
230 	}
231 	else if (verbose)
232 	    say2("Good.  This file appears to be the %s version.\n",
233 		revision);
234     }
235     Fseek(ifp, 0L, 0);		/* rewind file */
236     lines_per_buf = BUFFERSIZE / maxlen;
237     tireclen = maxlen;
238     tibuf[0] = malloc((MEM)(BUFFERSIZE + 1));
239     tibuf[1] = malloc((MEM)(BUFFERSIZE + 1));
240     if (tibuf[1] == Nullch)
241 	fatal1("Can't seem to get enough memory.\n");
242     for (i=1; ; i++) {
243 	if (! (i % lines_per_buf))	/* new block */
244 	    if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
245 		fatal1("patch: can't write temp file.\n");
246 	if (fgets(tibuf[0] + maxlen * (i%lines_per_buf), maxlen + 1, ifp)
247 	  == Nullch) {
248 	    input_lines = i - 1;
249 	    if (i % lines_per_buf)
250 		if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
251 		    fatal1("patch: can't write temp file.\n");
252 	    break;
253 	}
254     }
255     Fclose(ifp);
256     Close(tifd);
257     if ((tifd = open(TMPINNAME, 0)) < 0) {
258 	fatal2("Can't reopen file %s\n", TMPINNAME);
259     }
260 }
261 
262 /* Fetch a line from the input file, \n terminated, not necessarily \0. */
263 
264 char *
265 ifetch(line,whichbuf)
266 Reg1 LINENUM line;
267 int whichbuf;				/* ignored when file in memory */
268 {
269     if (line < 1 || line > input_lines)
270 	return "";
271     if (using_plan_a)
272 	return i_ptr[line];
273     else {
274 	LINENUM offline = line % lines_per_buf;
275 	LINENUM baseline = line - offline;
276 
277 	if (tiline[0] == baseline)
278 	    whichbuf = 0;
279 	else if (tiline[1] == baseline)
280 	    whichbuf = 1;
281 	else {
282 	    tiline[whichbuf] = baseline;
283 #ifndef lint		/* complains of long accuracy */
284 	    Lseek(tifd, (off_t)baseline / lines_per_buf * BUFFERSIZE, 0);
285 #endif
286 	    if (read(tifd, tibuf[whichbuf], BUFFERSIZE) < 0)
287 		fatal2("Error reading tmp file %s.\n", TMPINNAME);
288 	}
289 	return tibuf[whichbuf] + (tireclen*offline);
290     }
291 }
292 
293 /* True if the string argument contains the revision number we want. */
294 
295 bool
296 rev_in_string(string)
297 char *string;
298 {
299     Reg1 char *s;
300     Reg2 int patlen;
301 
302     if (revision == Nullch)
303 	return TRUE;
304     patlen = strlen(revision);
305     for (s = string; *s; s++) {
306 	if (isspace(*s) && strnEQ(s+1, revision, patlen) &&
307 		isspace(s[patlen+1] )) {
308 	    return TRUE;
309 	}
310     }
311     return FALSE;
312 }
313 
314