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