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