Annotation of src/usr.bin/patch/pch.c, Revision 1.27
1.23 joerg 1: /*
2: * $OpenBSD: pch.c,v 1.37 2007/09/02 15:19:33 deraadt Exp $
3: * $DragonFly: src/usr.bin/patch/pch.c,v 1.6 2008/08/10 23:35:40 joerg Exp $
1.27 ! christos 4: * $NetBSD: pch.c,v 1.26 2014/11/26 00:31:32 christos Exp $
1.23 joerg 5: */
1.18 itojun 6:
7: /*
1.23 joerg 8: * patch - a program to apply diffs to original files
9: *
10: * Copyright 1986, Larry Wall
11: *
1.18 itojun 12: * Redistribution and use in source and binary forms, with or without
1.23 joerg 13: * modification, are permitted provided that the following condition is met:
14: * 1. Redistributions of source code must retain the above copyright notice,
15: * this condition and the following disclaimer.
16: *
17: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
18: * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20: * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
21: * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24: * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
1.18 itojun 25: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27: * SUCH DAMAGE.
1.23 joerg 28: *
29: * -C option added in 1998, original code by Marc Espie, based on FreeBSD
30: * behaviour
1.18 itojun 31: */
32:
1.5 christos 33: #include <sys/cdefs.h>
1.27 ! christos 34: __RCSID("$NetBSD: pch.c,v 1.26 2014/11/26 00:31:32 christos Exp $");
1.23 joerg 35:
36: #include <sys/types.h>
37: #include <sys/stat.h>
38:
39: #include <ctype.h>
40: #include <libgen.h>
41: #include <limits.h>
42: #include <stdio.h>
43: #include <stdlib.h>
44: #include <string.h>
45: #include <unistd.h>
1.1 cgd 46:
47: #include "common.h"
48: #include "util.h"
49: #include "pch.h"
1.23 joerg 50: #include "pathnames.h"
1.5 christos 51:
1.1 cgd 52: /* Patch (diff listing) abstract type. */
53:
1.23 joerg 54: static long p_filesize; /* size of the patch file */
55: static LINENUM p_first; /* 1st line number */
56: static LINENUM p_newfirst; /* 1st line number of replacement */
57: static LINENUM p_ptrn_lines; /* # lines in pattern */
58: static LINENUM p_repl_lines; /* # lines in replacement text */
59: static LINENUM p_end = -1; /* last line in hunk */
60: static LINENUM p_max; /* max allowed value of p_end */
61: static LINENUM p_context = 3; /* # of context lines */
62: static LINENUM p_input_line = 0; /* current line # from patch file */
63: static char **p_line = NULL;/* the text of the hunk */
64: static short *p_len = NULL; /* length of each line */
65: static char *p_char = NULL; /* +, -, and ! */
66: static int hunkmax = INITHUNKMAX; /* size of above arrays to begin with */
67: static int p_indent; /* indent to patch */
68: static LINENUM p_base; /* where to intuit this time */
69: static LINENUM p_bline; /* line # of p_base */
70: static LINENUM p_start; /* where intuit found a patch */
71: static LINENUM p_sline; /* and the line number for it */
72: static LINENUM p_hunk_beg; /* line number of current hunk */
73: static LINENUM p_efake = -1; /* end of faked up lines--don't free */
74: static LINENUM p_bfake = -1; /* beg of faked up lines */
75: static FILE *pfp = NULL; /* patch file pointer */
76: static char *bestguess = NULL; /* guess at correct filename */
77:
78: static void grow_hunkmax(void);
79: static int intuit_diff_type(void);
80: static void next_intuit_at(LINENUM, LINENUM);
81: static void skip_to(LINENUM, LINENUM);
82: static char *pgets(char *, int, FILE *);
83: static char *best_name(const struct file_name *, bool);
84: static char *posix_name(const struct file_name *, bool);
85: static size_t num_components(const char *);
1.1 cgd 86:
1.23 joerg 87: /*
88: * Prepare to look for the next patch in the patch file.
89: */
1.1 cgd 90: void
1.8 kristerw 91: re_patch(void)
1.1 cgd 92: {
1.23 joerg 93: p_first = 0;
94: p_newfirst = 0;
95: p_ptrn_lines = 0;
96: p_repl_lines = 0;
97: p_end = (LINENUM) - 1;
98: p_max = 0;
1.10 kristerw 99: p_indent = 0;
1.1 cgd 100: }
101:
1.23 joerg 102: /*
1.10 kristerw 103: * Open the patch file at the beginning of time.
104: */
1.1 cgd 105: void
1.23 joerg 106: open_patch_file(const char *filename)
1.1 cgd 107: {
1.23 joerg 108: struct stat filestat;
109:
110: if (filename == NULL || *filename == '\0' || strEQ(filename, "-")) {
1.10 kristerw 111: pfp = fopen(TMPPATNAME, "w");
112: if (pfp == NULL)
113: pfatal("can't create %s", TMPPATNAME);
1.23 joerg 114: while (fgets(buf, buf_len, stdin) != NULL)
1.10 kristerw 115: fputs(buf, pfp);
1.23 joerg 116: if (ferror(pfp) || fclose(pfp))
117: pfatal("can't write %s", TMPPATNAME);
1.10 kristerw 118: filename = TMPPATNAME;
119: }
120: pfp = fopen(filename, "r");
1.9 kristerw 121: if (pfp == NULL)
1.10 kristerw 122: pfatal("patch file %s not found", filename);
1.23 joerg 123: fstat(fileno(pfp), &filestat);
1.10 kristerw 124: p_filesize = filestat.st_size;
1.23 joerg 125: next_intuit_at(0L, 1L); /* start at the beginning */
1.10 kristerw 126: set_hunkmax();
1.1 cgd 127: }
128:
1.10 kristerw 129: /*
130: * Make sure our dynamically realloced tables are malloced to begin with.
131: */
1.1 cgd 132: void
1.8 kristerw 133: set_hunkmax(void)
1.1 cgd 134: {
1.10 kristerw 135: if (p_line == NULL)
1.23 joerg 136: p_line = calloc((size_t) hunkmax, sizeof(char *));
1.10 kristerw 137: if (p_len == NULL)
1.23 joerg 138: p_len = calloc((size_t) hunkmax, sizeof(short));
1.10 kristerw 139: if (p_char == NULL)
1.23 joerg 140: p_char = calloc((size_t) hunkmax, sizeof(char));
1.1 cgd 141: }
142:
1.10 kristerw 143: /*
144: * Enlarge the arrays containing the current hunk of patch.
145: */
1.23 joerg 146: static void
1.8 kristerw 147: grow_hunkmax(void)
1.1 cgd 148: {
1.23 joerg 149: int new_hunkmax;
150: char **new_p_line;
151: short *new_p_len;
152: char *new_p_char;
153:
154: new_hunkmax = hunkmax * 2;
155:
156: if (p_line == NULL || p_len == NULL || p_char == NULL)
157: fatal("Internal memory allocation error\n");
158:
159: new_p_line = realloc(p_line, new_hunkmax * sizeof(char *));
160: if (new_p_line == NULL)
161: free(p_line);
162:
163: new_p_len = realloc(p_len, new_hunkmax * sizeof(short));
164: if (new_p_len == NULL)
165: free(p_len);
166:
167: new_p_char = realloc(p_char, new_hunkmax * sizeof(char));
168: if (new_p_char == NULL)
169: free(p_char);
170:
171: p_char = new_p_char;
172: p_len = new_p_len;
173: p_line = new_p_line;
1.12 kristerw 174:
1.23 joerg 175: if (p_line != NULL && p_len != NULL && p_char != NULL) {
176: hunkmax = new_hunkmax;
177: return;
178: }
179:
180: if (!using_plan_a)
181: fatal("out of memory\n");
182: out_of_mem = true; /* whatever is null will be allocated again */
183: /* from within plan_a(), of all places */
1.1 cgd 184: }
185:
1.23 joerg 186: /* True if the remainder of the patch file contains a diff of some sort. */
187:
1.1 cgd 188: bool
1.8 kristerw 189: there_is_another_patch(void)
1.1 cgd 190: {
1.23 joerg 191: bool exists = false;
192:
1.10 kristerw 193: if (p_base != 0L && p_base >= p_filesize) {
194: if (verbose)
195: say("done\n");
1.23 joerg 196: return false;
1.10 kristerw 197: }
1.1 cgd 198: if (verbose)
1.10 kristerw 199: say("Hmm...");
200: diff_type = intuit_diff_type();
201: if (!diff_type) {
202: if (p_base != 0L) {
203: if (verbose)
1.23 joerg 204: say(" Ignoring the trailing garbage.\ndone\n");
1.17 kristerw 205: } else
1.23 joerg 206: say(" I can't seem to find a patch in there anywhere.\n");
207: return false;
1.1 cgd 208: }
1.10 kristerw 209: if (verbose)
210: say(" %sooks like %s to me...\n",
211: (p_base == 0L ? "L" : "The next patch l"),
212: diff_type == UNI_DIFF ? "a unified diff" :
213: diff_type == CONTEXT_DIFF ? "a context diff" :
1.23 joerg 214: diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" :
1.10 kristerw 215: diff_type == NORMAL_DIFF ? "a normal diff" :
1.23 joerg 216: "an ed script");
1.10 kristerw 217: if (p_indent && verbose)
1.23 joerg 218: say("(Patch is indented %d space%s.)\n", p_indent,
219: p_indent == 1 ? "" : "s");
1.17 kristerw 220: skip_to(p_start, p_sline);
1.10 kristerw 221: while (filearg[0] == NULL) {
222: if (force || batch) {
223: say("No file to patch. Skipping...\n");
1.23 joerg 224: filearg[0] = savestr(bestguess);
225: skip_rest_of_patch = true;
226: return true;
1.10 kristerw 227: }
228: ask("File to patch: ");
229: if (*buf != '\n') {
1.23 joerg 230: free(bestguess);
231: bestguess = savestr(buf);
232: filearg[0] = fetchname(buf, &exists, 0);
1.10 kristerw 233: }
1.23 joerg 234: if (!exists) {
1.10 kristerw 235: ask("No file found--skip this patch? [n] ");
1.17 kristerw 236: if (*buf != 'y')
1.10 kristerw 237: continue;
238: if (verbose)
239: say("Skipping patch...\n");
1.23 joerg 240: free(filearg[0]);
241: filearg[0] = fetchname(bestguess, &exists, 0);
242: skip_rest_of_patch = true;
243: return true;
1.10 kristerw 244: }
1.1 cgd 245: }
1.23 joerg 246: return true;
1.1 cgd 247: }
248:
1.23 joerg 249: /* Determine what kind of diff is in the remaining part of the patch file. */
250:
251: static int
1.8 kristerw 252: intuit_diff_type(void)
1.1 cgd 253: {
1.23 joerg 254: long this_line = 0, previous_line;
255: long first_command_line = -1;
256: LINENUM fcl_line = -1;
257: bool last_line_was_command = false, this_is_a_command = false;
258: bool stars_last_line = false, stars_this_line = false;
259: char *s, *t;
260: int indent, retval;
261: struct file_name names[MAX_FILE];
262:
263: memset(names, 0, sizeof(names));
264: ok_to_create_file = false;
265: fseek(pfp, p_base, SEEK_SET);
1.10 kristerw 266: p_input_line = p_bline - 1;
267: for (;;) {
268: previous_line = this_line;
269: last_line_was_command = this_is_a_command;
270: stars_last_line = stars_this_line;
271: this_line = ftell(pfp);
272: indent = 0;
273: p_input_line++;
1.23 joerg 274: if (fgets(buf, buf_len, pfp) == NULL) {
1.10 kristerw 275: if (first_command_line >= 0L) {
276: /* nothing but deletes!? */
277: p_start = first_command_line;
278: p_sline = fcl_line;
279: retval = ED_DIFF;
280: goto scan_exit;
1.17 kristerw 281: } else {
1.10 kristerw 282: p_start = this_line;
283: p_sline = p_input_line;
284: retval = 0;
285: goto scan_exit;
286: }
287: }
288: for (s = buf; *s == ' ' || *s == '\t' || *s == 'X'; s++) {
289: if (*s == '\t')
290: indent += 8 - (indent % 8);
291: else
292: indent++;
293: }
1.17 kristerw 294: for (t = s; isdigit((unsigned char)*t) || *t == ','; t++)
295: ;
1.23 joerg 296: this_is_a_command = (isdigit((unsigned char)*s) &&
297: (*t == 'd' || *t == 'c' || *t == 'a'));
298: if (first_command_line < 0L && this_is_a_command) {
1.10 kristerw 299: first_command_line = this_line;
300: fcl_line = p_input_line;
301: p_indent = indent; /* assume this for now */
302: }
1.23 joerg 303: if (!stars_last_line && strnEQ(s, "*** ", 4))
304: names[OLD_FILE].path = fetchname(s + 4,
305: &names[OLD_FILE].exists, strippath);
306: else if (strnEQ(s, "--- ", 4))
307: names[NEW_FILE].path = fetchname(s + 4,
308: &names[NEW_FILE].exists, strippath);
309: else if (strnEQ(s, "+++ ", 4))
310: /* pretend it is the old name */
311: names[OLD_FILE].path = fetchname(s + 4,
312: &names[OLD_FILE].exists, strippath);
313: else if (strnEQ(s, "Index:", 6))
314: names[INDEX_FILE].path = fetchname(s + 6,
315: &names[INDEX_FILE].exists, strippath);
316: else if (strnEQ(s, "Prereq:", 7)) {
1.10 kristerw 317: for (t = s + 7; isspace((unsigned char)*t); t++)
318: ;
1.23 joerg 319: revision = savestr(t);
320: for (t = revision; *t && !isspace((unsigned char)*t); t++)
1.10 kristerw 321: ;
322: *t = '\0';
1.20 christos 323: if (*revision == '\0') {
1.10 kristerw 324: free(revision);
325: revision = NULL;
326: }
327: }
328: if ((!diff_type || diff_type == ED_DIFF) &&
329: first_command_line >= 0L &&
1.23 joerg 330: strEQ(s, ".\n")) {
1.10 kristerw 331: p_indent = indent;
332: p_start = first_command_line;
333: p_sline = fcl_line;
334: retval = ED_DIFF;
335: goto scan_exit;
336: }
1.23 joerg 337: if ((!diff_type || diff_type == UNI_DIFF) && strnEQ(s, "@@ -", 4)) {
338: if (strnEQ(s + 4, "0,0", 3))
339: ok_to_create_file = true;
1.10 kristerw 340: p_indent = indent;
341: p_start = this_line;
342: p_sline = p_input_line;
343: retval = UNI_DIFF;
344: goto scan_exit;
345: }
346: stars_this_line = strnEQ(s, "********", 8);
1.23 joerg 347: if ((!diff_type || diff_type == CONTEXT_DIFF) && stars_last_line &&
1.10 kristerw 348: strnEQ(s, "*** ", 4)) {
1.23 joerg 349: if (atol(s + 4) == 0)
350: ok_to_create_file = true;
1.10 kristerw 351: /*
352: * If this is a new context diff the character just
353: * before the newline is a '*'.
354: */
355: while (*s != '\n')
356: s++;
357: p_indent = indent;
358: p_start = previous_line;
359: p_sline = p_input_line - 1;
1.23 joerg 360: retval = (*(s - 1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF);
1.10 kristerw 361: goto scan_exit;
362: }
1.23 joerg 363: if ((!diff_type || diff_type == NORMAL_DIFF) &&
1.10 kristerw 364: last_line_was_command &&
1.23 joerg 365: (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2))) {
1.10 kristerw 366: p_start = previous_line;
367: p_sline = p_input_line - 1;
368: p_indent = indent;
369: retval = NORMAL_DIFF;
370: goto scan_exit;
371: }
372: }
1.23 joerg 373: scan_exit:
374: if (retval == UNI_DIFF) {
375: /* unswap old and new */
376: struct file_name tmp = names[OLD_FILE];
377: names[OLD_FILE] = names[NEW_FILE];
378: names[NEW_FILE] = tmp;
379: }
380: if (filearg[0] == NULL) {
381: if (posix)
382: filearg[0] = posix_name(names, ok_to_create_file);
383: else {
384: /* Ignore the Index: name for context diffs, like GNU */
385: if (names[OLD_FILE].path != NULL ||
386: names[NEW_FILE].path != NULL) {
387: free(names[INDEX_FILE].path);
388: names[INDEX_FILE].path = NULL;
389: }
390: filearg[0] = best_name(names, ok_to_create_file);
1.10 kristerw 391: }
1.1 cgd 392: }
1.23 joerg 393:
394: free(bestguess);
395: bestguess = NULL;
1.10 kristerw 396: if (filearg[0] != NULL)
1.23 joerg 397: bestguess = savestr(filearg[0]);
398: else if (!ok_to_create_file) {
399: /*
400: * We don't want to create a new file but we need a
401: * filename to set bestguess. Avoid setting filearg[0]
402: * so the file is not created automatically.
403: */
404: if (posix)
405: bestguess = posix_name(names, true);
406: else
407: bestguess = best_name(names, true);
408: }
409: free(names[OLD_FILE].path);
410: free(names[NEW_FILE].path);
411: free(names[INDEX_FILE].path);
1.10 kristerw 412: return retval;
1.1 cgd 413: }
414:
1.10 kristerw 415: /*
416: * Remember where this patch ends so we know where to start up again.
417: */
1.23 joerg 418: static void
419: next_intuit_at(LINENUM file_pos, LINENUM file_line)
1.1 cgd 420: {
1.10 kristerw 421: p_base = file_pos;
422: p_bline = file_line;
1.1 cgd 423: }
424:
1.10 kristerw 425: /*
426: * Basically a verbose fseek() to the actual diff listing.
427: */
1.23 joerg 428: static void
429: skip_to(LINENUM file_pos, LINENUM file_line)
1.1 cgd 430: {
1.23 joerg 431: char *ret;
1.1 cgd 432:
1.14 christos 433: if (p_base > file_pos)
1.23 joerg 434: fatal("Internal error: seek %ld>%ld\n", p_base, file_pos);
1.10 kristerw 435: if (verbose && p_base < file_pos) {
1.23 joerg 436: fseek(pfp, p_base, SEEK_SET);
437: say("The text leading up to this was:\n--------------------------\n");
1.10 kristerw 438: while (ftell(pfp) < file_pos) {
1.23 joerg 439: ret = fgets(buf, buf_len, pfp);
1.14 christos 440: if (ret == NULL)
441: fatal("Unexpected end of file\n");
1.10 kristerw 442: say("|%s", buf);
443: }
444: say("--------------------------\n");
1.23 joerg 445: } else
446: fseek(pfp, file_pos, SEEK_SET);
1.10 kristerw 447: p_input_line = file_line - 1;
1.1 cgd 448: }
449:
1.23 joerg 450: /* Make this a function for better debugging. */
1.24 joerg 451: __dead static void
1.8 kristerw 452: malformed(void)
1.1 cgd 453: {
1.23 joerg 454: fatal("malformed patch at line %ld: %s", p_input_line, buf);
455: /* about as informative as "Syntax error" in C */
1.1 cgd 456: }
457:
1.26 christos 458: static LINENUM
459: getlinenum(const char *s)
460: {
461: LINENUM l = (LINENUM)atol(s);
462: if (l < 0) {
463: l = 0;
464: malformed();
465: }
466: return l;
467: }
468:
469: static LINENUM
470: getskiplinenum(char **p)
471: {
472: char *s = *p;
473: LINENUM l = getlinenum(s);
474: while (isdigit((unsigned char)*s))
475: s++;
476: *p = s;
477: return l;
478: }
479:
1.10 kristerw 480: /*
1.25 wiz 481: * True if the line has been discarded (i.e., it is a line saying
1.13 kristerw 482: * "\ No newline at end of file".)
483: */
484: static bool
485: remove_special_line(void)
486: {
1.23 joerg 487: int c;
1.13 kristerw 488:
489: c = fgetc(pfp);
490: if (c == '\\') {
491: do {
492: c = fgetc(pfp);
493: } while (c != EOF && c != '\n');
494:
1.23 joerg 495: return true;
1.13 kristerw 496: }
497: if (c != EOF)
1.15 kristerw 498: fseek(pfp, -1L, SEEK_CUR);
1.13 kristerw 499:
1.23 joerg 500: return false;
1.13 kristerw 501: }
502:
503: /*
1.10 kristerw 504: * True if there is more of the current diff listing to process.
505: */
1.1 cgd 506: bool
1.8 kristerw 507: another_hunk(void)
1.1 cgd 508: {
1.23 joerg 509: long line_beginning; /* file pos of the current line */
510: LINENUM repl_beginning; /* index of --- line */
511: LINENUM fillcnt; /* #lines of missing ptrn or repl */
512: LINENUM fillsrc; /* index of first line to copy */
513: LINENUM filldst; /* index of first missing line */
514: bool ptrn_spaces_eaten; /* ptrn was slightly misformed */
515: bool repl_could_be_missing; /* no + or ! lines in this hunk */
516: bool repl_missing; /* we are now backtracking */
517: long repl_backtrack_position; /* file pos of first repl line */
518: LINENUM repl_patch_line; /* input line number for same */
519: LINENUM ptrn_copiable; /* # of copiable lines in ptrn */
520: char *s, *ret;
521: int context = 0;
522:
523: while (p_end >= 0) {
524: if (p_end == p_efake)
525: p_end = p_bfake; /* don't free twice */
526: else
527: free(p_line[p_end]);
528: p_end--;
529: }
530: p_efake = -1;
531:
532: p_max = hunkmax; /* gets reduced when --- found */
533: if (diff_type == CONTEXT_DIFF || diff_type == NEW_CONTEXT_DIFF) {
534: line_beginning = ftell(pfp);
535: repl_beginning = 0;
536: fillcnt = 0;
537: fillsrc = 0;
538: filldst = 0;
539: ptrn_spaces_eaten = false;
540: repl_could_be_missing = true;
541: repl_missing = false;
542: repl_backtrack_position = 0;
543: repl_patch_line = 0;
544: ptrn_copiable = 0;
545:
546: ret = pgets(buf, buf_len, pfp);
547: p_input_line++;
548: if (ret == NULL || strnNE(buf, "********", 8)) {
549: next_intuit_at(line_beginning, p_input_line);
550: return false;
551: }
552: p_context = 100;
553: p_hunk_beg = p_input_line + 1;
554: while (p_end < p_max) {
555: line_beginning = ftell(pfp);
556: ret = pgets(buf, buf_len, pfp);
557: p_input_line++;
558: if (ret == NULL) {
559: if (p_max - p_end < 4) {
560: /* assume blank lines got chopped */
561: strlcpy(buf, " \n", buf_len);
562: } else {
563: if (repl_beginning && repl_could_be_missing) {
564: repl_missing = true;
565: goto hunk_done;
566: }
567: fatal("unexpected end of file in patch\n");
568: }
569: }
570: p_end++;
571: if (p_end >= hunkmax)
572: fatal("Internal error: hunk larger than hunk "
573: "buffer size");
574: p_char[p_end] = *buf;
575: p_line[p_end] = NULL;
576: switch (*buf) {
577: case '*':
578: if (strnEQ(buf, "********", 8)) {
579: if (repl_beginning && repl_could_be_missing) {
580: repl_missing = true;
581: goto hunk_done;
582: } else
583: fatal("unexpected end of hunk "
584: "at line %ld\n",
585: p_input_line);
586: }
587: if (p_end != 0) {
588: if (repl_beginning && repl_could_be_missing) {
589: repl_missing = true;
590: goto hunk_done;
591: }
592: fatal("unexpected *** at line %ld: %s",
593: p_input_line, buf);
594: }
595: context = 0;
596: p_line[p_end] = savestr(buf);
597: if (out_of_mem) {
598: p_end--;
599: return false;
600: }
601: for (s = buf; *s && !isdigit((unsigned char)*s); s++)
602: ;
603: if (!*s)
604: malformed();
605: if (strnEQ(s, "0,0", 3))
606: memmove(s, s + 2, strlen(s + 2) + 1);
1.26 christos 607: p_first = getskiplinenum(&s);
1.23 joerg 608: if (*s == ',') {
609: for (; *s && !isdigit((unsigned char)*s); s++)
610: ;
611: if (!*s)
612: malformed();
1.26 christos 613: p_ptrn_lines = (getlinenum(s)) - p_first + 1;
614: if (p_ptrn_lines < 0)
615: malformed();
1.23 joerg 616: } else if (p_first)
617: p_ptrn_lines = 1;
618: else {
619: p_ptrn_lines = 0;
620: p_first = 1;
621: }
1.27 ! christos 622: if (p_first >= LINENUM_MAX - p_ptrn_lines ||
! 623: p_ptrn_lines >= LINENUM_MAX - 6)
1.26 christos 624: malformed();
1.23 joerg 625:
626: /* we need this much at least */
627: p_max = p_ptrn_lines + 6;
628: while (p_max >= hunkmax)
629: grow_hunkmax();
630: p_max = hunkmax;
631: break;
632: case '-':
633: if (buf[1] == '-') {
634: if (repl_beginning ||
635: (p_end != p_ptrn_lines + 1 +
636: (p_char[p_end - 1] == '\n'))) {
637: if (p_end == 1) {
638: /*
639: * `old' lines were omitted;
640: * set up to fill them in
641: * from 'new' context lines.
642: */
643: p_end = p_ptrn_lines + 1;
644: fillsrc = p_end + 1;
645: filldst = 1;
646: fillcnt = p_ptrn_lines;
647: } else {
648: if (repl_beginning) {
649: if (repl_could_be_missing) {
650: repl_missing = true;
651: goto hunk_done;
652: }
653: fatal("duplicate \"---\" at line %ld--check line numbers at line %ld\n",
654: p_input_line, p_hunk_beg + repl_beginning);
655: } else {
656: fatal("%s \"---\" at line %ld--check line numbers at line %ld\n",
657: (p_end <= p_ptrn_lines
658: ? "Premature"
659: : "Overdue"),
660: p_input_line, p_hunk_beg);
661: }
662: }
663: }
664: repl_beginning = p_end;
665: repl_backtrack_position = ftell(pfp);
666: repl_patch_line = p_input_line;
667: p_line[p_end] = savestr(buf);
668: if (out_of_mem) {
669: p_end--;
670: return false;
671: }
672: p_char[p_end] = '=';
673: for (s = buf; *s && !isdigit((unsigned char)*s); s++)
674: ;
675: if (!*s)
676: malformed();
1.26 christos 677: p_newfirst = getskiplinenum(&s);
1.23 joerg 678: if (*s == ',') {
679: for (; *s && !isdigit((unsigned char)*s); s++)
680: ;
681: if (!*s)
682: malformed();
1.26 christos 683: p_repl_lines = (getlinenum(s)) -
1.23 joerg 684: p_newfirst + 1;
1.26 christos 685: if (p_repl_lines < 0)
686: malformed();
1.23 joerg 687: } else if (p_newfirst)
688: p_repl_lines = 1;
689: else {
690: p_repl_lines = 0;
691: p_newfirst = 1;
692: }
1.26 christos 693: if (p_newfirst >= LINENUM_MAX - p_repl_lines ||
694: p_repl_lines >= LINENUM_MAX - p_end)
695: malformed();
1.23 joerg 696: p_max = p_repl_lines + p_end;
697: if (p_max > MAXHUNKSIZE)
698: fatal("hunk too large (%ld lines) at line %ld: %s",
699: p_max, p_input_line, buf);
700: while (p_max >= hunkmax)
701: grow_hunkmax();
702: if (p_repl_lines != ptrn_copiable &&
703: (p_context != 0 || p_repl_lines != 1))
704: repl_could_be_missing = false;
705: break;
706: }
707: goto change_line;
708: case '+':
709: case '!':
710: repl_could_be_missing = false;
711: change_line:
712: if (buf[1] == '\n' && canonicalize)
713: strlcpy(buf + 1, " \n", buf_len - 1);
714: if (!isspace((unsigned char)buf[1]) && buf[1] != '>' &&
715: buf[1] != '<' &&
716: repl_beginning && repl_could_be_missing) {
717: repl_missing = true;
718: goto hunk_done;
719: }
720: if (context >= 0) {
721: if (context < p_context)
722: p_context = context;
723: context = -1000;
724: }
725: p_line[p_end] = savestr(buf + 2);
726: if (out_of_mem) {
727: p_end--;
728: return false;
729: }
730: if (p_end == p_ptrn_lines) {
731: if (remove_special_line()) {
732: int len;
733:
734: len = strlen(p_line[p_end]) - 1;
735: (p_line[p_end])[len] = 0;
736: }
737: }
738: break;
739: case '\t':
740: case '\n': /* assume the 2 spaces got eaten */
741: if (repl_beginning && repl_could_be_missing &&
742: (!ptrn_spaces_eaten ||
743: diff_type == NEW_CONTEXT_DIFF)) {
744: repl_missing = true;
745: goto hunk_done;
746: }
747: p_line[p_end] = savestr(buf);
748: if (out_of_mem) {
749: p_end--;
750: return false;
751: }
752: if (p_end != p_ptrn_lines + 1) {
753: ptrn_spaces_eaten |= (repl_beginning != 0);
754: context++;
755: if (!repl_beginning)
756: ptrn_copiable++;
757: p_char[p_end] = ' ';
758: }
759: break;
760: case ' ':
761: if (!isspace((unsigned char)buf[1]) &&
762: repl_beginning && repl_could_be_missing) {
763: repl_missing = true;
764: goto hunk_done;
765: }
766: context++;
767: if (!repl_beginning)
768: ptrn_copiable++;
769: p_line[p_end] = savestr(buf + 2);
770: if (out_of_mem) {
771: p_end--;
772: return false;
773: }
774: break;
775: default:
776: if (repl_beginning && repl_could_be_missing) {
777: repl_missing = true;
778: goto hunk_done;
779: }
780: malformed();
781: }
782: /* set up p_len for strncmp() so we don't have to */
783: /* assume null termination */
784: if (p_line[p_end])
785: p_len[p_end] = strlen(p_line[p_end]);
786: else
787: p_len[p_end] = 0;
788: }
789:
790: hunk_done:
791: if (p_end >= 0 && !repl_beginning)
792: fatal("no --- found in patch at line %ld\n", pch_hunk_beg());
793:
794: if (repl_missing) {
795:
796: /* reset state back to just after --- */
797: p_input_line = repl_patch_line;
798: for (p_end--; p_end > repl_beginning; p_end--)
799: free(p_line[p_end]);
800: fseek(pfp, repl_backtrack_position, SEEK_SET);
801:
802: /* redundant 'new' context lines were omitted - set */
803: /* up to fill them in from the old file context */
804: if (!p_context && p_repl_lines == 1) {
805: p_repl_lines = 0;
806: p_max--;
807: }
808: fillsrc = 1;
809: filldst = repl_beginning + 1;
810: fillcnt = p_repl_lines;
811: p_end = p_max;
812: } else if (!p_context && fillcnt == 1) {
813: /* the first hunk was a null hunk with no context */
814: /* and we were expecting one line -- fix it up. */
815: while (filldst < p_end) {
816: p_line[filldst] = p_line[filldst + 1];
817: p_char[filldst] = p_char[filldst + 1];
818: p_len[filldst] = p_len[filldst + 1];
819: filldst++;
820: }
821: #if 0
822: repl_beginning--; /* this doesn't need to be fixed */
823: #endif
824: p_end--;
825: p_first++; /* do append rather than insert */
826: fillcnt = 0;
827: p_ptrn_lines = 0;
828: }
829: if (diff_type == CONTEXT_DIFF &&
830: (fillcnt || (p_first > 1 && ptrn_copiable > 2 * p_context))) {
831: if (verbose)
832: say("%s\n%s\n%s\n",
833: "(Fascinating--this is really a new-style context diff but without",
834: "the telltale extra asterisks on the *** line that usually indicate",
835: "the new style...)");
836: diff_type = NEW_CONTEXT_DIFF;
837: }
838: /* if there were omitted context lines, fill them in now */
839: if (fillcnt) {
840: p_bfake = filldst; /* remember where not to free() */
841: p_efake = filldst + fillcnt - 1;
842: while (fillcnt-- > 0) {
843: while (fillsrc <= p_end && p_char[fillsrc] != ' ')
844: fillsrc++;
845: if (fillsrc > p_end)
846: fatal("replacement text or line numbers mangled in hunk at line %ld\n",
847: p_hunk_beg);
848: p_line[filldst] = p_line[fillsrc];
849: p_char[filldst] = p_char[fillsrc];
850: p_len[filldst] = p_len[fillsrc];
851: fillsrc++;
852: filldst++;
853: }
854: while (fillsrc <= p_end && fillsrc != repl_beginning &&
855: p_char[fillsrc] != ' ')
856: fillsrc++;
857: #ifdef DEBUGGING
858: if (debug & 64)
859: printf("fillsrc %ld, filldst %ld, rb %ld, e+1 %ld\n",
860: fillsrc, filldst, repl_beginning, p_end + 1);
861: #endif
862: if (fillsrc != p_end + 1 && fillsrc != repl_beginning)
863: malformed();
864: if (filldst != p_end + 1 && filldst != repl_beginning)
865: malformed();
866: }
867: if (p_line[p_end] != NULL) {
868: if (remove_special_line()) {
869: p_len[p_end] -= 1;
870: (p_line[p_end])[p_len[p_end]] = 0;
871: }
872: }
873: } else if (diff_type == UNI_DIFF) {
874: LINENUM fillold; /* index of old lines */
875: LINENUM fillnew; /* index of new lines */
876: char ch;
877:
878: line_beginning = ftell(pfp); /* file pos of the current line */
879: ret = pgets(buf, buf_len, pfp);
880: p_input_line++;
881: if (ret == NULL || strnNE(buf, "@@ -", 4)) {
882: next_intuit_at(line_beginning, p_input_line);
883: return false;
1.1 cgd 884: }
1.23 joerg 885: s = buf + 4;
1.1 cgd 886: if (!*s)
1.23 joerg 887: malformed();
1.26 christos 888: p_first = getskiplinenum(&s);
889: if (*s == ',') {
1.23 joerg 890: s++;
1.26 christos 891: p_ptrn_lines = getskiplinenum(&s);
1.23 joerg 892: } else
893: p_ptrn_lines = 1;
1.26 christos 894: if (p_first >= LINENUM_MAX - p_ptrn_lines)
895: malformed();
1.23 joerg 896: if (*s == ' ')
897: s++;
898: if (*s != '+' || !*++s)
899: malformed();
1.26 christos 900: p_newfirst = getskiplinenum(&s);
901: if (*s == ',') {
1.17 kristerw 902: s++;
1.26 christos 903: p_repl_lines = getskiplinenum(&s);
1.23 joerg 904: } else
905: p_repl_lines = 1;
906: if (*s == ' ')
907: s++;
908: if (*s != '@')
1.17 kristerw 909: malformed();
1.26 christos 910: if (p_first >= LINENUM_MAX - p_ptrn_lines ||
911: p_newfirst > LINENUM_MAX - p_repl_lines ||
912: p_ptrn_lines >= LINENUM_MAX - p_repl_lines - 1)
913: malformed();
1.23 joerg 914: if (!p_ptrn_lines)
915: p_first++; /* do append rather than insert */
916: p_max = p_ptrn_lines + p_repl_lines + 1;
917: while (p_max >= hunkmax)
918: grow_hunkmax();
919: fillold = 1;
920: fillnew = fillold + p_ptrn_lines;
921: p_end = fillnew + p_repl_lines;
922: snprintf(buf, buf_len, "*** %ld,%ld ****\n", p_first,
923: p_first + p_ptrn_lines - 1);
924: p_line[0] = savestr(buf);
925: if (out_of_mem) {
926: p_end = -1;
927: return false;
928: }
929: p_char[0] = '*';
930: snprintf(buf, buf_len, "--- %ld,%ld ----\n", p_newfirst,
931: p_newfirst + p_repl_lines - 1);
932: p_line[fillnew] = savestr(buf);
933: if (out_of_mem) {
934: p_end = 0;
935: return false;
1.1 cgd 936: }
1.23 joerg 937: p_char[fillnew++] = '=';
938: p_context = 100;
939: context = 0;
940: p_hunk_beg = p_input_line + 1;
941: while (fillold <= p_ptrn_lines || fillnew <= p_end) {
942: line_beginning = ftell(pfp);
943: ret = pgets(buf, buf_len, pfp);
944: p_input_line++;
945: if (ret == NULL) {
946: if (p_max - fillnew < 3) {
947: /* assume blank lines got chopped */
948: strlcpy(buf, " \n", buf_len);
949: } else {
950: fatal("unexpected end of file in patch\n");
951: }
952: }
953: if (*buf == '\t' || *buf == '\n') {
954: ch = ' '; /* assume the space got eaten */
955: s = savestr(buf);
1.17 kristerw 956: } else {
1.23 joerg 957: ch = *buf;
958: s = savestr(buf + 1);
959: }
960: if (out_of_mem) {
961: while (--fillnew > p_ptrn_lines)
962: free(p_line[fillnew]);
963: p_end = fillold - 1;
964: return false;
965: }
966: switch (ch) {
967: case '-':
968: if (fillold > p_ptrn_lines) {
969: free(s);
970: p_end = fillnew - 1;
971: malformed();
972: }
973: p_char[fillold] = ch;
974: p_line[fillold] = s;
975: p_len[fillold++] = strlen(s);
976: if (fillold > p_ptrn_lines) {
977: if (remove_special_line()) {
978: p_len[fillold - 1] -= 1;
979: s[p_len[fillold - 1]] = 0;
980: }
981: }
982: break;
983: case '=':
984: ch = ' ';
985: /* FALL THROUGH */
986: case ' ':
987: if (fillold > p_ptrn_lines) {
988: free(s);
989: while (--fillnew > p_ptrn_lines)
990: free(p_line[fillnew]);
991: p_end = fillold - 1;
992: malformed();
993: }
994: context++;
995: p_char[fillold] = ch;
996: p_line[fillold] = s;
997: p_len[fillold++] = strlen(s);
998: s = savestr(s);
999: if (out_of_mem) {
1000: while (--fillnew > p_ptrn_lines)
1001: free(p_line[fillnew]);
1002: p_end = fillold - 1;
1003: return false;
1004: }
1005: if (fillold > p_ptrn_lines) {
1006: if (remove_special_line()) {
1007: p_len[fillold - 1] -= 1;
1008: s[p_len[fillold - 1]] = 0;
1009: }
1010: }
1011: /* FALL THROUGH */
1012: case '+':
1013: if (fillnew > p_end) {
1014: free(s);
1015: while (--fillnew > p_ptrn_lines)
1016: free(p_line[fillnew]);
1017: p_end = fillold - 1;
1018: malformed();
1019: }
1020: p_char[fillnew] = ch;
1021: p_line[fillnew] = s;
1022: p_len[fillnew++] = strlen(s);
1023: if (fillold > p_ptrn_lines) {
1024: if (remove_special_line()) {
1025: p_len[fillnew - 1] -= 1;
1026: s[p_len[fillnew - 1]] = 0;
1027: }
1028: }
1029: break;
1030: default:
1031: p_end = fillnew;
1032: malformed();
1.1 cgd 1033: }
1.23 joerg 1034: if (ch != ' ' && context > 0) {
1035: if (context < p_context)
1036: p_context = context;
1037: context = -1000;
1038: }
1039: } /* while */
1040: } else { /* normal diff--fake it up */
1041: char hunk_type;
1042: int i;
1043: LINENUM min, max;
1044:
1045: line_beginning = ftell(pfp);
1046: p_context = 0;
1047: ret = pgets(buf, buf_len, pfp);
1048: p_input_line++;
1049: if (ret == NULL || !isdigit((unsigned char)*buf)) {
1050: next_intuit_at(line_beginning, p_input_line);
1051: return false;
1052: }
1.26 christos 1053: s = buf;
1054: p_first = getskiplinenum(&s);
1.23 joerg 1055: if (*s == ',') {
1.26 christos 1056: s++;
1057: p_ptrn_lines = getskiplinenum(&s) - p_first + 1;
1.23 joerg 1058: } else
1059: p_ptrn_lines = (*s != 'a');
1.26 christos 1060: if (p_first >= LINENUM_MAX - p_ptrn_lines)
1061: malformed();
1062: hunk_type = *s++;
1.23 joerg 1063: if (hunk_type == 'a')
1064: p_first++; /* do append rather than insert */
1.26 christos 1065: min = getskiplinenum(&s);
1.23 joerg 1066: if (*s == ',')
1.26 christos 1067: max = getlinenum(++s);
1.23 joerg 1068: else
1069: max = min;
1.26 christos 1070: if (min < 0 || min > max || max - min == LINENUM_MAX)
1071: malformed();
1.23 joerg 1072: if (hunk_type == 'd')
1073: min++;
1074: p_end = p_ptrn_lines + 1 + max - min + 1;
1.26 christos 1075: p_newfirst = min;
1076: p_repl_lines = max - min + 1;
1077: if (p_newfirst > LINENUM_MAX - p_repl_lines ||
1078: p_ptrn_lines >= LINENUM_MAX - p_repl_lines - 1)
1079: malformed();
1080: p_end = p_ptrn_lines + p_repl_lines + 1;
1.23 joerg 1081: if (p_end > MAXHUNKSIZE)
1082: fatal("hunk too large (%ld lines) at line %ld: %s",
1083: p_end, p_input_line, buf);
1084: while (p_end >= hunkmax)
1.1 cgd 1085: grow_hunkmax();
1.23 joerg 1086: snprintf(buf, buf_len, "*** %ld,%ld\n", p_first,
1087: p_first + p_ptrn_lines - 1);
1088: p_line[0] = savestr(buf);
1089: if (out_of_mem) {
1090: p_end = -1;
1091: return false;
1092: }
1093: p_char[0] = '*';
1094: for (i = 1; i <= p_ptrn_lines; i++) {
1095: ret = pgets(buf, buf_len, pfp);
1096: p_input_line++;
1097: if (ret == NULL)
1098: fatal("unexpected end of file in patch at line %ld\n",
1099: p_input_line);
1100: if (*buf != '<')
1101: fatal("< expected at line %ld of patch\n",
1102: p_input_line);
1103: p_line[i] = savestr(buf + 2);
1104: if (out_of_mem) {
1105: p_end = i - 1;
1106: return false;
1.13 kristerw 1107: }
1.23 joerg 1108: p_len[i] = strlen(p_line[i]);
1109: p_char[i] = '-';
1.13 kristerw 1110: }
1111:
1112: if (remove_special_line()) {
1.23 joerg 1113: p_len[i - 1] -= 1;
1114: (p_line[i - 1])[p_len[i - 1]] = 0;
1.13 kristerw 1115: }
1.23 joerg 1116: if (hunk_type == 'c') {
1117: ret = pgets(buf, buf_len, pfp);
1118: p_input_line++;
1119: if (ret == NULL)
1120: fatal("unexpected end of file in patch at line %ld\n",
1121: p_input_line);
1122: if (*buf != '-')
1123: fatal("--- expected at line %ld of patch\n",
1124: p_input_line);
1125: }
1126: snprintf(buf, buf_len, "--- %ld,%ld\n", min, max);
1127: p_line[i] = savestr(buf);
1128: if (out_of_mem) {
1129: p_end = i - 1;
1130: return false;
1131: }
1132: p_char[i] = '=';
1133: for (i++; i <= p_end; i++) {
1134: ret = pgets(buf, buf_len, pfp);
1135: p_input_line++;
1136: if (ret == NULL)
1137: fatal("unexpected end of file in patch at line %ld\n",
1138: p_input_line);
1139: if (*buf != '>')
1140: fatal("> expected at line %ld of patch\n",
1141: p_input_line);
1142: p_line[i] = savestr(buf + 2);
1143: if (out_of_mem) {
1144: p_end = i - 1;
1145: return false;
1.13 kristerw 1146: }
1.23 joerg 1147: p_len[i] = strlen(p_line[i]);
1148: p_char[i] = '+';
1.13 kristerw 1149: }
1.23 joerg 1150:
1151: if (remove_special_line()) {
1152: p_len[i - 1] -= 1;
1153: (p_line[i - 1])[p_len[i - 1]] = 0;
1.13 kristerw 1154: }
1.23 joerg 1155: }
1156: if (reverse) /* backwards patch? */
1157: if (!pch_swap())
1158: say("Not enough memory to swap next hunk!\n");
1.1 cgd 1159: #ifdef DEBUGGING
1.23 joerg 1160: if (debug & 2) {
1161: int i;
1162: char special;
1163:
1164: for (i = 0; i <= p_end; i++) {
1165: if (i == p_ptrn_lines)
1166: special = '^';
1167: else
1168: special = ' ';
1169: fprintf(stderr, "%3d %c %c %s", i, p_char[i],
1170: special, p_line[i]);
1171: fflush(stderr);
1172: }
1.1 cgd 1173: }
1174: #endif
1.23 joerg 1175: if (p_end + 1 < hunkmax)/* paranoia reigns supreme... */
1176: p_char[p_end + 1] = '^'; /* add a stopper for apply_hunk */
1177: return true;
1.1 cgd 1178: }
1179:
1.10 kristerw 1180: /*
1181: * Input a line from the patch file, worrying about indentation.
1182: */
1.23 joerg 1183: static char *
1.8 kristerw 1184: pgets(char *bf, int sz, FILE *fp)
1.1 cgd 1185: {
1.23 joerg 1186: char *s, *ret = fgets(bf, sz, fp);
1187: int indent = 0;
1.10 kristerw 1188:
1189: if (p_indent && ret != NULL) {
1.17 kristerw 1190: for (s = buf;
1.23 joerg 1191: indent < p_indent && (*s == ' ' || *s == '\t' || *s == 'X');
1192: s++) {
1.10 kristerw 1193: if (*s == '\t')
1194: indent += 8 - (indent % 7);
1195: else
1196: indent++;
1197: }
1.23 joerg 1198: if (buf != s && strlcpy(buf, s, buf_len) >= buf_len)
1199: fatal("buffer too small in pgets()\n");
1.10 kristerw 1200: }
1201: return ret;
1202: }
1203:
1204: /*
1205: * Reverse the old and new portions of the current hunk.
1206: */
1.1 cgd 1207: bool
1.8 kristerw 1208: pch_swap(void)
1.1 cgd 1209: {
1.23 joerg 1210: char **tp_line; /* the text of the hunk */
1211: short *tp_len; /* length of each line */
1212: char *tp_char; /* +, -, and ! */
1213: LINENUM i;
1214: LINENUM n;
1215: bool blankline = false;
1216: char *s;
1.10 kristerw 1217:
1218: i = p_first;
1219: p_first = p_newfirst;
1220: p_newfirst = i;
1.23 joerg 1221:
1.10 kristerw 1222: /* make a scratch copy */
1.1 cgd 1223:
1.10 kristerw 1224: tp_line = p_line;
1225: tp_len = p_len;
1226: tp_char = p_char;
1.23 joerg 1227: p_line = NULL; /* force set_hunkmax to allocate again */
1.10 kristerw 1228: p_len = NULL;
1229: p_char = NULL;
1230: set_hunkmax();
1231: if (p_line == NULL || p_len == NULL || p_char == NULL) {
1.23 joerg 1232:
1233: free(p_line);
1.10 kristerw 1234: p_line = tp_line;
1.23 joerg 1235: free(p_len);
1.10 kristerw 1236: p_len = tp_len;
1.23 joerg 1237: free(p_char);
1.10 kristerw 1238: p_char = tp_char;
1.23 joerg 1239: return false; /* not enough memory to swap hunk! */
1.10 kristerw 1240: }
1241: /* now turn the new into the old */
1.1 cgd 1242:
1243: i = p_ptrn_lines + 1;
1.10 kristerw 1244: if (tp_char[i] == '\n') { /* account for possible blank line */
1.23 joerg 1245: blankline = true;
1.10 kristerw 1246: i++;
1247: }
1.23 joerg 1248: if (p_efake >= 0) { /* fix non-freeable ptr range */
1.10 kristerw 1249: if (p_efake <= i)
1250: n = p_end - i + 1;
1251: else
1252: n = -i;
1253: p_efake += n;
1254: p_bfake += n;
1255: }
1.17 kristerw 1256: for (n = 0; i <= p_end; i++, n++) {
1.10 kristerw 1257: p_line[n] = tp_line[i];
1258: p_char[n] = tp_char[i];
1259: if (p_char[n] == '+')
1260: p_char[n] = '-';
1261: p_len[n] = tp_len[i];
1262: }
1263: if (blankline) {
1264: i = p_ptrn_lines + 1;
1265: p_line[n] = tp_line[i];
1266: p_char[n] = tp_char[i];
1267: p_len[n] = tp_len[i];
1268: n++;
1269: }
1.14 christos 1270: if (p_char[0] != '=')
1.23 joerg 1271: fatal("Malformed patch at line %ld: expected '=' found '%c'\n",
1.14 christos 1272: p_input_line, p_char[0]);
1.10 kristerw 1273: p_char[0] = '*';
1.17 kristerw 1274: for (s = p_line[0]; *s; s++)
1.10 kristerw 1275: if (*s == '-')
1276: *s = '*';
1277:
1278: /* now turn the old into the new */
1279:
1.23 joerg 1280: if (p_char[0] != '*')
1281: fatal("Malformed patch at line %ld: expected '*' found '%c'\n",
1282: p_input_line, p_char[0]);
1.10 kristerw 1283: tp_char[0] = '=';
1.17 kristerw 1284: for (s = tp_line[0]; *s; s++)
1.10 kristerw 1285: if (*s == '*')
1286: *s = '-';
1.17 kristerw 1287: for (i = 0; n <= p_end; i++, n++) {
1.10 kristerw 1288: p_line[n] = tp_line[i];
1289: p_char[n] = tp_char[i];
1290: if (p_char[n] == '-')
1291: p_char[n] = '+';
1292: p_len[n] = tp_len[i];
1293: }
1.23 joerg 1294:
1.14 christos 1295: if (i != p_ptrn_lines + 1)
1.23 joerg 1296: fatal("Malformed patch at line %ld: expected %ld lines, "
1297: "got %ld\n",
1.14 christos 1298: p_input_line, p_ptrn_lines + 1, i);
1.23 joerg 1299:
1.10 kristerw 1300: i = p_ptrn_lines;
1301: p_ptrn_lines = p_repl_lines;
1302: p_repl_lines = i;
1.23 joerg 1303:
1304: free(tp_line);
1305: free(tp_len);
1306: free(tp_char);
1307:
1308: return true;
1.10 kristerw 1309: }
1310:
1311: /*
1312: * Return the specified line position in the old file of the old context.
1313: */
1.1 cgd 1314: LINENUM
1.8 kristerw 1315: pch_first(void)
1.1 cgd 1316: {
1.10 kristerw 1317: return p_first;
1.1 cgd 1318: }
1319:
1.10 kristerw 1320: /*
1321: * Return the number of lines of old context.
1322: */
1.1 cgd 1323: LINENUM
1.8 kristerw 1324: pch_ptrn_lines(void)
1.1 cgd 1325: {
1.10 kristerw 1326: return p_ptrn_lines;
1.1 cgd 1327: }
1328:
1.10 kristerw 1329: /*
1330: * Return the probable line position in the new file of the first line.
1331: */
1.1 cgd 1332: LINENUM
1.8 kristerw 1333: pch_newfirst(void)
1.1 cgd 1334: {
1.10 kristerw 1335: return p_newfirst;
1.1 cgd 1336: }
1337:
1.10 kristerw 1338: /*
1339: * Return the number of lines in the replacement text including context.
1340: */
1.1 cgd 1341: LINENUM
1.8 kristerw 1342: pch_repl_lines(void)
1.1 cgd 1343: {
1.10 kristerw 1344: return p_repl_lines;
1.1 cgd 1345: }
1346:
1.10 kristerw 1347: /*
1348: * Return the number of lines in the whole hunk.
1349: */
1.1 cgd 1350: LINENUM
1.8 kristerw 1351: pch_end(void)
1.1 cgd 1352: {
1.10 kristerw 1353: return p_end;
1.1 cgd 1354: }
1355:
1.10 kristerw 1356: /*
1357: * Return the number of context lines before the first changed line.
1358: */
1.1 cgd 1359: LINENUM
1.8 kristerw 1360: pch_context(void)
1.1 cgd 1361: {
1.10 kristerw 1362: return p_context;
1.1 cgd 1363: }
1364:
1.10 kristerw 1365: /*
1366: * Return the length of a particular patch line.
1367: */
1.23 joerg 1368: short
1.8 kristerw 1369: pch_line_len(LINENUM line)
1.1 cgd 1370: {
1.10 kristerw 1371: return p_len[line];
1.1 cgd 1372: }
1373:
1.10 kristerw 1374: /*
1375: * Return the control character (+, -, *, !, etc) for a patch line.
1376: */
1.1 cgd 1377: char
1.8 kristerw 1378: pch_char(LINENUM line)
1.1 cgd 1379: {
1.10 kristerw 1380: return p_char[line];
1.1 cgd 1381: }
1382:
1.10 kristerw 1383: /*
1384: * Return a pointer to a particular patch line.
1385: */
1.1 cgd 1386: char *
1.8 kristerw 1387: pfetch(LINENUM line)
1.1 cgd 1388: {
1.10 kristerw 1389: return p_line[line];
1.1 cgd 1390: }
1391:
1.10 kristerw 1392: /*
1393: * Return where in the patch file this hunk began, for error messages.
1394: */
1.1 cgd 1395: LINENUM
1.8 kristerw 1396: pch_hunk_beg(void)
1.1 cgd 1397: {
1.10 kristerw 1398: return p_hunk_beg;
1.1 cgd 1399: }
1400:
1.10 kristerw 1401: /*
1402: * Apply an ed script by feeding ed itself.
1403: */
1.1 cgd 1404: void
1.8 kristerw 1405: do_ed_script(void)
1.1 cgd 1406: {
1.23 joerg 1407: char *t;
1408: long beginning_of_this_line;
1409: FILE *pipefp = NULL;
1.10 kristerw 1410:
1411: if (!skip_rest_of_patch) {
1.23 joerg 1412: if (copy_file(filearg[0], TMPOUTNAME) < 0) {
1413: unlink(TMPOUTNAME);
1414: fatal("can't create temp file %s", TMPOUTNAME);
1415: }
1416: snprintf(buf, buf_len, "%s%s%s", _PATH_ED,
1417: verbose ? " " : " -s ", TMPOUTNAME);
1.10 kristerw 1418: pipefp = popen(buf, "w");
1419: }
1420: for (;;) {
1421: beginning_of_this_line = ftell(pfp);
1.23 joerg 1422: if (pgets(buf, buf_len, pfp) == NULL) {
1.17 kristerw 1423: next_intuit_at(beginning_of_this_line, p_input_line);
1.10 kristerw 1424: break;
1425: }
1426: p_input_line++;
1.17 kristerw 1427: for (t = buf; isdigit((unsigned char)*t) || *t == ','; t++)
1428: ;
1.23 joerg 1429: /* POSIX defines allowed commands as {a,c,d,i,s} */
1430: if (isdigit((unsigned char)*buf) && (*t == 'a' || *t == 'c' ||
1431: *t == 'd' || *t == 'i' || *t == 's')) {
1432: if (pipefp != NULL)
1.10 kristerw 1433: fputs(buf, pipefp);
1434: if (*t != 'd') {
1.23 joerg 1435: while (pgets(buf, buf_len, pfp) != NULL) {
1.10 kristerw 1436: p_input_line++;
1.23 joerg 1437: if (pipefp != NULL)
1.10 kristerw 1438: fputs(buf, pipefp);
1439: if (strEQ(buf, ".\n"))
1440: break;
1441: }
1442: }
1.17 kristerw 1443: } else {
1.23 joerg 1444: next_intuit_at(beginning_of_this_line, p_input_line);
1.1 cgd 1445: break;
1446: }
1447: }
1.23 joerg 1448: if (pipefp == NULL)
1.10 kristerw 1449: return;
1450: fprintf(pipefp, "w\n");
1451: fprintf(pipefp, "q\n");
1.23 joerg 1452: fflush(pipefp);
1453: pclose(pipefp);
1.10 kristerw 1454: ignore_signals();
1.23 joerg 1455: if (!check_only) {
1456: if (move_file(TMPOUTNAME, outname) < 0) {
1457: toutkeep = true;
1458: chmod(TMPOUTNAME, filemode);
1459: } else
1460: chmod(outname, filemode);
1461: }
1.10 kristerw 1462: set_signals(1);
1.1 cgd 1463: }
1.23 joerg 1464:
1465: /*
1466: * Choose the name of the file to be patched based on POSIX rules.
1467: * NOTE: the POSIX rules are amazingly stupid and we only follow them
1468: * if the user specified --posix or set POSIXLY_CORRECT.
1469: */
1470: static char *
1471: posix_name(const struct file_name *names, bool assume_exists)
1472: {
1473: char *path = NULL;
1474: int i;
1475:
1476: /*
1477: * POSIX states that the filename will be chosen from one
1478: * of the old, new and index names (in that order) if
1479: * the file exists relative to CWD after -p stripping.
1480: */
1481: for (i = 0; i < MAX_FILE; i++) {
1482: if (names[i].path != NULL && names[i].exists) {
1483: path = names[i].path;
1484: break;
1485: }
1486: }
1487: if (path == NULL && !assume_exists) {
1488: /*
1489: * No files found, look for something we can checkout from
1490: * RCS/SCCS dirs. Same order as above.
1491: */
1492: for (i = 0; i < MAX_FILE; i++) {
1493: if (names[i].path != NULL &&
1494: (path = checked_in(names[i].path)) != NULL)
1495: break;
1496: }
1497: /*
1498: * Still no match? Check to see if the diff could be creating
1499: * a new file.
1500: */
1501: if (path == NULL && ok_to_create_file &&
1502: names[NEW_FILE].path != NULL)
1503: path = names[NEW_FILE].path;
1504: }
1505:
1506: return path ? savestr(path) : NULL;
1507: }
1508:
1509: /*
1510: * Choose the name of the file to be patched based the "best" one
1511: * available.
1512: */
1513: static char *
1514: best_name(const struct file_name *names, bool assume_exists)
1515: {
1516: size_t min_components, min_baselen, min_len, tmp;
1517: char *best = NULL;
1518: int i;
1519:
1520: /*
1521: * The "best" name is the one with the fewest number of path
1522: * components, the shortest basename length, and the shortest
1523: * overall length (in that order). We only use the Index: file
1524: * if neither of the old or new files could be intuited from
1525: * the diff header.
1526: */
1527: min_components = min_baselen = min_len = SIZE_MAX;
1528: for (i = INDEX_FILE; i >= OLD_FILE; i--) {
1529: if (names[i].path == NULL ||
1530: (!names[i].exists && !assume_exists))
1531: continue;
1532: if ((tmp = num_components(names[i].path)) > min_components)
1533: continue;
1534: min_components = tmp;
1535: if ((tmp = strlen(basename(names[i].path))) > min_baselen)
1536: continue;
1537: min_baselen = tmp;
1538: if ((tmp = strlen(names[i].path)) > min_len)
1539: continue;
1540: min_len = tmp;
1541: best = names[i].path;
1542: }
1543: if (best == NULL) {
1544: /*
1545: * No files found, look for something we can checkout from
1546: * RCS/SCCS dirs. Logic is identical to that above...
1547: */
1548: min_components = min_baselen = min_len = SIZE_MAX;
1549: for (i = INDEX_FILE; i >= OLD_FILE; i--) {
1550: if (names[i].path == NULL ||
1551: checked_in(names[i].path) == NULL)
1552: continue;
1553: if ((tmp = num_components(names[i].path)) > min_components)
1554: continue;
1555: min_components = tmp;
1556: if ((tmp = strlen(basename(names[i].path))) > min_baselen)
1557: continue;
1558: min_baselen = tmp;
1559: if ((tmp = strlen(names[i].path)) > min_len)
1560: continue;
1561: min_len = tmp;
1562: best = names[i].path;
1563: }
1564: /*
1565: * Still no match? Check to see if the diff could be creating
1566: * a new file.
1567: */
1568: if (best == NULL && ok_to_create_file &&
1569: names[NEW_FILE].path != NULL)
1570: best = names[NEW_FILE].path;
1571: }
1572:
1573: return best ? savestr(best) : NULL;
1574: }
1575:
1576: static size_t
1577: num_components(const char *path)
1578: {
1579: size_t n;
1580: const char *cp;
1581:
1582: for (n = 0, cp = path; (cp = strchr(cp, '/')) != NULL; n++, cp++) {
1583: while (*cp == '/')
1584: cp++; /* skip consecutive slashes */
1585: }
1586: return n;
1587: }
CVSweb <webmaster@jp.NetBSD.org>