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