Commit | Line | Data |
---|---|---|
055d27ab WJ |
1 | /* $Header: pch.c,v 2.0.1.6 87/06/04 16:18:13 lwall Exp $ |
2 | * | |
3 | * $Log: pch.c,v $ | |
4 | * Revision 2.0.1.6 87/06/04 16:18:13 lwall | |
5 | * pch_swap didn't swap p_bfake and p_efake. | |
6 | * | |
7 | * Revision 2.0.1.5 87/01/30 22:47:42 lwall | |
8 | * Improved responses to mangled patches. | |
9 | * | |
10 | * Revision 2.0.1.4 87/01/05 16:59:53 lwall | |
11 | * New-style context diffs caused double call to free(). | |
12 | * | |
13 | * Revision 2.0.1.3 86/11/14 10:08:33 lwall | |
14 | * Fixed problem where a long pattern wouldn't grow the hunk. | |
15 | * Also restored p_input_line when backtracking so error messages are right. | |
16 | * | |
17 | * Revision 2.0.1.2 86/11/03 17:49:52 lwall | |
18 | * New-style delete triggers spurious assertion error. | |
19 | * | |
20 | * Revision 2.0.1.1 86/10/29 15:52:08 lwall | |
21 | * Could falsely report new-style context diff. | |
22 | * | |
23 | * Revision 2.0 86/09/17 15:39:37 lwall | |
24 | * Baseline for netwide release. | |
25 | * | |
26 | */ | |
27 | ||
28 | #include "EXTERN.h" | |
29 | #include "common.h" | |
30 | #include "util.h" | |
31 | #include "INTERN.h" | |
32 | #include "pch.h" | |
33 | ||
34 | /* Patch (diff listing) abstract type. */ | |
35 | ||
36 | static long p_filesize; /* size of the patch file */ | |
37 | static LINENUM p_first; /* 1st line number */ | |
38 | static LINENUM p_newfirst; /* 1st line number of replacement */ | |
39 | static LINENUM p_ptrn_lines; /* # lines in pattern */ | |
40 | static LINENUM p_repl_lines; /* # lines in replacement text */ | |
41 | static LINENUM p_end = -1; /* last line in hunk */ | |
42 | static LINENUM p_max; /* max allowed value of p_end */ | |
43 | static LINENUM p_context = 3; /* # of context lines */ | |
44 | static LINENUM p_input_line = 0; /* current line # from patch file */ | |
45 | static char **p_line = Null(char**); /* the text of the hunk */ | |
46 | static short *p_len = Null(short*); /* length of each line */ | |
47 | static char *p_char = Nullch; /* +, -, and ! */ | |
48 | static int hunkmax = INITHUNKMAX; /* size of above arrays to begin with */ | |
49 | static int p_indent; /* indent to patch */ | |
50 | static LINENUM p_base; /* where to intuit this time */ | |
51 | static LINENUM p_bline; /* line # of p_base */ | |
52 | static LINENUM p_start; /* where intuit found a patch */ | |
53 | static LINENUM p_sline; /* and the line number for it */ | |
54 | static LINENUM p_hunk_beg; /* line number of current hunk */ | |
55 | static LINENUM p_efake = -1; /* end of faked up lines--don't free */ | |
56 | static LINENUM p_bfake = -1; /* beg of faked up lines */ | |
57 | ||
58 | /* Prepare to look for the next patch in the patch file. */ | |
59 | ||
60 | void | |
61 | re_patch() | |
62 | { | |
63 | p_first = Nulline; | |
64 | p_newfirst = Nulline; | |
65 | p_ptrn_lines = Nulline; | |
66 | p_repl_lines = Nulline; | |
67 | p_end = (LINENUM)-1; | |
68 | p_max = Nulline; | |
69 | p_indent = 0; | |
70 | } | |
71 | ||
72 | /* Open the patch file at the beginning of time. */ | |
73 | ||
74 | void | |
75 | open_patch_file(filename) | |
76 | char *filename; | |
77 | { | |
78 | if (filename == Nullch || !*filename || strEQ(filename, "-")) { | |
79 | pfp = fopen(TMPPATNAME, "w"); | |
80 | if (pfp == Nullfp) | |
81 | fatal2("patch: can't create %s.\n", TMPPATNAME); | |
82 | while (fgets(buf, sizeof buf, stdin) != Nullch) | |
83 | fputs(buf, pfp); | |
84 | Fclose(pfp); | |
85 | filename = TMPPATNAME; | |
86 | } | |
87 | pfp = fopen(filename, "r"); | |
88 | if (pfp == Nullfp) | |
89 | fatal2("patch file %s not found\n", filename); | |
90 | Fstat(fileno(pfp), &filestat); | |
91 | p_filesize = filestat.st_size; | |
92 | next_intuit_at(0L,1L); /* start at the beginning */ | |
93 | set_hunkmax(); | |
94 | } | |
95 | ||
96 | /* Make sure our dynamically realloced tables are malloced to begin with. */ | |
97 | ||
98 | void | |
99 | set_hunkmax() | |
100 | { | |
101 | #ifndef lint | |
102 | if (p_line == Null(char**)) | |
103 | p_line = (char**) malloc((MEM)hunkmax * sizeof(char *)); | |
104 | if (p_len == Null(short*)) | |
105 | p_len = (short*) malloc((MEM)hunkmax * sizeof(short)); | |
106 | #endif | |
107 | if (p_char == Nullch) | |
108 | p_char = (char*) malloc((MEM)hunkmax * sizeof(char)); | |
109 | } | |
110 | ||
111 | /* Enlarge the arrays containing the current hunk of patch. */ | |
112 | ||
113 | void | |
114 | grow_hunkmax() | |
115 | { | |
116 | hunkmax *= 2; | |
117 | /* | |
118 | * Note that on most systems, only the p_line array ever gets fresh memory | |
119 | * since p_len can move into p_line's old space, and p_char can move into | |
120 | * p_len's old space. Not on PDP-11's however. But it doesn't matter. | |
121 | */ | |
122 | assert(p_line != Null(char**) && p_len != Null(short*) && p_char != Nullch); | |
123 | #ifndef lint | |
124 | p_line = (char**) realloc((char*)p_line, (MEM)hunkmax * sizeof(char *)); | |
125 | p_len = (short*) realloc((char*)p_len, (MEM)hunkmax * sizeof(short)); | |
126 | p_char = (char*) realloc((char*)p_char, (MEM)hunkmax * sizeof(char)); | |
127 | #endif | |
128 | if (p_line != Null(char**) && p_len != Null(short*) && p_char != Nullch) | |
129 | return; | |
130 | if (!using_plan_a) | |
131 | fatal1("patch: out of memory (grow_hunkmax)\n"); | |
132 | out_of_mem = TRUE; /* whatever is null will be allocated again */ | |
133 | /* from within plan_a(), of all places */ | |
134 | } | |
135 | ||
136 | /* True if the remainder of the patch file contains a diff of some sort. */ | |
137 | ||
138 | bool | |
139 | there_is_another_patch() | |
140 | { | |
141 | if (p_base != 0L && p_base >= p_filesize) { | |
142 | if (verbose) | |
143 | say1("done\n"); | |
144 | return FALSE; | |
145 | } | |
146 | if (verbose) | |
147 | say1("Hmm..."); | |
148 | diff_type = intuit_diff_type(); | |
149 | if (!diff_type) { | |
150 | if (p_base != 0L) { | |
151 | if (verbose) | |
152 | say1(" Ignoring the trailing garbage.\ndone\n"); | |
153 | } | |
154 | else | |
155 | say1(" I can't seem to find a patch in there anywhere.\n"); | |
156 | return FALSE; | |
157 | } | |
158 | if (verbose) | |
159 | say3(" %sooks like %s to me...\n", | |
160 | (p_base == 0L ? "L" : "The next patch l"), | |
161 | diff_type == CONTEXT_DIFF ? "a context diff" : | |
162 | diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" : | |
163 | diff_type == NORMAL_DIFF ? "a normal diff" : | |
164 | "an ed script" ); | |
165 | if (p_indent && verbose) | |
166 | say3("(Patch is indented %d space%s.)\n", p_indent, p_indent==1?"":"s"); | |
167 | skip_to(p_start,p_sline); | |
168 | while (filearg[0] == Nullch) { | |
169 | if (force) { | |
170 | say1("No file to patch. Skipping...\n"); | |
171 | filearg[0] = savestr(bestguess); | |
172 | return TRUE; | |
173 | } | |
174 | ask1("File to patch: "); | |
175 | if (*buf != '\n') { | |
176 | if (bestguess) | |
177 | free(bestguess); | |
178 | bestguess = savestr(buf); | |
179 | filearg[0] = fetchname(buf, 0, FALSE); | |
180 | } | |
181 | if (filearg[0] == Nullch) { | |
182 | ask1("No file found--skip this patch? [n] "); | |
183 | if (*buf != 'y') { | |
184 | continue; | |
185 | } | |
186 | if (verbose) | |
187 | say1("Skipping patch...\n"); | |
188 | filearg[0] = fetchname(bestguess, 0, TRUE); | |
189 | skip_rest_of_patch = TRUE; | |
190 | return TRUE; | |
191 | } | |
192 | } | |
193 | return TRUE; | |
194 | } | |
195 | ||
196 | /* Determine what kind of diff is in the remaining part of the patch file. */ | |
197 | ||
198 | int | |
199 | intuit_diff_type() | |
200 | { | |
201 | Reg4 long this_line = 0; | |
202 | Reg5 long previous_line; | |
203 | Reg6 long first_command_line = -1; | |
204 | long fcl_line; | |
205 | Reg7 bool last_line_was_command = FALSE; | |
206 | Reg8 bool this_is_a_command = FALSE; | |
207 | Reg9 bool stars_last_line = FALSE; | |
208 | Reg10 bool stars_this_line = FALSE; | |
209 | Reg3 int indent; | |
210 | Reg1 char *s; | |
211 | Reg2 char *t; | |
212 | char *indtmp = Nullch; | |
213 | char *oldtmp = Nullch; | |
214 | char *newtmp = Nullch; | |
215 | char *indname = Nullch; | |
216 | char *oldname = Nullch; | |
217 | char *newname = Nullch; | |
218 | Reg11 int retval; | |
219 | bool no_filearg = (filearg[0] == Nullch); | |
220 | ||
221 | ok_to_create_file = FALSE; | |
222 | Fseek(pfp, p_base, 0); | |
223 | p_input_line = p_bline - 1; | |
224 | for (;;) { | |
225 | previous_line = this_line; | |
226 | last_line_was_command = this_is_a_command; | |
227 | stars_last_line = stars_this_line; | |
228 | this_line = ftell(pfp); | |
229 | indent = 0; | |
230 | p_input_line++; | |
231 | if (fgets(buf, sizeof buf, pfp) == Nullch) { | |
232 | if (first_command_line >= 0L) { | |
233 | /* nothing but deletes!? */ | |
234 | p_start = first_command_line; | |
235 | p_sline = fcl_line; | |
236 | retval = ED_DIFF; | |
237 | goto scan_exit; | |
238 | } | |
239 | else { | |
240 | p_start = this_line; | |
241 | p_sline = p_input_line; | |
242 | retval = 0; | |
243 | goto scan_exit; | |
244 | } | |
245 | } | |
246 | for (s = buf; *s == ' ' || *s == '\t'; s++) { | |
247 | if (*s == '\t') | |
248 | indent += 8 - (indent % 8); | |
249 | else | |
250 | indent++; | |
251 | } | |
252 | for (t=s; isdigit(*t) || *t == ','; t++) ; | |
253 | this_is_a_command = (isdigit(*s) && | |
254 | (*t == 'd' || *t == 'c' || *t == 'a') ); | |
255 | if (first_command_line < 0L && this_is_a_command) { | |
256 | first_command_line = this_line; | |
257 | fcl_line = p_input_line; | |
258 | p_indent = indent; /* assume this for now */ | |
259 | } | |
260 | if (!stars_last_line && strnEQ(s, "*** ", 4)) | |
261 | oldtmp = savestr(s+4); | |
262 | else if (strnEQ(s, "--- ", 4)) | |
263 | newtmp = savestr(s+4); | |
264 | else if (strnEQ(s, "Index:", 6)) | |
265 | indtmp = savestr(s+6); | |
266 | else if (strnEQ(s, "Prereq:", 7)) { | |
267 | for (t=s+7; isspace(*t); t++) ; | |
268 | revision = savestr(t); | |
269 | for (t=revision; *t && !isspace(*t); t++) ; | |
270 | *t = '\0'; | |
271 | if (!*revision) { | |
272 | free(revision); | |
273 | revision = Nullch; | |
274 | } | |
275 | } | |
276 | if ((!diff_type || diff_type == ED_DIFF) && | |
277 | first_command_line >= 0L && | |
278 | strEQ(s, ".\n") ) { | |
279 | p_indent = indent; | |
280 | p_start = first_command_line; | |
281 | p_sline = fcl_line; | |
282 | retval = ED_DIFF; | |
283 | goto scan_exit; | |
284 | } | |
285 | stars_this_line = strnEQ(s, "********", 8); | |
286 | if ((!diff_type || diff_type == CONTEXT_DIFF) && stars_last_line && | |
287 | strnEQ(s, "*** ", 4)) { | |
288 | if (!atol(s+4)) | |
289 | ok_to_create_file = TRUE; | |
290 | /* if this is a new context diff the character just before */ | |
291 | /* the newline is a '*'. */ | |
292 | while (*s != '\n') | |
293 | s++; | |
294 | p_indent = indent; | |
295 | p_start = previous_line; | |
296 | p_sline = p_input_line - 1; | |
297 | retval = (*(s-1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF); | |
298 | goto scan_exit; | |
299 | } | |
300 | if ((!diff_type || diff_type == NORMAL_DIFF) && | |
301 | last_line_was_command && | |
302 | (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2)) ) { | |
303 | p_start = previous_line; | |
304 | p_sline = p_input_line - 1; | |
305 | p_indent = indent; | |
306 | retval = NORMAL_DIFF; | |
307 | goto scan_exit; | |
308 | } | |
309 | } | |
310 | scan_exit: | |
311 | if (no_filearg) { | |
312 | if (indtmp != Nullch) | |
313 | indname = fetchname(indtmp, strippath, ok_to_create_file); | |
314 | if (oldtmp != Nullch) | |
315 | oldname = fetchname(oldtmp, strippath, ok_to_create_file); | |
316 | if (newtmp != Nullch) | |
317 | newname = fetchname(newtmp, strippath, ok_to_create_file); | |
318 | if (oldname && newname) { | |
319 | if (strlen(oldname) < strlen(newname)) | |
320 | filearg[0] = savestr(oldname); | |
321 | else | |
322 | filearg[0] = savestr(newname); | |
323 | } | |
324 | else if (oldname) | |
325 | filearg[0] = savestr(oldname); | |
326 | else if (newname) | |
327 | filearg[0] = savestr(newname); | |
328 | else if (indname) | |
329 | filearg[0] = savestr(indname); | |
330 | } | |
331 | if (bestguess) { | |
332 | free(bestguess); | |
333 | bestguess = Nullch; | |
334 | } | |
335 | if (filearg[0] != Nullch) | |
336 | bestguess = savestr(filearg[0]); | |
337 | else if (indtmp != Nullch) | |
338 | bestguess = fetchname(indtmp, strippath, TRUE); | |
339 | else { | |
340 | if (oldtmp != Nullch) | |
341 | oldname = fetchname(oldtmp, strippath, TRUE); | |
342 | if (newtmp != Nullch) | |
343 | newname = fetchname(newtmp, strippath, TRUE); | |
344 | if (oldname && newname) { | |
345 | if (strlen(oldname) < strlen(newname)) | |
346 | bestguess = savestr(oldname); | |
347 | else | |
348 | bestguess = savestr(newname); | |
349 | } | |
350 | else if (oldname) | |
351 | bestguess = savestr(oldname); | |
352 | else if (newname) | |
353 | bestguess = savestr(newname); | |
354 | } | |
355 | if (indtmp != Nullch) | |
356 | free(indtmp); | |
357 | if (oldtmp != Nullch) | |
358 | free(oldtmp); | |
359 | if (newtmp != Nullch) | |
360 | free(newtmp); | |
361 | if (indname != Nullch) | |
362 | free(indname); | |
363 | if (oldname != Nullch) | |
364 | free(oldname); | |
365 | if (newname != Nullch) | |
366 | free(newname); | |
367 | return retval; | |
368 | } | |
369 | ||
370 | /* Remember where this patch ends so we know where to start up again. */ | |
371 | ||
372 | void | |
373 | next_intuit_at(file_pos,file_line) | |
374 | long file_pos; | |
375 | long file_line; | |
376 | { | |
377 | p_base = file_pos; | |
378 | p_bline = file_line; | |
379 | } | |
380 | ||
381 | /* Basically a verbose fseek() to the actual diff listing. */ | |
382 | ||
383 | void | |
384 | skip_to(file_pos,file_line) | |
385 | long file_pos; | |
386 | long file_line; | |
387 | { | |
388 | char *ret; | |
389 | ||
390 | assert(p_base <= file_pos); | |
391 | if (verbose && p_base < file_pos) { | |
392 | Fseek(pfp, p_base, 0); | |
393 | say1("The text leading up to this was:\n--------------------------\n"); | |
394 | while (ftell(pfp) < file_pos) { | |
395 | ret = fgets(buf, sizeof buf, pfp); | |
396 | assert(ret != Nullch); | |
397 | say2("|%s", buf); | |
398 | } | |
399 | say1("--------------------------\n"); | |
400 | } | |
401 | else | |
402 | Fseek(pfp, file_pos, 0); | |
403 | p_input_line = file_line - 1; | |
404 | } | |
405 | ||
406 | /* True if there is more of the current diff listing to process. */ | |
407 | ||
408 | bool | |
409 | another_hunk() | |
410 | { | |
411 | Reg1 char *s; | |
412 | Reg8 char *ret; | |
413 | Reg2 int context = 0; | |
414 | ||
415 | while (p_end >= 0) { | |
416 | if (p_end == p_efake) | |
417 | p_end = p_bfake; /* don't free twice */ | |
418 | else | |
419 | free(p_line[p_end]); | |
420 | p_end--; | |
421 | } | |
422 | assert(p_end == -1); | |
423 | p_efake = -1; | |
424 | ||
425 | p_max = hunkmax; /* gets reduced when --- found */ | |
426 | if (diff_type == CONTEXT_DIFF || diff_type == NEW_CONTEXT_DIFF) { | |
427 | long line_beginning = ftell(pfp); | |
428 | /* file pos of the current line */ | |
429 | LINENUM repl_beginning = 0; /* index of --- line */ | |
430 | Reg4 LINENUM fillcnt = 0; /* #lines of missing ptrn or repl */ | |
431 | Reg5 LINENUM fillsrc; /* index of first line to copy */ | |
432 | Reg6 LINENUM filldst; /* index of first missing line */ | |
433 | bool ptrn_spaces_eaten = FALSE; /* ptrn was slightly misformed */ | |
434 | Reg9 bool repl_could_be_missing = TRUE; | |
435 | /* no + or ! lines in this hunk */ | |
436 | bool repl_missing = FALSE; /* we are now backtracking */ | |
437 | long repl_backtrack_position = 0; | |
438 | /* file pos of first repl line */ | |
439 | LINENUM repl_patch_line; /* input line number for same */ | |
440 | Reg7 LINENUM ptrn_copiable = 0; | |
441 | /* # of copiable lines in ptrn */ | |
442 | ||
443 | ret = pgets(buf, sizeof buf, pfp); | |
444 | p_input_line++; | |
445 | if (ret == Nullch || strnNE(buf, "********", 8)) { | |
446 | next_intuit_at(line_beginning,p_input_line); | |
447 | return FALSE; | |
448 | } | |
449 | p_context = 100; | |
450 | p_hunk_beg = p_input_line + 1; | |
451 | while (p_end < p_max) { | |
452 | line_beginning = ftell(pfp); | |
453 | ret = pgets(buf, sizeof buf, pfp); | |
454 | p_input_line++; | |
455 | if (ret == Nullch) { | |
456 | if (p_max - p_end < 4) | |
457 | Strcpy(buf, " \n"); /* assume blank lines got chopped */ | |
458 | else { | |
459 | if (repl_beginning && repl_could_be_missing) { | |
460 | repl_missing = TRUE; | |
461 | goto hunk_done; | |
462 | } | |
463 | fatal1("Unexpected end of file in patch.\n"); | |
464 | } | |
465 | } | |
466 | p_end++; | |
467 | assert(p_end < hunkmax); | |
468 | p_char[p_end] = *buf; | |
469 | p_line[p_end] = Nullch; | |
470 | switch (*buf) { | |
471 | case '*': | |
472 | if (strnEQ(buf, "********", 8)) { | |
473 | if (repl_beginning && repl_could_be_missing) { | |
474 | repl_missing = TRUE; | |
475 | goto hunk_done; | |
476 | } | |
477 | else | |
478 | fatal2("Unexpected end of hunk at line %ld.\n", | |
479 | p_input_line); | |
480 | } | |
481 | if (p_end != 0) { | |
482 | if (repl_beginning && repl_could_be_missing) { | |
483 | repl_missing = TRUE; | |
484 | goto hunk_done; | |
485 | } | |
486 | fatal3("Unexpected *** at line %ld: %s", p_input_line, buf); | |
487 | } | |
488 | context = 0; | |
489 | p_line[p_end] = savestr(buf); | |
490 | if (out_of_mem) { | |
491 | p_end--; | |
492 | return FALSE; | |
493 | } | |
494 | for (s=buf; *s && !isdigit(*s); s++) ; | |
495 | if (!*s) | |
496 | goto malformed; | |
497 | p_first = (LINENUM) atol(s); | |
498 | while (isdigit(*s)) s++; | |
499 | if (*s == ',') { | |
500 | for (; *s && !isdigit(*s); s++) ; | |
501 | if (!*s) | |
502 | goto malformed; | |
503 | p_ptrn_lines = ((LINENUM)atol(s)) - p_first + 1; | |
504 | } | |
505 | else if (p_first) | |
506 | p_ptrn_lines = 1; | |
507 | else { | |
508 | p_ptrn_lines = 0; | |
509 | p_first = 1; | |
510 | } | |
511 | p_max = p_ptrn_lines + 6; /* we need this much at least */ | |
512 | while (p_max >= hunkmax) | |
513 | grow_hunkmax(); | |
514 | p_max = hunkmax; | |
515 | break; | |
516 | case '-': | |
517 | if (buf[1] == '-') { | |
518 | if (repl_beginning || | |
519 | (p_end != p_ptrn_lines + 1 + (p_char[p_end-1] == '\n'))) | |
520 | { | |
521 | if (p_end == 1) { | |
522 | /* `old' lines were omitted - set up to fill */ | |
523 | /* them in from 'new' context lines. */ | |
524 | p_end = p_ptrn_lines + 1; | |
525 | fillsrc = p_end + 1; | |
526 | filldst = 1; | |
527 | fillcnt = p_ptrn_lines; | |
528 | } | |
529 | else { | |
530 | if (repl_beginning) { | |
531 | if (repl_could_be_missing){ | |
532 | repl_missing = TRUE; | |
533 | goto hunk_done; | |
534 | } | |
535 | fatal3( | |
536 | "Duplicate \"---\" at line %ld--check line numbers at line %ld.\n", | |
537 | p_input_line, p_hunk_beg + repl_beginning); | |
538 | } | |
539 | else { | |
540 | fatal4( | |
541 | "%s \"---\" at line %ld--check line numbers at line %ld.\n", | |
542 | (p_end <= p_ptrn_lines | |
543 | ? "Premature" | |
544 | : "Overdue" ), | |
545 | p_input_line, p_hunk_beg); | |
546 | } | |
547 | } | |
548 | } | |
549 | repl_beginning = p_end; | |
550 | repl_backtrack_position = ftell(pfp); | |
551 | repl_patch_line = p_input_line; | |
552 | p_line[p_end] = savestr(buf); | |
553 | if (out_of_mem) { | |
554 | p_end--; | |
555 | return FALSE; | |
556 | } | |
557 | p_char[p_end] = '='; | |
558 | for (s=buf; *s && !isdigit(*s); s++) ; | |
559 | if (!*s) | |
560 | goto malformed; | |
561 | p_newfirst = (LINENUM) atol(s); | |
562 | while (isdigit(*s)) s++; | |
563 | if (*s == ',') { | |
564 | for (; *s && !isdigit(*s); s++) ; | |
565 | if (!*s) | |
566 | goto malformed; | |
567 | p_repl_lines = ((LINENUM)atol(s)) - p_newfirst + 1; | |
568 | } | |
569 | else if (p_newfirst) | |
570 | p_repl_lines = 1; | |
571 | else { | |
572 | p_repl_lines = 0; | |
573 | p_newfirst = 1; | |
574 | } | |
575 | p_max = p_repl_lines + p_end; | |
576 | if (p_max > MAXHUNKSIZE) | |
577 | fatal4("Hunk too large (%ld lines) at line %ld: %s", | |
578 | p_max, p_input_line, buf); | |
579 | while (p_max >= hunkmax) | |
580 | grow_hunkmax(); | |
581 | if (p_repl_lines != ptrn_copiable) | |
582 | repl_could_be_missing = FALSE; | |
583 | break; | |
584 | } | |
585 | goto change_line; | |
586 | case '+': case '!': | |
587 | repl_could_be_missing = FALSE; | |
588 | change_line: | |
589 | if (!isspace(buf[1]) && buf[1] != '>' && buf[1] != '<' && | |
590 | repl_beginning && repl_could_be_missing) { | |
591 | repl_missing = TRUE; | |
592 | goto hunk_done; | |
593 | } | |
594 | if (context > 0) { | |
595 | if (context < p_context) | |
596 | p_context = context; | |
597 | context = -1000; | |
598 | } | |
599 | p_line[p_end] = savestr(buf+2); | |
600 | if (out_of_mem) { | |
601 | p_end--; | |
602 | return FALSE; | |
603 | } | |
604 | break; | |
605 | case '\t': case '\n': /* assume the 2 spaces got eaten */ | |
606 | if (repl_beginning && repl_could_be_missing && | |
607 | (!ptrn_spaces_eaten || diff_type == NEW_CONTEXT_DIFF) ) { | |
608 | repl_missing = TRUE; | |
609 | goto hunk_done; | |
610 | } | |
611 | p_line[p_end] = savestr(buf); | |
612 | if (out_of_mem) { | |
613 | p_end--; | |
614 | return FALSE; | |
615 | } | |
616 | if (p_end != p_ptrn_lines + 1) { | |
617 | ptrn_spaces_eaten |= (repl_beginning != 0); | |
618 | context++; | |
619 | if (!repl_beginning) | |
620 | ptrn_copiable++; | |
621 | p_char[p_end] = ' '; | |
622 | } | |
623 | break; | |
624 | case ' ': | |
625 | if (!isspace(buf[1]) && | |
626 | repl_beginning && repl_could_be_missing) { | |
627 | repl_missing = TRUE; | |
628 | goto hunk_done; | |
629 | } | |
630 | context++; | |
631 | if (!repl_beginning) | |
632 | ptrn_copiable++; | |
633 | p_line[p_end] = savestr(buf+2); | |
634 | if (out_of_mem) { | |
635 | p_end--; | |
636 | return FALSE; | |
637 | } | |
638 | break; | |
639 | default: | |
640 | if (repl_beginning && repl_could_be_missing) { | |
641 | repl_missing = TRUE; | |
642 | goto hunk_done; | |
643 | } | |
644 | goto malformed; | |
645 | } | |
646 | /* set up p_len for strncmp() so we don't have to */ | |
647 | /* assume null termination */ | |
648 | if (p_line[p_end]) | |
649 | p_len[p_end] = strlen(p_line[p_end]); | |
650 | else | |
651 | p_len[p_end] = 0; | |
652 | } | |
653 | ||
654 | hunk_done: | |
655 | if (p_end >=0 && !repl_beginning) | |
656 | fatal2("No --- found in patch at line %ld\n", pch_hunk_beg()); | |
657 | ||
658 | if (repl_missing) { | |
659 | ||
660 | /* reset state back to just after --- */ | |
661 | p_input_line = repl_patch_line; | |
662 | for (p_end--; p_end > repl_beginning; p_end--) | |
663 | free(p_line[p_end]); | |
664 | Fseek(pfp, repl_backtrack_position, 0); | |
665 | ||
666 | /* redundant 'new' context lines were omitted - set */ | |
667 | /* up to fill them in from the old file context */ | |
668 | fillsrc = 1; | |
669 | filldst = repl_beginning+1; | |
670 | fillcnt = p_repl_lines; | |
671 | p_end = p_max; | |
672 | } | |
673 | ||
674 | if (diff_type == CONTEXT_DIFF && | |
675 | (fillcnt || (p_first > 1 && ptrn_copiable > 2*p_context)) ) { | |
676 | if (verbose) | |
677 | say1("\ | |
678 | (Fascinating--this is really a new-style context diff but without the telltale\n\ | |
679 | extra asterisks on the *** line that usually indicate the new style...)\n"); | |
680 | diff_type = NEW_CONTEXT_DIFF; | |
681 | } | |
682 | ||
683 | /* if there were omitted context lines, fill them in now */ | |
684 | if (fillcnt) { | |
685 | p_bfake = filldst; /* remember where not to free() */ | |
686 | p_efake = filldst + fillcnt - 1; | |
687 | while (fillcnt-- > 0) { | |
688 | while (fillsrc <= p_end && p_char[fillsrc] != ' ') | |
689 | fillsrc++; | |
690 | if (fillsrc > p_end) | |
691 | fatal2("Replacement text or line numbers mangled in hunk at line %ld\n", | |
692 | p_hunk_beg); | |
693 | p_line[filldst] = p_line[fillsrc]; | |
694 | p_char[filldst] = p_char[fillsrc]; | |
695 | p_len[filldst] = p_len[fillsrc]; | |
696 | fillsrc++; filldst++; | |
697 | } | |
698 | while (fillsrc <= p_end && fillsrc != repl_beginning && | |
699 | p_char[fillsrc] != ' ') | |
700 | fillsrc++; | |
701 | #ifdef DEBUGGING | |
702 | if (debug & 64) | |
703 | printf("fillsrc %ld, filldst %ld, rb %ld, e+1 %ld\n", | |
704 | fillsrc,filldst,repl_beginning,p_end+1); | |
705 | #endif | |
706 | assert(fillsrc==p_end+1 || fillsrc==repl_beginning); | |
707 | assert(filldst==p_end+1 || filldst==repl_beginning); | |
708 | } | |
709 | } | |
710 | else { /* normal diff--fake it up */ | |
711 | char hunk_type; | |
712 | Reg3 int i; | |
713 | LINENUM min, max; | |
714 | long line_beginning = ftell(pfp); | |
715 | ||
716 | p_context = 0; | |
717 | ret = pgets(buf, sizeof buf, pfp); | |
718 | p_input_line++; | |
719 | if (ret == Nullch || !isdigit(*buf)) { | |
720 | next_intuit_at(line_beginning,p_input_line); | |
721 | return FALSE; | |
722 | } | |
723 | p_first = (LINENUM)atol(buf); | |
724 | for (s=buf; isdigit(*s); s++) ; | |
725 | if (*s == ',') { | |
726 | p_ptrn_lines = (LINENUM)atol(++s) - p_first + 1; | |
727 | while (isdigit(*s)) s++; | |
728 | } | |
729 | else | |
730 | p_ptrn_lines = (*s != 'a'); | |
731 | hunk_type = *s; | |
732 | if (hunk_type == 'a') | |
733 | p_first++; /* do append rather than insert */ | |
734 | min = (LINENUM)atol(++s); | |
735 | for (; isdigit(*s); s++) ; | |
736 | if (*s == ',') | |
737 | max = (LINENUM)atol(++s); | |
738 | else | |
739 | max = min; | |
740 | if (hunk_type == 'd') | |
741 | min++; | |
742 | p_end = p_ptrn_lines + 1 + max - min + 1; | |
743 | if (p_end > MAXHUNKSIZE) | |
744 | fatal4("Hunk too large (%ld lines) at line %ld: %s", | |
745 | p_end, p_input_line, buf); | |
746 | while (p_end >= hunkmax) | |
747 | grow_hunkmax(); | |
748 | p_newfirst = min; | |
749 | p_repl_lines = max - min + 1; | |
750 | Sprintf(buf, "*** %ld,%ld\n", p_first, p_first + p_ptrn_lines - 1); | |
751 | p_line[0] = savestr(buf); | |
752 | if (out_of_mem) { | |
753 | p_end = -1; | |
754 | return FALSE; | |
755 | } | |
756 | p_char[0] = '*'; | |
757 | for (i=1; i<=p_ptrn_lines; i++) { | |
758 | ret = pgets(buf, sizeof buf, pfp); | |
759 | p_input_line++; | |
760 | if (ret == Nullch) | |
761 | fatal2("Unexpected end of file in patch at line %ld.\n", | |
762 | p_input_line); | |
763 | if (*buf != '<') | |
764 | fatal2("< expected at line %ld of patch.\n", p_input_line); | |
765 | p_line[i] = savestr(buf+2); | |
766 | if (out_of_mem) { | |
767 | p_end = i-1; | |
768 | return FALSE; | |
769 | } | |
770 | p_len[i] = strlen(p_line[i]); | |
771 | p_char[i] = '-'; | |
772 | } | |
773 | if (hunk_type == 'c') { | |
774 | ret = pgets(buf, sizeof buf, pfp); | |
775 | p_input_line++; | |
776 | if (ret == Nullch) | |
777 | fatal2("Unexpected end of file in patch at line %ld.\n", | |
778 | p_input_line); | |
779 | if (*buf != '-') | |
780 | fatal2("--- expected at line %ld of patch.\n", p_input_line); | |
781 | } | |
782 | Sprintf(buf, "--- %ld,%ld\n", min, max); | |
783 | p_line[i] = savestr(buf); | |
784 | if (out_of_mem) { | |
785 | p_end = i-1; | |
786 | return FALSE; | |
787 | } | |
788 | p_char[i] = '='; | |
789 | for (i++; i<=p_end; i++) { | |
790 | ret = pgets(buf, sizeof buf, pfp); | |
791 | p_input_line++; | |
792 | if (ret == Nullch) | |
793 | fatal2("Unexpected end of file in patch at line %ld.\n", | |
794 | p_input_line); | |
795 | if (*buf != '>') | |
796 | fatal2("> expected at line %ld of patch.\n", p_input_line); | |
797 | p_line[i] = savestr(buf+2); | |
798 | if (out_of_mem) { | |
799 | p_end = i-1; | |
800 | return FALSE; | |
801 | } | |
802 | p_len[i] = strlen(p_line[i]); | |
803 | p_char[i] = '+'; | |
804 | } | |
805 | } | |
806 | if (reverse) /* backwards patch? */ | |
807 | if (!pch_swap()) | |
808 | say1("Not enough memory to swap next hunk!\n"); | |
809 | #ifdef DEBUGGING | |
810 | if (debug & 2) { | |
811 | int i; | |
812 | char special; | |
813 | ||
814 | for (i=0; i <= p_end; i++) { | |
815 | if (i == p_ptrn_lines) | |
816 | special = '^'; | |
817 | else | |
818 | special = ' '; | |
819 | fprintf(stderr, "%3d %c %c %s", i, p_char[i], special, p_line[i]); | |
820 | Fflush(stderr); | |
821 | } | |
822 | } | |
823 | #endif | |
824 | if (p_end+1 < hunkmax) /* paranoia reigns supreme... */ | |
825 | p_char[p_end+1] = '^'; /* add a stopper for apply_hunk */ | |
826 | return TRUE; | |
827 | ||
828 | malformed: | |
829 | fatal3("Malformed patch at line %ld: %s", p_input_line, buf); | |
830 | /* about as informative as "Syntax error" in C */ | |
831 | return FALSE; /* for lint */ | |
832 | } | |
833 | ||
834 | /* Input a line from the patch file, worrying about indentation. */ | |
835 | ||
836 | char * | |
837 | pgets(bf,sz,fp) | |
838 | char *bf; | |
839 | int sz; | |
840 | FILE *fp; | |
841 | { | |
842 | char *ret = fgets(bf, sz, fp); | |
843 | Reg1 char *s; | |
844 | Reg2 int indent = 0; | |
845 | ||
846 | if (p_indent && ret != Nullch) { | |
847 | for (s=buf; indent < p_indent && (*s == ' ' || *s == '\t'); s++) { | |
848 | if (*s == '\t') | |
849 | indent += 8 - (indent % 7); | |
850 | else | |
851 | indent++; | |
852 | } | |
853 | if (buf != s) | |
854 | Strcpy(buf, s); | |
855 | } | |
856 | return ret; | |
857 | } | |
858 | ||
859 | /* Reverse the old and new portions of the current hunk. */ | |
860 | ||
861 | bool | |
862 | pch_swap() | |
863 | { | |
864 | char **tp_line; /* the text of the hunk */ | |
865 | short *tp_len; /* length of each line */ | |
866 | char *tp_char; /* +, -, and ! */ | |
867 | Reg1 LINENUM i; | |
868 | Reg2 LINENUM n; | |
869 | bool blankline = FALSE; | |
870 | Reg3 char *s; | |
871 | ||
872 | i = p_first; | |
873 | p_first = p_newfirst; | |
874 | p_newfirst = i; | |
875 | ||
876 | /* make a scratch copy */ | |
877 | ||
878 | tp_line = p_line; | |
879 | tp_len = p_len; | |
880 | tp_char = p_char; | |
881 | p_line = Null(char**); /* force set_hunkmax to allocate again */ | |
882 | p_len = Null(short*); | |
883 | p_char = Nullch; | |
884 | set_hunkmax(); | |
885 | if (p_line == Null(char**) || p_len == Null(short*) || p_char == Nullch) { | |
886 | #ifndef lint | |
887 | if (p_line == Null(char**)) | |
888 | free((char*)p_line); | |
889 | p_line = tp_line; | |
890 | if (p_len == Null(short*)) | |
891 | free((char*)p_len); | |
892 | p_len = tp_len; | |
893 | #endif | |
894 | if (p_char == Nullch) | |
895 | free((char*)p_char); | |
896 | p_char = tp_char; | |
897 | return FALSE; /* not enough memory to swap hunk! */ | |
898 | } | |
899 | ||
900 | /* now turn the new into the old */ | |
901 | ||
902 | i = p_ptrn_lines + 1; | |
903 | if (tp_char[i] == '\n') { /* account for possible blank line */ | |
904 | blankline = TRUE; | |
905 | i++; | |
906 | } | |
907 | if (p_efake >= 0) { /* fix non-freeable ptr range */ | |
908 | n = p_end - i + 1; | |
909 | if (p_efake > i) | |
910 | n = -n; | |
911 | p_efake += n; | |
912 | p_bfake += n; | |
913 | } | |
914 | for (n=0; i <= p_end; i++,n++) { | |
915 | p_line[n] = tp_line[i]; | |
916 | p_char[n] = tp_char[i]; | |
917 | if (p_char[n] == '+') | |
918 | p_char[n] = '-'; | |
919 | p_len[n] = tp_len[i]; | |
920 | } | |
921 | if (blankline) { | |
922 | i = p_ptrn_lines + 1; | |
923 | p_line[n] = tp_line[i]; | |
924 | p_char[n] = tp_char[i]; | |
925 | p_len[n] = tp_len[i]; | |
926 | n++; | |
927 | } | |
928 | assert(p_char[0] == '='); | |
929 | p_char[0] = '*'; | |
930 | for (s=p_line[0]; *s; s++) | |
931 | if (*s == '-') | |
932 | *s = '*'; | |
933 | ||
934 | /* now turn the old into the new */ | |
935 | ||
936 | assert(tp_char[0] == '*'); | |
937 | tp_char[0] = '='; | |
938 | for (s=tp_line[0]; *s; s++) | |
939 | if (*s == '*') | |
940 | *s = '-'; | |
941 | for (i=0; n <= p_end; i++,n++) { | |
942 | p_line[n] = tp_line[i]; | |
943 | p_char[n] = tp_char[i]; | |
944 | if (p_char[n] == '-') | |
945 | p_char[n] = '+'; | |
946 | p_len[n] = tp_len[i]; | |
947 | } | |
948 | assert(i == p_ptrn_lines + 1); | |
949 | i = p_ptrn_lines; | |
950 | p_ptrn_lines = p_repl_lines; | |
951 | p_repl_lines = i; | |
952 | #ifndef lint | |
953 | if (tp_line == Null(char**)) | |
954 | free((char*)tp_line); | |
955 | if (tp_len == Null(short*)) | |
956 | free((char*)tp_len); | |
957 | #endif | |
958 | if (tp_char == Nullch) | |
959 | free((char*)tp_char); | |
960 | return TRUE; | |
961 | } | |
962 | ||
963 | /* Return the specified line position in the old file of the old context. */ | |
964 | ||
965 | LINENUM | |
966 | pch_first() | |
967 | { | |
968 | return p_first; | |
969 | } | |
970 | ||
971 | /* Return the number of lines of old context. */ | |
972 | ||
973 | LINENUM | |
974 | pch_ptrn_lines() | |
975 | { | |
976 | return p_ptrn_lines; | |
977 | } | |
978 | ||
979 | /* Return the probable line position in the new file of the first line. */ | |
980 | ||
981 | LINENUM | |
982 | pch_newfirst() | |
983 | { | |
984 | return p_newfirst; | |
985 | } | |
986 | ||
987 | /* Return the number of lines in the replacement text including context. */ | |
988 | ||
989 | LINENUM | |
990 | pch_repl_lines() | |
991 | { | |
992 | return p_repl_lines; | |
993 | } | |
994 | ||
995 | /* Return the number of lines in the whole hunk. */ | |
996 | ||
997 | LINENUM | |
998 | pch_end() | |
999 | { | |
1000 | return p_end; | |
1001 | } | |
1002 | ||
1003 | /* Return the number of context lines before the first changed line. */ | |
1004 | ||
1005 | LINENUM | |
1006 | pch_context() | |
1007 | { | |
1008 | return p_context; | |
1009 | } | |
1010 | ||
1011 | /* Return the length of a particular patch line. */ | |
1012 | ||
1013 | short | |
1014 | pch_line_len(line) | |
1015 | LINENUM line; | |
1016 | { | |
1017 | return p_len[line]; | |
1018 | } | |
1019 | ||
1020 | /* Return the control character (+, -, *, !, etc) for a patch line. */ | |
1021 | ||
1022 | char | |
1023 | pch_char(line) | |
1024 | LINENUM line; | |
1025 | { | |
1026 | return p_char[line]; | |
1027 | } | |
1028 | ||
1029 | /* Return a pointer to a particular patch line. */ | |
1030 | ||
1031 | char * | |
1032 | pfetch(line) | |
1033 | LINENUM line; | |
1034 | { | |
1035 | return p_line[line]; | |
1036 | } | |
1037 | ||
1038 | /* Return where in the patch file this hunk began, for error messages. */ | |
1039 | ||
1040 | LINENUM | |
1041 | pch_hunk_beg() | |
1042 | { | |
1043 | return p_hunk_beg; | |
1044 | } | |
1045 | ||
1046 | /* Apply an ed script by feeding ed itself. */ | |
1047 | ||
1048 | void | |
1049 | do_ed_script() | |
1050 | { | |
1051 | Reg1 char *t; | |
1052 | Reg2 long beginning_of_this_line; | |
1053 | Reg3 bool this_line_is_command = FALSE; | |
1054 | Reg4 FILE *pipefp; | |
1055 | FILE *popen(); | |
1056 | ||
1057 | if (!skip_rest_of_patch) { | |
1058 | Unlink(TMPOUTNAME); | |
1059 | copy_file(filearg[0], TMPOUTNAME); | |
1060 | if (verbose) | |
1061 | Sprintf(buf, "/bin/ed %s", TMPOUTNAME); | |
1062 | else | |
1063 | Sprintf(buf, "/bin/ed - %s", TMPOUTNAME); | |
1064 | pipefp = popen(buf, "w"); | |
1065 | } | |
1066 | for (;;) { | |
1067 | beginning_of_this_line = ftell(pfp); | |
1068 | if (pgets(buf, sizeof buf, pfp) == Nullch) { | |
1069 | next_intuit_at(beginning_of_this_line,p_input_line); | |
1070 | break; | |
1071 | } | |
1072 | p_input_line++; | |
1073 | for (t=buf; isdigit(*t) || *t == ','; t++) ; | |
1074 | this_line_is_command = (isdigit(*buf) && | |
1075 | (*t == 'd' || *t == 'c' || *t == 'a') ); | |
1076 | if (this_line_is_command) { | |
1077 | if (!skip_rest_of_patch) | |
1078 | fputs(buf, pipefp); | |
1079 | if (*t != 'd') { | |
1080 | while (pgets(buf, sizeof buf, pfp) != Nullch) { | |
1081 | p_input_line++; | |
1082 | if (!skip_rest_of_patch) | |
1083 | fputs(buf, pipefp); | |
1084 | if (strEQ(buf, ".\n")) | |
1085 | break; | |
1086 | } | |
1087 | } | |
1088 | } | |
1089 | else { | |
1090 | next_intuit_at(beginning_of_this_line,p_input_line); | |
1091 | break; | |
1092 | } | |
1093 | } | |
1094 | if (skip_rest_of_patch) | |
1095 | return; | |
1096 | fprintf(pipefp, "w\n"); | |
1097 | fprintf(pipefp, "q\n"); | |
1098 | Fflush(pipefp); | |
1099 | Pclose(pipefp); | |
1100 | ignore_signals(); | |
1101 | if (move_file(TMPOUTNAME, outname) < 0) { | |
1102 | toutkeep = TRUE; | |
1103 | chmod(TMPOUTNAME, filemode); | |
1104 | } | |
1105 | else | |
1106 | chmod(outname, filemode); | |
1107 | set_signals(); | |
1108 | } |