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