Annotation of src/usr.bin/sdiff/sdiff.c, Revision 1.5
1.5 ! tedu 1: /* $OpenBSD: sdiff.c,v 1.4 2005/12/27 04:28:08 tedu Exp $ */
1.1 tedu 2:
3: /*
4: * Written by Raymond Lai <ray@cyth.net>.
5: * Public domain.
6: */
7:
8: #include <sys/param.h>
9: #include <sys/queue.h>
10: #include <sys/types.h>
11: #include <sys/wait.h>
12:
13: #include <assert.h>
14: #include <ctype.h>
15: #include <err.h>
16: #include <getopt.h>
17: #include <limits.h>
18: #include <stdio.h>
19: #include <stdlib.h>
20: #include <string.h>
21: #include <unistd.h>
22: #include <util.h>
23:
24: #include "extern.h"
25:
26: #define WIDTH 130
27: /*
28: * Each column must be at least one character wide, plus three
29: * characters between the columns (space, [<|>], space).
30: */
31: #define WIDTH_MIN 5
32:
33: /* A single diff line. */
34: struct diffline {
35: SIMPLEQ_ENTRY(diffline) diffentries;
36: const char *left;
37: char div;
38: const char *right;
39: };
40:
41: static void astrcat(char **, const char *);
42: static void enqueue(const char *, const char, const char *);
43: static void freediff(const struct diffline *);
44: static void int_usage(void);
45: static int parsecmd(FILE *, FILE *);
46: static void printa(FILE *, size_t);
47: static void printc(FILE *, size_t, FILE *, size_t);
48: static void printcol(const char *, size_t *, const size_t);
49: static void printd(FILE *, FILE *, size_t);
50: static void println(const char *, const char, const char *);
51: static void processq(void);
52: static void prompt(const char *, const char *);
53: static void undiff(char *);
54: __dead static void usage(void);
55: static char *xfgets(FILE *);
56: static size_t xstrtonum(const char *);
57:
58: SIMPLEQ_HEAD(, diffline) diffhead = SIMPLEQ_HEAD_INITIALIZER(diffhead);
59: size_t line_width; /* width of a line (two columns and divider) */
60: size_t width; /* width of each column */
61: size_t file1ln, file2ln; /* line number of file1 and file2 */
62: int Dflag; /* debug - verify lots of things */
63: int lflag; /* print only left column for identical lines */
64: int sflag; /* skip identical lines */
1.3 tedu 65: FILE *outfile; /* file to save changes to */
1.1 tedu 66:
67: static struct option longopts[] = {
68: { "text", no_argument, NULL, 'a' },
69: { "ignore-blank-lines", no_argument, NULL, 'B' },
70: { "ignore-space-change", no_argument, NULL, 'b' },
71: { "minimal", no_argument, NULL, 'd' },
72: { "ignore-tab-expansion", no_argument, NULL, 'E' },
73: { "diff-program", required_argument, NULL, 'F' },
74: { "speed-large-files", no_argument, NULL, 'H' },
75: { "ignore-matching-lines", required_argument, NULL, 'I' },
76: { "left-column", no_argument, NULL, 'l' },
77: { "output", required_argument, NULL, 'o' },
78: { "strip-trailing-cr", no_argument, NULL, 'S' },
79: { "suppress-common-lines", no_argument, NULL, 's' },
80: { "expand-tabs", no_argument, NULL, 't' },
81: { "ignore-all-space", no_argument, NULL, 'W' },
82: { "width", required_argument, NULL, 'w' },
83: { NULL, 0, NULL, 0 }
84: };
85:
86: int
87: main(int argc, char **argv)
88: {
89: FILE *difffile, *origfile;
90: size_t argc_max, diffargc, wflag;
91: int ch, fd[2], status;
92: pid_t pid;
93: const char *cmd, **diffargv, *diffprog;
94:
95: /* Initialize variables. */
96: diffargc = 0;
97: diffprog = "diff";
98: outfile = NULL;
99: wflag = WIDTH;
100:
101: /*
102: * Process diff flags.
103: */
104: /*
105: * Allocate memory for diff arguments and NULL.
106: * Each flag has at most one argument, so doubling argc gives an
107: * upper limit of how many diff args can be passed. argv[0],
108: * file1, and file2 won't have arguments so doubling them will
109: * waste some memory; however we need an extra space for the
110: * NULL at the end, so it sort of works out.
111: */
112: argc_max = argc * 2;
113: if (!(diffargv = malloc(sizeof(char **) * argc_max)))
114: err(2, "out of memory");
115:
116: /* Add first argument, the program name. */
117: diffargv[diffargc++] = diffprog;
118:
119: while ((ch = getopt_long(argc, argv, "aBbDdEHI:ilo:stWw:",
120: longopts, NULL)) != -1) {
121: const char *errstr;
122:
123: switch (ch) {
124: case 'a':
125: diffargv[diffargc++] = "-a";
126: break;
127: case 'B':
128: diffargv[diffargc++] = "-B";
129: break;
130: case 'b':
131: diffargv[diffargc++] = "-b";
132: break;
133: case 'D':
134: Dflag = 1;
135: break;
136: case 'd':
137: diffargv[diffargc++] = "-d";
138: break;
139: case 'E':
140: diffargv[diffargc++] = "-E";
141: break;
142: case 'F':
1.4 tedu 143: diffargv[0] = diffprog = optarg;
1.1 tedu 144: break;
145: case 'H':
146: diffargv[diffargc++] = "-H";
147: break;
148: case 'I':
149: diffargv[diffargc++] = "-I";
150: diffargv[diffargc++] = optarg;
151: break;
152: case 'i':
153: diffargv[diffargc++] = "-i";
154: break;
155: case 'l':
156: lflag = 1;
157: break;
158: case 'o':
159: if ((outfile = fopen(optarg, "w")) == NULL)
160: err(2, "could not open: %s", optarg);
161: break;
162: case 'S':
163: diffargv[diffargc++] = "--strip-trailing-cr";
164: break;
165: case 's':
166: sflag = 1;
167: break;
168: case 't':
169: diffargv[diffargc++] = "-t";
170: break;
171: case 'W':
172: diffargv[diffargc++] = "-w";
173: break;
174: case 'w':
175: wflag = strtonum(optarg, WIDTH_MIN,
176: (MIN(SIZE_T_MAX, LLONG_MAX)), &errstr);
177: if (errstr)
178: errx(2, "width is %s: %s", errstr, optarg);
179: break;
180: default:
181: usage();
182: /* NOTREACHED */
183: }
184:
185: }
186: argc -= optind;
187: argv += optind;
188:
1.4 tedu 189: if (argc != 2) {
190: usage();
191: /* NOTREACHED */
192: }
193:
1.1 tedu 194: /* file1 */
195: diffargv[diffargc++] = argv[0];
196: /* file2 */
197: diffargv[diffargc++] = argv[1];
198: /* Add NULL to end of array to indicate end of array. */
199: diffargv[diffargc++] = NULL;
200:
201: /* Subtract column divider and divide by two. */
202: width = (wflag - 3) / 2;
203: if (Dflag)
204: assert(width > 0);
205: /* Make sure line_width can fit in size_t. */
206: if (width > (SIZE_T_MAX - 3) / 2)
207: errx(2, "width is too large: %zu", width);
208: line_width = width * 2 + 3;
209:
210: if (pipe(fd))
211: err(2, "pipe");
212:
213: switch(pid = fork()) {
214: case 0:
215: /* child */
216: /* We don't read from the pipe. */
1.5 ! tedu 217: close(fd[0]);
1.1 tedu 218: if (dup2(fd[1], STDOUT_FILENO) == -1)
219: err(2, "child could not duplicate descriptor");
220: /* Free unused descriptor. */
1.5 ! tedu 221: close(fd[1]);
1.1 tedu 222:
223: execvp(diffprog, (char *const *)diffargv);
224: err(2, "could not execute diff: %s", diffprog);
225: case -1:
226: err(2, "could not fork");
227: }
228:
229: /* parent */
230: /* We don't write to the pipe. */
1.5 ! tedu 231: close(fd[1]);
1.1 tedu 232:
233: /* Open pipe to diff command. */
234: if ((difffile = fdopen(fd[0], "r")) == NULL)
235: err(2, "could not open diff pipe");
236: /* If file1 was given as `-', open stdin. */
237: /* XXX - Does not work. */
238: if (strcmp(argv[0], "-") == 0)
239: origfile = stdin;
240: /* Otherwise, open as normal file. */
241: else if ((origfile = fopen(argv[0], "r")) == NULL)
242: err(2, "could not open file1: %s", argv[0]);
243: /* Line numbers start at one. */
244: file1ln = file2ln = 1;
245:
246: /* Read and parse diff output. */
247: while (parsecmd(difffile, origfile) != EOF)
248: ;
1.5 ! tedu 249: fclose(difffile);
1.1 tedu 250:
251: /* Wait for diff to exit. */
252: if (waitpid(pid, &status, 0) == -1 || !WIFEXITED(status) ||
253: WEXITSTATUS(status) >= 2)
254: err(2, "diff exited abnormally");
255:
256: /* No more diffs, so print common lines. */
257: while ((cmd = xfgets(origfile)))
258: enqueue(cmd, ' ', lflag ? NULL : cmd);
1.5 ! tedu 259: fclose(origfile);
1.1 tedu 260: /* Process unmodified lines. */
261: processq();
262:
263: /* Return diff exit status. */
264: return (WEXITSTATUS(status));
265: }
266:
267: /*
268: * Takes a string nptr and returns a numeric value. The first character
269: * must be a digit. Parsing ends when a non-numerical character is
270: * reached.
271: */
272: static size_t
273: xstrtonum(const char *nptr)
274: {
275: size_t n;
276: const char *errstr;
277: char *copy, *ptr;
278:
279: /* Make copy of numeric string. */
280: if ((copy = strdup(nptr)) == NULL)
281: err(2, "out of memory");
282:
283: /* Look for first non-digit. */
284: for (ptr = copy; isdigit(*ptr); ++ptr)
285: ;
286:
287: /* End string at first non-digit. */
288: if (*ptr != '\0')
289: *ptr = '\0';
290:
291: /* Parse number. */
292: /* XXX - Is it safe to compare SIZE_T_MAX and LLONG_MAX? */
293: n = strtonum(copy, 0, MIN(SIZE_T_MAX, LLONG_MAX), &errstr);
294: if (errstr)
295: errx(2, "line number in diff is %s: %s", errstr, nptr);
296:
297: /* Free copy of numeric string. */
298: free(copy);
299:
300: return (n);
301: }
302:
303: /*
304: * Prints an individual column (left or right), taking into account
305: * that tabs are variable-width. Takes a string, the current column
306: * the cursor is on the screen, and the maximum value of the column.
307: * The column value is updated as we go along.
308: */
309: static void
310: printcol(const char *s, size_t *col, const size_t col_max)
311: {
312: if (Dflag) {
313: assert(s);
314: assert(*col <= col_max);
315: }
316:
317: for (; *s && *col < col_max; ++s) {
318: size_t new_col;
319:
320: if (Dflag)
321: assert(*s != '\n');
322:
323: switch (*s) {
324: case '\t':
325: /*
326: * If rounding to next multiple of eight causes
327: * an integer overflow, just return.
328: */
329: if (*col > SIZE_T_MAX - 8)
330: return;
331:
332: /* Round to next multiple of eight. */
333: new_col = (*col / 8 + 1) * 8;
334:
335: /*
336: * If printing the tab goes past the column
337: * width, don't print it and just quit.
338: */
339: if (new_col > col_max)
340: return;
341: *col = new_col;
342: break;
343:
344: default:
345: ++(*col);
346: }
347:
348: putchar(*s);
349: }
350: }
351:
352: /*
353: * Prompts user to either choose between two strings or edit one, both,
354: * or neither.
355: */
356: static void
357: prompt(const char *s1, const char *s2)
358: {
359: const char *cmd;
360:
361: /* Print command prompt. */
362: putchar('%');
363:
364: /* Get user input. */
1.3 tedu 365: for (; (cmd = xfgets(stdin)); free((void *)cmd)) {
1.1 tedu 366: const char *p;
367:
368: /* Skip leading whitespace. */
369: for (p = cmd; isspace(*p); ++p)
370: ;
371:
372: switch (*p) {
373: case 'e':
374: /* Skip `e'. */
375: ++p;
376:
377: if (eparse(p, s1, s2) == -1)
378: goto USAGE;
379: break;
380:
381: case 'l':
382: /* Choose left column as-is. */
383: if (s1 != NULL)
384: fprintf(outfile, "%s\n", s1);
385:
386: /* End of command parsing. */
387: break;
388:
389: case 'q':
390: goto QUIT;
391:
392: case 'r':
393: /* Choose right column as-is. */
394: if (s2 != NULL)
395: fprintf(outfile, "%s\n", s2);
396:
397: /* End of command parsing. */
398: break;
399:
400: case 's':
401: sflag = 1;
402: goto PROMPT;
403:
404: case 'v':
405: sflag = 0;
406: /* FALLTHROUGH */
407:
408: default:
409: /* Interactive usage help. */
410: USAGE:
411: int_usage();
412: PROMPT:
413: putchar('%');
414:
415: /* Prompt user again. */
416: continue;
417: }
418:
1.3 tedu 419: free((void *)cmd);
1.1 tedu 420: return;
421: }
422:
423: /*
424: * If there was no error, we received an EOF from stdin, so we
425: * should quit.
426: */
427: QUIT:
1.5 ! tedu 428: fclose(outfile);
1.1 tedu 429: exit(0);
430: }
431:
432: /*
433: * Takes two strings, separated by a column divider. NULL strings are
434: * treated as empty columns. If the divider is the ` ' character, the
435: * second column is not printed (-l flag). In this case, the second
436: * string must be NULL. When the second column is NULL, the divider
437: * does not print the trailing space following the divider character.
438: *
439: * Takes into account that tabs can take multiple columns.
440: */
441: static void
442: println(const char *s1, const char div, const char *s2)
443: {
444: size_t col;
445:
446: if (Dflag) {
447: /* These are the only legal column dividers. */
448: assert(div == '<' || div == '|' || div == '>' || div == ' ');
449: /* These are the only valid combinations. */
450: assert((s1 != NULL && div == '<' && s2 == NULL) || div != '<');
451: assert((s1 == NULL && div == '>' && s2 != NULL) || div != '>');
452: assert((s1 != NULL && div == '|' && s2 != NULL && s2 != s1) ||
453: div != '|');
454: assert((s1 != NULL && div == ' ' && (s2 == s1 || s2 == NULL)) ||
455: div != ' ');
456: }
457:
458: /* Print first column. Skips if s1 == NULL. */
459: col = 0;
460: if (s1) {
461: /* Skip angle bracket and space. */
462: printcol(s1, &col, width);
463:
464: /* We should never exceed the width. */
465: if (Dflag)
466: assert(col <= width);
467: }
468:
469: /* Only print left column. */
470: if (div == ' ' && !s2) {
471: putchar('\n');
472: return;
473: }
474:
475: /* Otherwise, we pad this column up to width. */
476: for (; col < width; ++col)
477: putchar(' ');
478:
479: /*
480: * Print column divider. If there is no second column, we don't
481: * need to add the space for padding.
482: */
483: if (!s2) {
484: printf(" %c\n", div);
485: return;
486: }
487: printf(" %c ", div);
488: col += 3;
489:
490: /* Skip angle bracket and space. */
491: printcol(s2, &col, line_width);
492:
493: /* We should never exceed the line width. */
494: if (Dflag)
495: assert(col <= line_width);
496:
497: putchar('\n');
498: }
499:
500: /*
501: * Reads a line from file and returns as a string. If EOF is reached,
502: * NULL is returned. The returned string must be freed afterwards.
503: */
504: static char *
505: xfgets(FILE *file)
506: {
507: const char delim[3] = {'\0', '\0', '\0'};
508: char *s;
509:
510: if (Dflag)
511: assert(file);
512:
513: /* XXX - Is this necessary? */
514: clearerr(file);
515:
516: if (!(s = fparseln(file, NULL, NULL, delim, 0)) &&
517: ferror(file))
518: err(2, "error reading file");
519:
520: if (!s) {
521: /* NULL from fparseln() should mean EOF. */
522: if (Dflag)
523: assert(feof(file));
524:
525: return (NULL);
526: }
527:
528: return (s);
529: }
530:
531: /*
532: * Parse ed commands from diff and print lines from difffile
533: * (lines to add or change) or origfile (lines to change or delete).
534: * Returns EOF or not.
535: */
536: static int
537: parsecmd(FILE *difffile, FILE *origfile)
538: {
539: size_t file1start, file1end, file2start, file2end;
540: /* ed command line and pointer to characters in line */
541: const char *line, *p;
542: char cmd;
543:
544: /* Read ed command. */
545: if (!(line = xfgets(difffile)))
546: return (EOF);
547:
548: file1start = xstrtonum(line);
549: p = line;
550: /* Go to character after line number. */
551: while (isdigit(*p))
552: ++p;
553:
554: /* A range is specified for file1. */
555: if (*p == ',') {
556: /* Go to range end. */
557: ++p;
558:
559: file1end = xstrtonum(p);
560: if (file1start > file1end)
561: errx(2, "invalid line range in file1: %s", line);
562:
563: /* Go to character after file2end. */
564: while (isdigit(*p))
565: ++p;
566: } else
567: file1end = file1start;
568:
569: /* This character should be the ed command now. */
570: cmd = *p;
571:
572: /* Check that cmd is valid. */
573: if (!(cmd == 'a' || cmd == 'c' || cmd == 'd'))
574: errx(2, "ed command not recognized: %c: %s", cmd, line);
575:
576: /* Go to file2 line range. */
577: ++p;
578:
579: file2start = xstrtonum(p);
580: /* Go to character after line number. */
581: while (isdigit(*p))
582: ++p;
583:
584: /*
585: * There should either be a comma signifying a second line
586: * number or the line should just end here.
587: */
588: if (!(*p == ',' || *p == '\0'))
589: errx(2, "invalid line range in file2: %c: %s", *p, line);
590:
591: if (*p == ',') {
592: ++p;
593:
594: file2end = xstrtonum(p);
595: if (file2start >= file2end)
596: errx(2, "invalid line range in file2: %s", line);
597: } else
598: file2end = file2start;
599:
600: /* Appends happen _after_ stated line. */
601: if (cmd == 'a') {
602: if (file1start != file1end)
603: errx(2, "append cannot have a file1 range: %s",
604: line);
605: if (file1start == SIZE_T_MAX)
606: errx(2, "file1 line range too high: %s", line);
607: file1start = ++file1end;
608: }
609: /*
610: * I'm not sure what the deal is with the line numbers for
611: * deletes, though.
612: */
613: else if (cmd == 'd') {
614: if (file2start != file2end)
615: errx(2, "delete cannot have a file2 range: %s",
616: line);
617: if (file2start == SIZE_T_MAX)
618: errx(2, "file2 line range too high: %s", line);
619: file2start = ++file2end;
620: }
621:
622: /* Skip unmodified lines. */
623: for (; file1ln < file1start; ++file1ln, ++file2ln) {
624: const char *line;
625:
626: if (!(line = xfgets(origfile)))
627: errx(2, "file1 shorter than expected");
628:
629: /* If the -l flag was specified, print only left column. */
630: enqueue(line, ' ', lflag ? NULL : line);
631: }
632: /* Process unmodified lines. */
633: processq();
634:
635: if (Dflag) {
636: /*
637: * We are now at the line where adds, changes,
638: * or deletions occur.
639: */
640: assert(file1start == file1ln);
641: assert(file2start == file2ln);
642: assert(file1start <= file1end);
643: assert(file2start <= file2end);
644: }
645: switch (cmd) {
646: case 'a':
647: /* A range cannot be specified for file1. */
648: if (Dflag)
649: assert(file1start == file1end);
650:
651: printa(difffile, file2end);
652: break;
653:
654: case 'c':
655: printc(origfile, file1end, difffile, file2end);
656: break;
657:
658: case 'd':
659: /* A range cannot be specified for file2. */
660: if (Dflag)
661: assert(file2start == file2end);
662:
663: printd(origfile, difffile, file1end);
664: break;
665:
666: default:
667: errx(2, "invalid diff command: %c: %s", cmd, line);
668: }
669:
1.3 tedu 670: return (0);
1.1 tedu 671: }
672:
673: /*
674: * Queues up a diff line.
675: */
676: static void
677: enqueue(const char *left, const char div, const char *right)
678: {
679: struct diffline *diffp;
680:
681: if (!(diffp = malloc(sizeof(struct diffline))))
682: err(2, "could not allocate memory");
683: diffp->left = left;
684: diffp->div = div;
685: diffp->right = right;
686: SIMPLEQ_INSERT_TAIL(&diffhead, diffp, diffentries);
687: }
688:
689: /*
690: * Free a diffline structure and its elements.
691: */
692: static void
693: freediff(const struct diffline *diffp)
694: {
695: if (Dflag)
696: assert(diffp);
697:
698: if (diffp->left)
1.3 tedu 699: free((void *)diffp->left);
1.1 tedu 700: /*
701: * Free right string only if it is different than left.
702: * The strings are the same when the lines are identical.
703: */
704: if (diffp->right && diffp->right != diffp->left)
1.3 tedu 705: free((void *)diffp->right);
1.1 tedu 706: }
707:
708: /*
709: * Append second string into first. Repeated appends to the same string
710: * are cached, making this an O(n) function, where n = strlen(append).
711: */
712: static void
713: astrcat(char **s, const char *append)
714: {
715: /* Length of string in previous run. */
716: static size_t offset = 0;
717: size_t copied, newlen;
718: /*
719: * String from previous run. Compared to *s to see if we are
720: * dealing with the same string. If so, we can use offset.
721: */
722: const static char *oldstr = NULL;
723: char *newstr;
724:
725: if (Dflag)
726: assert(append);
727:
728: /*
729: * First string is NULL, so just copy append.
730: */
731: if (!*s) {
732: if (!(*s = strdup(append)))
733: err(2, "could not allocate memory");
734:
735: /* Keep track of string. */
736: offset = strlen(*s);
737: oldstr = *s;
738:
739: return;
740: }
741:
742: /*
743: * *s is a string so concatenate.
744: */
745:
746: /* Did we process the same string in the last run? */
747: /*
748: * If this is a different string from the one we just processed
749: * cache new string.
750: */
751: if (oldstr != *s) {
752: offset = strlen(*s);
753: oldstr = *s;
754: }
755: /* This should always be the end of the string. */
756: if (Dflag) {
757: assert(*(*s + offset) == '\0');
758: assert(strlen(*s) == offset);
759: }
760:
761: /* Length = strlen(*s) + \n + strlen(append) + '\0'. */
762: newlen = offset + 1 + strlen(append) + 1;
763:
764: /* Resize *s to fit new string. */
765: newstr = realloc(*s, newlen);
766: if (newstr == NULL)
767: err(2, "could not allocate memory");
768: *s = newstr;
769:
770: /* Concatenate. */
771: strlcpy(*s + offset, "\n", newlen - offset);
772: copied = strlcat(*s + offset, append, newlen - offset);
773:
774: /*
775: * We should have copied exactly newlen characters, including
776: * the terminating NUL. `copied' includes the \n character.
777: */
778: if (Dflag)
779: assert(offset + copied + sizeof((char)'\0') == newlen);
780:
781: /* Store generated string's values. */
782: offset = newlen - sizeof((char)'\0');
783: oldstr = *s;
784: }
785:
786: /*
787: * Process diff set queue, printing, prompting, and saving each diff
788: * line stored in queue.
789: */
790: static void
791: processq(void)
792: {
793: struct diffline *diffp;
794: char div, *left, *right;
795:
796: /* Don't process empty queue. */
797: if (SIMPLEQ_EMPTY(&diffhead))
798: return;
799:
800: div = '\0';
801: left = NULL;
802: right = NULL;
803: /*
804: * Go through set of diffs, concatenating each line in left or
805: * right column into two long strings, `left' and `right'.
806: */
807: SIMPLEQ_FOREACH(diffp, &diffhead, diffentries) {
808: /*
809: * Make sure that div is consistent throughout set.
810: * If div is set, compare to next entry's div. They
811: * should be the same. If div is not set, then store
812: * this as this set's div.
813: */
814: if (Dflag)
815: assert(div == diffp->div || !div);
816: if (!div)
817: div = diffp->div;
818:
819: /*
820: * If the -s flag was not given or the lines are not
821: * identical then print columns.
822: */
823: if (!sflag || diffp->div != ' ')
824: println(diffp->left, diffp->div, diffp->right);
825:
826: /* Append new lines to diff set. */
827: if (diffp->left)
828: astrcat(&left, diffp->left);
829: if (diffp->right)
830: astrcat(&right, diffp->right);
831: }
832:
833: /* div should no longer be NUL. */
834: if (Dflag)
835: assert(div);
836:
837: /* Empty queue and free each diff line and its elements. */
838: while (!SIMPLEQ_EMPTY(&diffhead)) {
839: diffp = SIMPLEQ_FIRST(&diffhead);
840: freediff(diffp);
841: SIMPLEQ_REMOVE_HEAD(&diffhead, diffentries);
842: free(diffp);
843: }
844:
845: /* Write to outfile, prompting user if lines are different. */
846: if (outfile) {
847: if (div == ' ')
848: fprintf(outfile, "%s\n", left);
849: else
850: prompt(left, right);
851: }
852:
853: /* Free left and right. */
854: if (left)
855: free(left);
856: if (right)
857: free(right);
858: }
859:
860: /*
861: * Remove angle bracket in front of diff line.
862: */
863: static void
864: undiff(char *s)
865: {
866: size_t len;
867:
868: if (Dflag) {
869: assert(s);
870: assert(*s == '<' || *s == '>');
871: assert(*(s + 1) == ' ');
872: }
873:
874: /* Remove angle bracket and space but keep the NUL. */
875: len = strlen(s) - 2 + 1;
876: /* Copy at least the NUL. */
877: if (Dflag)
878: assert(len > 0);
879: /* Move everything two characters over. */
880: memmove(s, s + 2, len);
881: }
882:
883: /*
884: * Print lines following an (a)ppend command.
885: */
886: static void
887: printa(FILE *file, size_t line2)
888: {
889: char *line;
890:
891: if (Dflag) {
892: assert(file);
893: assert(file2ln <= line2);
894: }
895:
896: for (; file2ln <= line2; ++file2ln) {
897: if (!(line = xfgets(file)))
898: errx(2, "append ended early");
899: undiff(line);
900: enqueue(NULL, '>', line);
901: }
902:
903: processq();
904: }
905:
906: /*
907: * Print lines following a (c)hange command, from file1ln to file1end
908: * and from file2ln to file2end.
909: */
910: static void
911: printc(FILE *file1, size_t file1end, FILE *file2, size_t file2end)
912: {
913: struct fileline {
914: SIMPLEQ_ENTRY(fileline) fileentries;
915: const char *line;
916: };
917: SIMPLEQ_HEAD(, fileline) delqhead = SIMPLEQ_HEAD_INITIALIZER(delqhead);
918: char *line;
919:
920: if (Dflag) {
921: assert(file1);
922: assert(file2);
923: assert(file1ln <= file1end);
924: assert(file2ln <= file2end);
925: /* Change diff sets always start out with an empty queue. */
926: assert(SIMPLEQ_EMPTY(&diffhead));
927: }
928:
929: /* Read lines to be deleted. */
930: for (; file1ln <= file1end; ++file1ln) {
931: struct fileline *linep;
932: const char *line1, *line2;
933:
934: /* Read lines from both. */
935: if (!(line1 = xfgets(file1)))
936: errx(2, "error reading file1 in delete in change");
937: if (!(line2 = xfgets(file2)))
938: errx(2, "error reading diff in delete in change");
939:
940: /* Verify lines. */
941: if (Dflag && strncmp("< ", line2, 2) != 0)
942: errx(2, "invalid del/change diff: %s", line2);
943: if (Dflag && strcmp(line1, line2 + 2))
944: warnx("diff differs from file1:\ndiff:\n%s\nfile:\n%s",
945: line2, line1);
946:
947: /* Unused now. */
1.3 tedu 948: free((void *)line2);
1.1 tedu 949:
950: /* Add to delete queue. */
951: if (!(linep = malloc(sizeof(struct fileline))))
952: err(2, "could not allocate memory");
953: linep->line = line1;
954: SIMPLEQ_INSERT_TAIL(&delqhead, linep, fileentries);
955: }
956:
957: /* There should be a divider here. */
958: if (!(line = xfgets(file2)))
959: errx(2, "error reading diff in change: expected divider");
960: if (Dflag && strcmp("---", line))
961: errx(2, "divider expected: %s", line);
962: free(line);
963:
964: #define getaddln(add) do { \
965: /* Read diff for line. */ \
966: if (!((add) = xfgets(file2))) \
967: errx(2, "error reading add in change"); \
968: /* Verify line. */ \
969: if (Dflag && strncmp("> ", (add), 2)) \
970: errx(2, "invalid add/change diff: %s", (add)); \
971: /* Remove ``> ''. */ \
972: undiff(add); \
973: } while (0)
974: /* Process changed lines.. */
975: for (; !SIMPLEQ_EMPTY(&delqhead) && file2ln <= file2end;
976: ++file2ln) {
977: struct fileline *del;
978: char *add;
979:
980: /* Get add line. */
981: getaddln(add);
982:
983: del = SIMPLEQ_FIRST(&delqhead);
984: enqueue(del->line, '|', add);
985: SIMPLEQ_REMOVE_HEAD(&delqhead, fileentries);
986: /*
987: * Free fileline structure but not its elements since
988: * they are queued up.
989: */
990: free(del);
991: }
992: processq();
993:
994: /* Process remaining lines to add. */
995: for (; file2ln <= file2end; ++file2ln) {
996: char *add;
997:
998: /* Get add line. */
999: getaddln(add);
1000:
1001: enqueue(NULL, '>', add);
1002: }
1003: processq();
1004: #undef getaddln
1005:
1006: /* Process remaining lines to delete. */
1007: while (!SIMPLEQ_EMPTY(&delqhead)) {
1008: struct fileline *filep;
1009:
1010: filep = SIMPLEQ_FIRST(&delqhead);
1011: enqueue(filep->line, '<', NULL);
1012: SIMPLEQ_REMOVE_HEAD(&delqhead, fileentries);
1013: free(filep);
1014: }
1015: processq();
1016: }
1017:
1018: /*
1019: * Print deleted lines from file, from file1ln to file1end.
1020: */
1021: static void
1022: printd(FILE *file1, FILE *file2, size_t file1end)
1023: {
1024: const char *line1, *line2;
1025:
1026: if (Dflag) {
1027: assert(file1);
1028: assert(file2);
1029: assert(file1ln <= file1end);
1030: /* Delete diff sets always start with an empty queue. */
1031: assert(SIMPLEQ_EMPTY(&diffhead));
1032: }
1033:
1034: /* Print out lines file1ln to line2. */
1035: for (; file1ln <= file1end; ++file1ln) {
1036: /* XXX - Why can't this handle stdin? */
1037: if (!(line1 = xfgets(file1)))
1038: errx(2, "file1 ended early in delete");
1039: if (!(line2 = xfgets(file2)))
1040: errx(2, "diff ended early in delete");
1041: /* Compare delete line from diff to file1. */
1042: if (Dflag && strcmp(line1, line2 + 2) != 0)
1043: warnx("diff differs from file1:\ndiff:\n%s\nfile:\n%s",
1044: line2, line1);
1.3 tedu 1045: free((void *)line2);
1.1 tedu 1046: enqueue(line1, '<', NULL);
1047: }
1048: processq();
1049: }
1050:
1051: /*
1052: * Interactive mode usage.
1053: */
1054: static void
1055: int_usage(void)
1056: {
1057: puts("e:\tedit blank diff\n"
1058: "eb:\tedit both diffs concatenated\n"
1059: "el:\tedit left diff\n"
1060: "er:\tedit right diff\n"
1061: "l:\tchoose left diff\n"
1062: "r:\tchoose right diff\n"
1063: "s:\tsilent mode--don't print identical lines\n"
1064: "v:\tverbose mode--print identical lines\n"
1065: "q:\tquit");
1066: }
1067:
1068: static void
1069: usage(void)
1070: {
1071: extern char *__progname;
1072:
1073: fprintf(stderr,
1074: "usage: %s [-abDdilstW] [-I regexp] [-o outfile] [-w width] file1 file2\n",
1075: __progname);
1076: exit(2);
1077: }