1/* See LICENSE file for copyright and license details. */
2#include "paths.h"
3#include "util.h"
4#include "wexec.h"
5
6#include <sys/stat.h>
7#include <sys/wait.h>
8
9#include <ctype.h>
10#include <dirent.h>
11#include <errno.h>
12#include <fcntl.h>
13#include <fnmatch.h>
14#include <getopt.h>
15#include <limits.h>
16#include <math.h>
17#include <poll.h>
18#include <regex.h>
19#include <signal.h>
20#include <stdint.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <time.h>
25#include <unistd.h>
26
27#ifndef __dead
28#define __dead __attribute__((__noreturn__))
29#endif
30
31#define d_status d_type
32
33/* output format options */
34#define D_NORMAL 0
35#define D_EDIT -1
36#define D_REVERSE 1
37#define D_CONTEXT 2
38#define D_UNIFIED 3
39#define D_IFDEF 4
40#define D_NREVERSE 5
41#define D_BRIEF 6
42#define D_GFORMAT 7
43#define D_SIDEBYSIDE 8
44
45#define D_UNSET -2
46
47/* algorithms */
48#define D_DIFFNONE 0
49#define D_DIFFSTONE 1
50#define D_DIFFMYERS 2
51#define D_DIFFPATIENCE 3
52
53/* output flags */
54#define D_HEADER 0x001
55#define D_EMPTY1 0x002
56#define D_EMPTY2 0x004
57
58/* command line flags */
59#define D_FORCEASCII 0x008
60#define D_FOLDBLANKS 0x010
61#define D_MINIMAL 0x020
62#define D_IGNORECASE 0x040
63#define D_PROTOTYPE 0x080
64#define D_EXPANDTABS 0x100
65#define D_IGNOREBLANKS 0x200
66#define D_STRIPCR 0x400
67#define D_SKIPBLANKLINES 0x800
68#define D_MATCHLAST 0x1000
69
70/* features supported by new algorithms */
71#define D_NEWALGO_FLAGS (D_FORCEASCII | D_PROTOTYPE | D_IGNOREBLANKS)
72
73/* status values for print_status and diffreg return values */
74#define D_SAME 0
75#define D_DIFFER 1
76#define D_BINARY 2
77#define D_MISMATCH1 3
78#define D_MISMATCH2 4
79#define D_SKIPPED1 5
80#define D_SKIPPED2 6
81#define D_ERROR 7
82
83/* color options */
84#define COLORFLAG_NEVER 0
85#define COLORFLAG_AUTO 1
86#define COLORFLAG_ALWAYS 2
87
88#define FUNCTION_CONTEXT_SIZE 55
89
90#ifndef roundup
91#define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
92#endif
93
94struct Excludes {
95 char *pattern;
96 struct Excludes *next;
97};
98
99struct Cand {
100 int x;
101 int y;
102 int pred;
103};
104
105struct Line {
106 int serial;
107 int value;
108};
109
110struct ContextVec {
111 int a;
112 int b;
113 int c;
114 int d;
115};
116
117struct Pr {
118 int ostdout;
119 pid_t cpid;
120};
121
122struct Algorithm {
123 const char *name;
124 int id;
125};
126
127static struct Algorithm algorithms[] = {
128 {"stone", D_DIFFSTONE}, {"myers", D_DIFFMYERS}, {"patience", D_DIFFPATIENCE}, {NULL, D_DIFFNONE}
129};
130
131/* options */
132#define OPTIONS "0123456789A:aBbC:cdD:efF:HhI:iL:lnNPpqrS:sTtU:uwW:X:x:y"
133enum {
134 OPT_TSIZE = CHAR_MAX + 1,
135 OPT_STRIPCR,
136 OPT_IGN_FN_CASE,
137 OPT_NO_IGN_FN_CASE,
138 OPT_NORMAL,
139 OPT_HELP,
140 OPT_HORIZON_LINES,
141 OPT_CHANGED_GROUP_FORMAT,
142 OPT_SUPPRESS_COMMON,
143 OPT_COLOR,
144 OPT_NO_DEREFERENCE,
145 OPT_VERSION,
146};
147
148static struct option longopts[] = {
149 {"algorithm", required_argument, 0, 'A'},
150 {"text", no_argument, 0, 'a'},
151 {"ignore-space-change", no_argument, 0, 'b'},
152 {"context", optional_argument, 0, 'C'},
153 {"ifdef", required_argument, 0, 'D'},
154 {"minimal", no_argument, 0, 'd'},
155 {"ed", no_argument, 0, 'e'},
156 {"forward-ed", no_argument, 0, 'f'},
157 {"show-function-line", required_argument, 0, 'F'},
158 {"speed-large-files", no_argument, NULL, 'H'},
159 {"ignore-blank-lines", no_argument, 0, 'B'},
160 {"ignore-matching-lines", required_argument, 0, 'I'},
161 {"ignore-case", no_argument, 0, 'i'},
162 {"paginate", no_argument, NULL, 'l'},
163 {"label", required_argument, 0, 'L'},
164 {"new-file", no_argument, 0, 'N'},
165 {"rcs", no_argument, 0, 'n'},
166 {"unidirectional-new-file", no_argument, 0, 'P'},
167 {"show-c-function", no_argument, 0, 'p'},
168 {"brief", no_argument, 0, 'q'},
169 {"recursive", no_argument, 0, 'r'},
170 {"report-identical-files", no_argument, 0, 's'},
171 {"starting-file", required_argument, 0, 'S'},
172 {"expand-tabs", no_argument, 0, 't'},
173 {"initial-tab", no_argument, 0, 'T'},
174 {"unified", optional_argument, 0, 'U'},
175 {"ignore-all-space", no_argument, 0, 'w'},
176 {"width", required_argument, 0, 'W'},
177 {"exclude", required_argument, 0, 'x'},
178 {"exclude-from", required_argument, 0, 'X'},
179 {"side-by-side", no_argument, NULL, 'y'},
180 {"ignore-file-name-case", no_argument, NULL, OPT_IGN_FN_CASE},
181 {"help", no_argument, NULL, OPT_HELP},
182 {"horizon-lines", required_argument, NULL, OPT_HORIZON_LINES},
183 {"no-dereference", no_argument, NULL, OPT_NO_DEREFERENCE},
184 {"no-ignore-file-name-case", no_argument, NULL, OPT_NO_IGN_FN_CASE},
185 {"normal", no_argument, NULL, OPT_NORMAL},
186 {"strip-trailing-cr", no_argument, NULL, OPT_STRIPCR},
187 {"tabsize", required_argument, NULL, OPT_TSIZE},
188 {"changed-group-format", required_argument, NULL, OPT_CHANGED_GROUP_FORMAT},
189 {"suppress-common-lines", no_argument, NULL, OPT_SUPPRESS_COMMON},
190 {"color", optional_argument, NULL, OPT_COLOR},
191 {"version", no_argument, NULL, OPT_VERSION},
192 {NULL, 0, 0, '\0'}
193};
194
195static const char diff_version[] = "FreeBSD diff 20240307";
196int lflag, Nflag, Pflag, rflag, sflag, Tflag, cflag;
197int ignore_file_case, suppress_common, color, noderef;
198static int help = 0;
199int diff_format, diff_context, diff_algorithm, status;
200int diff_algorithm_set;
201int tabsize = 8, width = 130;
202static int colorflag = COLORFLAG_NEVER;
203char *start, *ifdefname, *diffargs, *label[2];
204char *ignore_pats, *most_recent_pat;
205char *group_format = NULL;
206const char *add_code, *del_code;
207struct stat stb1, stb2;
208struct Excludes *excludes_list;
209regex_t ignore_re, most_recent_re;
210
211enum Readhash { RH_BINARY, RH_OK, RH_EOF };
212
213static struct Line *file[2];
214
215static int *J;
216static int *class;
217static int *klist;
218static int *member;
219static int clen;
220static int inifdef;
221static size_t len[2];
222static size_t pref, suff;
223static size_t slen[2];
224static int anychange;
225static int hw, lpad, rpad;
226static int edoffset;
227static long *ixnew;
228static long *ixold;
229static struct Cand *clist;
230static int clistlen;
231static struct Line *sfile[2];
232static int (*chrtran)(int);
233static struct ContextVec *context_vec_start;
234static struct ContextVec *context_vec_end;
235static struct ContextVec *context_vec_ptr;
236static char lastbuf[FUNCTION_CONTEXT_SIZE];
237static int lastline;
238static int lastmatchline;
239
240static int sigpipe[2] = {-1, -1};
241static struct pollfd poll_fd;
242
243static void checked_regcomp(char const *, regex_t *);
244static void usage(void);
245static void conflicting_format(void);
246static void push_excludes(char *);
247static void push_ignore_pats(char *);
248static void read_excludes_file(char *);
249static void set_argstr(char **, char **);
250static char *diff_splice(char *, char *);
251static int do_color(void);
252static int cup2low(int);
253static int clow2low(int);
254static void xasprintf(char **, const char *, ...);
255
256int diffreg(char *, char *, int, int);
257void diffdir(char *, char *, int);
258void print_status(int, char *, char *, const char *);
259
260static int selectfile(const struct dirent *);
261static void diffit(struct dirent *, char *, size_t, struct dirent *, char *, size_t, int);
262static void print_only(const char *, size_t, const char *);
263
264static FILE *opentemp(const char *);
265static void output(char *, FILE *, char *, FILE *, int);
266static void check(FILE *, FILE *, int);
267static void range(int, int, const char *);
268static void uni_range(int, int);
269static void dump_context_vec(FILE *, FILE *, int);
270static void dump_unified_vec(FILE *, FILE *, int);
271static int prepare(int, FILE *, size_t, int);
272static void prune(void);
273static void equiv(struct Line *, int, struct Line *, int, int *);
274static void unravel(int);
275static void unsort(struct Line *, int, int *);
276static void change(char *, FILE *, char *, FILE *, int, int, int, int, int *);
277static void sort(struct Line *, int);
278static void print_header(const char *, const char *);
279static void print_space(int, int, int);
280static int ignoreline_pattern(char *);
281static int ignoreline(char *, int);
282static int asciifile(FILE *);
283static int fetch(long *, int, int, FILE *, int, int, int);
284static int newcand(int, int, int);
285static int search(int *, int, int);
286static int skipline(FILE *);
287static int stone(int *, int, int *, int *, int);
288static enum Readhash readhash(FILE *, int, unsigned *);
289static int files_differ(FILE *, FILE *, int);
290static char *match_function(const long *, int, FILE *);
291static char *preadline(int, size_t, off_t);
292
293static void handle_sig(int);
294struct Pr *start_pr(char *, char *);
295void stop_pr(struct Pr *);
296
297static void
298handle_sig(int signo)
299{
300 write(sigpipe[1], &signo, sizeof(signo));
301}
302
303struct Pr *
304start_pr(char *file1, char *file2)
305{
306 int pfd[2];
307 pid_t pid;
308 char *header;
309 struct Pr *pr;
310
311 pr = ecalloc(1, sizeof(*pr));
312 xasprintf(&header, "%s %s %s", diffargs, file1, file2);
313 signal(SIGPIPE, SIG_IGN);
314 fflush(stdout);
315 if (pipe(pfd) == -1)
316 enprintf(2, "pipe");
317 if (sigpipe[0] < 0) {
318 if (pipe(sigpipe) == -1)
319 enprintf(2, "pipe");
320 if (fcntl(sigpipe[0], F_SETFD, FD_CLOEXEC) == -1)
321 enprintf(2, "fcntl");
322 if (fcntl(sigpipe[1], F_SETFD, FD_CLOEXEC) == -1)
323 enprintf(2, "fcntl");
324 if (signal(SIGCHLD, handle_sig) == SIG_ERR)
325 enprintf(2, "signal");
326 poll_fd.fd = sigpipe[0];
327 poll_fd.events = POLLIN;
328 }
329 poll_fd.revents = 0;
330 switch ((pid = fork())) {
331 case -1:
332 status |= 2;
333 free(header);
334 enprintf(2, "no more processes");
335 /* fallthrough */
336 case 0:
337 if (pfd[0] != STDIN_FILENO) {
338 dup2(pfd[0], STDIN_FILENO);
339 close(pfd[0]);
340 }
341 close(pfd[1]);
342 {
343 char *pr_argv[4];
344 pr_argv[0] = "pr";
345 pr_argv[1] = "-h";
346 pr_argv[2] = header;
347 pr_argv[3] = NULL;
348 wexecvp_self("pr", pr_argv);
349 }
350 _exit(127);
351 default:
352 if (pfd[1] != STDOUT_FILENO) {
353 pr->ostdout = dup(STDOUT_FILENO);
354 dup2(pfd[1], STDOUT_FILENO);
355 close(pfd[1]);
356 }
357 close(pfd[0]);
358 free(header);
359 pr->cpid = pid;
360 }
361 return pr;
362}
363
364void
365stop_pr(struct Pr *pr)
366{
367 int wstatus;
368 int done = 0;
369
370 if (!pr)
371 return;
372
373 fflush(stdout);
374 if (pr->ostdout != STDOUT_FILENO) {
375 close(STDOUT_FILENO);
376 dup2(pr->ostdout, STDOUT_FILENO);
377 close(pr->ostdout);
378 }
379 while (!done) {
380 pid_t wpid;
381 int npe = poll(&poll_fd, 1, -1);
382 if (npe == -1) {
383 if (errno == EINTR)
384 continue;
385 enprintf(2, "poll");
386 }
387 if (poll_fd.revents != POLLIN)
388 continue;
389 if (read(poll_fd.fd, &npe, sizeof(npe)) < 0)
390 enprintf(2, "read");
391 while ((wpid = waitpid(-1, &wstatus, WNOHANG)) > 0) {
392 if (wpid != pr->cpid)
393 continue;
394 if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != 0)
395 enprintf(2, "pr exited abnormally");
396 else if (WIFSIGNALED(wstatus))
397 enprintf(2, "pr killed by signal %d", WTERMSIG(wstatus));
398 done = 1;
399 break;
400 }
401 }
402 free(pr);
403}
404
405// ?man diff: differential file and directory comparator
406// ?man arguments: file1 file2
407// ?man synopsis: [-aBbdipTtw] [-c|-e|-f|-n|-q|-u|-y] [-A algo] [--brief]
408// [--color=when] [--changed-group-format GFMT] [--ed] [--expand-tabs]
409// [--forward-ed] [--ignore-all-space] [--ignore-case] [--ignore-space-change]
410// [--initial-tab] [--minimal] [--no-dereference] [--no-ignore-file-name-case]
411// [--normal] [--rcs] [--show-c-function] [--starting-file]
412// [--speed-large-files] [--strip-trailing-cr] [--tabsize number] [--text] [-I
413// pattern] [-F pattern] [-L label] file1 file2
414int
415main(int argc, char **argv)
416{
417 const char *errstr = NULL;
418 char *ep, **oargv;
419 long l;
420 int ch, dflags, lastch, gotstdin, prevoptind, newarg;
421
422 oargv = argv;
423 gotstdin = 0;
424 dflags = 0;
425 lastch = '\0';
426 prevoptind = 1;
427 newarg = 1;
428 diff_context = 3;
429 diff_format = D_UNSET;
430 diff_algorithm = D_DIFFMYERS;
431 diff_algorithm_set = 0;
432#define FORMAT_MISMATCHED(type) (diff_format != D_UNSET && diff_format != (type))
433 while ((ch = getopt_long(argc, argv, OPTIONS, longopts, NULL)) != -1) {
434 // ?man -0: context length
435 // ?man -1: context length
436 // ?man -2: context length
437 // ?man -3: context length
438 // ?man -4: context length
439 // ?man -5: context length
440 // ?man -6: context length
441 // ?man -7: context length
442 // ?man -8: context length
443 // ?man -9: context length
444 switch (ch) {
445 case '0':
446 case '1':
447 case '2':
448 case '3':
449 case '4':
450 case '5':
451 case '6':
452 case '7':
453 case '8':
454 case '9':
455 if (newarg)
456 usage();
457 else if (lastch == 'c' || lastch == 'u')
458 diff_context = 0;
459 else if (!isdigit(lastch) || diff_context > INT_MAX / 10)
460 usage();
461 diff_context = (diff_context * 10) + (ch - '0');
462 break;
463 // ?man -A:algo: algorithm (stone, myers, patience)
464 case 'A':
465 diff_algorithm = D_DIFFNONE;
466 for (struct Algorithm *a = algorithms; a->name; a++) {
467 if (strcasecmp(optarg, a->name) == 0) {
468 diff_algorithm = a->id;
469 diff_algorithm_set = 1;
470 break;
471 }
472 }
473 if (diff_algorithm == D_DIFFNONE) {
474 printf("unknown algorithm: %s\n", optarg);
475 usage();
476 }
477 break;
478 // ?man -a: force text mode
479 case 'a':
480 dflags |= D_FORCEASCII;
481 break;
482 // ?man -b: ignore space changes
483 case 'b':
484 dflags |= D_FOLDBLANKS;
485 break;
486 // ?man -C:lines: context format
487 case 'C':
488 // ?man -c: context format
489 case 'c':
490 if (FORMAT_MISMATCHED(D_CONTEXT))
491 conflicting_format();
492 cflag = 1;
493 diff_format = D_CONTEXT;
494 if (optarg != NULL) {
495 l = strtol(optarg, &ep, 10);
496 if (*ep != '\0' || l < 0 || l >= INT_MAX)
497 usage();
498 diff_context = (int)l;
499 }
500 break;
501 // ?man -d: minimal diff search
502 case 'd':
503 dflags |= D_MINIMAL;
504 break;
505 // ?man -D:name: ifdef format
506 case 'D':
507 if (FORMAT_MISMATCHED(D_IFDEF))
508 conflicting_format();
509 diff_format = D_IFDEF;
510 ifdefname = optarg;
511 break;
512 // ?man -e: ed script format
513 case 'e':
514 if (FORMAT_MISMATCHED(D_EDIT))
515 conflicting_format();
516 diff_format = D_EDIT;
517 break;
518 // ?man -f: forward ed script format
519 case 'f':
520 if (FORMAT_MISMATCHED(D_REVERSE))
521 conflicting_format();
522 diff_format = D_REVERSE;
523 break;
524 // ?man -H: speed up large files (stub)
525 case 'H':
526 break;
527 // ?man -h: backward compatibility (stub)
528 case 'h':
529 break;
530 // ?man -B: ignore blank lines
531 case 'B':
532 dflags |= D_SKIPBLANKLINES;
533 break;
534 // ?man -F:pat: show function matching pattern
535 case 'F':
536 if (dflags & D_PROTOTYPE)
537 conflicting_format();
538 dflags |= D_MATCHLAST;
539 most_recent_pat = estrdup(optarg);
540 break;
541 // ?man -I:pat: ignore pattern matching lines
542 case 'I':
543 push_ignore_pats(optarg);
544 break;
545 // ?man -i: ignore case
546 case 'i':
547 dflags |= D_IGNORECASE;
548 break;
549 // ?man -L:label: custom label
550 case 'L':
551 if (label[0] == NULL)
552 label[0] = optarg;
553 else if (label[1] == NULL)
554 label[1] = optarg;
555 else
556 usage();
557 break;
558 // ?man -l: paginate output using pr
559 case 'l':
560 lflag = 1;
561 break;
562 // ?man -N: treat missing files as empty
563 case 'N':
564 Nflag = 1;
565 break;
566 // ?man -n: rcs format
567 case 'n':
568 if (FORMAT_MISMATCHED(D_NREVERSE))
569 conflicting_format();
570 diff_format = D_NREVERSE;
571 break;
572 // ?man -p: show C prototype context
573 case 'p':
574 if (dflags & D_MATCHLAST)
575 conflicting_format();
576 dflags |= D_PROTOTYPE;
577 break;
578 // ?man -P: treat missing destination files as empty
579 case 'P':
580 Pflag = 1;
581 break;
582 // ?man -r: recursive directory comparison
583 case 'r':
584 rflag = 1;
585 break;
586 // ?man -q: brief output (differ / same only)
587 case 'q':
588 if (FORMAT_MISMATCHED(D_BRIEF))
589 conflicting_format();
590 diff_format = D_BRIEF;
591 break;
592 // ?man -S:name: starting file in directory comparison
593 case 'S':
594 start = optarg;
595 break;
596 // ?man -s: report identical files
597 case 's':
598 sflag = 1;
599 break;
600 // ?man -T: initial tab alignment
601 case 'T':
602 Tflag = 1;
603 break;
604 // ?man -t: expand tabs in output
605 case 't':
606 dflags |= D_EXPANDTABS;
607 break;
608 // ?man -U:lines: unified context format
609 case 'U':
610 // ?man -u: unified context format
611 case 'u':
612 if (FORMAT_MISMATCHED(D_UNIFIED))
613 conflicting_format();
614 diff_format = D_UNIFIED;
615 if (optarg != NULL) {
616 l = strtol(optarg, &ep, 10);
617 if (*ep != '\0' || l < 0 || l >= INT_MAX)
618 usage();
619 diff_context = (int)l;
620 }
621 break;
622 // ?man -w: ignore all whitespace
623 case 'w':
624 dflags |= D_IGNOREBLANKS;
625 break;
626 // ?man -W:cols: column width for side-by-side
627 case 'W':
628 width = (int)strtonum(optarg, 1, INT_MAX, &errstr);
629 if (errstr) {
630 weprintf("invalid width argument");
631 usage();
632 }
633 break;
634 // ?man -X:file: exclude patterns file
635 case 'X':
636 read_excludes_file(optarg);
637 break;
638 // ?man -x:pat: exclude pattern
639 case 'x':
640 push_excludes(optarg);
641 break;
642 // ?man -y: side by side format
643 case 'y':
644 if (FORMAT_MISMATCHED(D_SIDEBYSIDE))
645 conflicting_format();
646 diff_format = D_SIDEBYSIDE;
647 break;
648 case OPT_CHANGED_GROUP_FORMAT:
649 if (FORMAT_MISMATCHED(D_GFORMAT))
650 conflicting_format();
651 diff_format = D_GFORMAT;
652 group_format = optarg;
653 break;
654 case OPT_HELP:
655 help = 1;
656 usage();
657 break;
658 case OPT_HORIZON_LINES:
659 break;
660 case OPT_IGN_FN_CASE:
661 ignore_file_case = 1;
662 break;
663 case OPT_NO_IGN_FN_CASE:
664 ignore_file_case = 0;
665 break;
666 case OPT_NORMAL:
667 if (FORMAT_MISMATCHED(D_NORMAL))
668 conflicting_format();
669 diff_format = D_NORMAL;
670 break;
671 case OPT_TSIZE:
672 tabsize = (int)strtonum(optarg, 1, INT_MAX, &errstr);
673 if (errstr) {
674 weprintf("invalid tabsize argument");
675 usage();
676 }
677 break;
678 case OPT_STRIPCR:
679 dflags |= D_STRIPCR;
680 break;
681 case OPT_SUPPRESS_COMMON:
682 suppress_common = 1;
683 break;
684 case OPT_COLOR:
685 if (optarg == NULL || strncmp(optarg, "auto", 4) == 0)
686 colorflag = COLORFLAG_AUTO;
687 else if (strncmp(optarg, "always", 6) == 0)
688 colorflag = COLORFLAG_ALWAYS;
689 else if (strncmp(optarg, "never", 5) == 0)
690 colorflag = COLORFLAG_NEVER;
691 else
692 enprintf(2, "unsupported color option %s", optarg);
693 break;
694 case OPT_NO_DEREFERENCE:
695 noderef = 1;
696 break;
697 case OPT_VERSION:
698 printf("%s\n", diff_version);
699 exit(0);
700 default:
701 usage();
702 break;
703 }
704 lastch = ch;
705 newarg = optind != prevoptind;
706 prevoptind = optind;
707 }
708 if (diff_format == D_UNSET && (dflags & D_PROTOTYPE) != 0)
709 diff_format = D_CONTEXT;
710 if (diff_format == D_UNSET)
711 diff_format = D_NORMAL;
712 argc -= optind;
713 argv += optind;
714
715 if (do_color()) {
716 char *p;
717 const char *env;
718
719 color = 1;
720 add_code = "32";
721 del_code = "31";
722 env = getenv("DIFFCOLORS");
723 if (env != NULL && *env != '\0' && (p = estrdup(env))) {
724 add_code = p;
725 strsep(&p, ":");
726 if (p != NULL)
727 del_code = p;
728 }
729 }
730
731 if (argc != 2)
732 usage();
733 checked_regcomp(ignore_pats, &ignore_re);
734 checked_regcomp(most_recent_pat, &most_recent_re);
735 if (strcmp(argv[0], "-") == 0) {
736 fstat(STDIN_FILENO, &stb1);
737 gotstdin = 1;
738 } else if (stat(argv[0], &stb1) != 0) {
739 if (!Nflag || errno != ENOENT)
740 enprintf(2, "%s", argv[0]);
741 dflags |= D_EMPTY1;
742 memset(&stb1, 0, sizeof(struct stat));
743 }
744
745 if (strcmp(argv[1], "-") == 0) {
746 fstat(STDIN_FILENO, &stb2);
747 gotstdin = 1;
748 } else if (stat(argv[1], &stb2) != 0) {
749 if (!Nflag || errno != ENOENT)
750 enprintf(2, "%s", argv[1]);
751 dflags |= D_EMPTY2;
752 memset(&stb2, 0, sizeof(stb2));
753 stb2.st_mode = stb1.st_mode;
754 }
755
756 if (dflags & D_EMPTY1 && dflags & D_EMPTY2) {
757 weprintf("%s", argv[0]);
758 weprintf("%s", argv[1]);
759 exit(2);
760 }
761
762 if (stb1.st_mode == 0)
763 stb1.st_mode = stb2.st_mode;
764
765 if (gotstdin && (S_ISDIR(stb1.st_mode) || S_ISDIR(stb2.st_mode)))
766 enprintf(2, "cant compare - to directory");
767 set_argstr(oargv, argv);
768 if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
769 if (diff_format == D_IFDEF)
770 enprintf(2, "-D option not supported with directories");
771 diffdir(argv[0], argv[1], dflags);
772 } else {
773 if (S_ISDIR(stb1.st_mode)) {
774 argv[0] = diff_splice(argv[0], argv[1]);
775 if (stat(argv[0], &stb1) == -1)
776 enprintf(2, "%s", argv[0]);
777 }
778 if (S_ISDIR(stb2.st_mode)) {
779 argv[1] = diff_splice(argv[1], argv[0]);
780 if (stat(argv[1], &stb2) == -1)
781 enprintf(2, "%s", argv[1]);
782 }
783 print_status(diffreg(argv[0], argv[1], dflags, 1), argv[0], argv[1], "");
784 }
785 if (fflush(stdout) != 0)
786 enprintf(2, "stdout");
787 exit(status);
788}
789
790static void
791checked_regcomp(char const *pattern, regex_t *comp)
792{
793 char buf[BUFSIZ];
794 int error;
795
796 if (pattern == NULL)
797 return;
798
799 error = regcomp(comp, pattern, REG_NEWLINE | REG_EXTENDED);
800 if (error != 0) {
801 regerror(error, comp, buf, sizeof(buf));
802 if (*pattern != '\0')
803 enprintf(2, "%s: %s", pattern, buf);
804 else
805 enprintf(2, "%s", buf);
806 }
807}
808
809static void
810set_argstr(char **av, char **ave)
811{
812 size_t argsize;
813 char **ap;
814
815 argsize = strlen("diff") + 1;
816 for (ap = av + 1; ap < ave; ap++) {
817 if (strcmp(*ap, "--") != 0)
818 argsize += 1 + strlen(*ap);
819 }
820 diffargs = emalloc(argsize);
821 strlcpy(diffargs, "diff", argsize);
822 for (ap = av + 1; ap < ave; ap++) {
823 if (strcmp(*ap, "--") != 0) {
824 strlcat(diffargs, " ", argsize);
825 strlcat(diffargs, *ap, argsize);
826 }
827 }
828}
829
830static void
831read_excludes_file(char *file)
832{
833 FILE *fp;
834 char *pattern = NULL;
835 size_t blen = 0;
836 ssize_t len;
837
838 if (strcmp(file, "-") == 0)
839 fp = stdin;
840 else if ((fp = fopen(file, "r")) == NULL)
841 enprintf(2, "%s", file);
842 while ((len = getline(&pattern, &blen, fp)) >= 0) {
843 if ((len > 0) && (pattern[len - 1] == '\n'))
844 pattern[len - 1] = '\0';
845 push_excludes(pattern);
846 pattern = NULL;
847 blen = 0;
848 }
849 free(pattern);
850 if (strcmp(file, "-") != 0)
851 fclose(fp);
852}
853
854static void
855push_excludes(char *pattern)
856{
857 struct Excludes *entry;
858
859 entry = emalloc(sizeof(*entry));
860 entry->pattern = pattern;
861 entry->next = excludes_list;
862 excludes_list = entry;
863}
864
865static void
866push_ignore_pats(char *pattern)
867{
868 size_t len;
869
870 if (ignore_pats == NULL)
871 ignore_pats = estrdup(pattern);
872 else {
873 len = strlen(ignore_pats) + strlen(pattern) + 2;
874 ignore_pats = ereallocarray(ignore_pats, 1, len);
875 strlcat(ignore_pats, "|", len);
876 strlcat(ignore_pats, pattern, len);
877 }
878}
879
880void
881print_status(int val, char *path1, char *path2, const char *entry)
882{
883 if (label[0] != NULL)
884 path1 = label[0];
885 if (label[1] != NULL)
886 path2 = label[1];
887
888 switch (val) {
889 case D_BINARY:
890 printf("Binary files %s%s and %s%s differ\n", path1, entry, path2, entry);
891 break;
892 case D_DIFFER:
893 if (diff_format == D_BRIEF)
894 printf("Files %s%s and %s%s differ\n", path1, entry, path2, entry);
895 break;
896 case D_SAME:
897 if (sflag)
898 printf("Files %s%s and %s%s are identical\n", path1, entry, path2, entry);
899 break;
900 case D_MISMATCH1:
901 printf("File %s%s is directory, %s%s is file\n", path1, entry, path2, entry);
902 break;
903 case D_MISMATCH2:
904 printf("File %s%s is file, %s%s is directory\n", path1, entry, path2, entry);
905 break;
906 case D_SKIPPED1:
907 printf("File %s%s is not regular file or directory\n", path1, entry);
908 break;
909 case D_SKIPPED2:
910 printf("File %s%s is not regular file or directory\n", path2, entry);
911 break;
912 }
913}
914
915static void
916usage(void)
917{
918 fprintf(
919 stderr,
920 "usage: diff [-aBbdipTtw] [-c|-e|-f|-n|-q|-u|-y] [-A algo] "
921 "[-I pattern] [-F pattern] [-L label] file1 file2\n"
922 );
923 exit(2);
924}
925
926static void
927conflicting_format(void)
928{
929 enprintf(2, "conflicting output format options");
930}
931
932static char *
933diff_splice(char *dir, char *file)
934{
935 char *p, *res;
936
937 p = strrchr(file, '/');
938 p = p ? p + 1 : file;
939 xasprintf(&res, "%s/%s", dir, p);
940 return res;
941}
942
943static void
944xasprintf(char **strp, const char *fmt, ...)
945{
946 va_list ap;
947 int r;
948
949 va_start(ap, fmt);
950 r = vasprintf(strp, fmt, ap);
951 va_end(ap);
952 if (r == -1)
953 enprintf(2, "asprintf");
954}
955
956static int
957cup2low(int c)
958{
959 return tolower(c);
960}
961
962static int
963clow2low(int c)
964{
965 return c;
966}
967
968static int
969do_color(void)
970{
971 if (colorflag == COLORFLAG_ALWAYS)
972 return 1;
973 if (colorflag == COLORFLAG_NEVER)
974 return 0;
975 if (isatty(STDOUT_FILENO)) {
976 char *term = getenv("COLORTERM");
977 if (term && *term != '\0')
978 return 1;
979 }
980 return 0;
981}
982
983void
984diffdir(char *p1, char *p2, int flags)
985{
986 struct dirent *dent1, **dp1, **edp1, **dirp1 = NULL;
987 struct dirent *dent2, **dp2, **edp2, **dirp2 = NULL;
988 size_t dirlen1, dirlen2;
989 char path1[PATH_MAX], path2[PATH_MAX];
990 int pos;
991
992 edp1 = edp2 = NULL;
993 dirlen1 = strlcpy(path1, *p1 ? p1 : ".", sizeof(path1));
994 if (dirlen1 >= sizeof(path1) - 1) {
995 errno = ENAMETOOLONG;
996 weprintf("%s", p1);
997 status |= 2;
998 return;
999 }
1000 if (path1[dirlen1 - 1] != '/') {
1001 path1[dirlen1++] = '/';
1002 path1[dirlen1] = '\0';
1003 }
1004 dirlen2 = strlcpy(path2, *p2 ? p2 : ".", sizeof(path2));
1005 if (dirlen2 >= sizeof(path2) - 1) {
1006 errno = ENAMETOOLONG;
1007 weprintf("%s", p2);
1008 status |= 2;
1009 return;
1010 }
1011 if (path2[dirlen2 - 1] != '/') {
1012 path2[dirlen2++] = '/';
1013 path2[dirlen2] = '\0';
1014 }
1015
1016 pos = scandir(path1, &dirp1, selectfile, alphasort);
1017 if (pos == -1) {
1018 if (errno == ENOENT && (Nflag || Pflag))
1019 pos = 0;
1020 else {
1021 weprintf("%s", path1);
1022 goto closem;
1023 }
1024 }
1025 dp1 = dirp1;
1026 edp1 = dirp1 + pos;
1027
1028 pos = scandir(path2, &dirp2, selectfile, alphasort);
1029 if (pos == -1) {
1030 if (errno == ENOENT && Nflag)
1031 pos = 0;
1032 else {
1033 weprintf("%s", path2);
1034 goto closem;
1035 }
1036 }
1037 dp2 = dirp2;
1038 edp2 = dirp2 + pos;
1039
1040 if (start != NULL) {
1041 while (dp1 != edp1 && strcmp((*dp1)->d_name, start) < 0)
1042 dp1++;
1043 while (dp2 != edp2 && strcmp((*dp2)->d_name, start) < 0)
1044 dp2++;
1045 }
1046
1047 while (dp1 != edp1 || dp2 != edp2) {
1048 dent1 = dp1 != edp1 ? *dp1 : NULL;
1049 dent2 = dp2 != edp2 ? *dp2 : NULL;
1050
1051 pos = dent1 == NULL ? 1
1052 : dent2 == NULL ? -1
1053 : ignore_file_case ? strcasecmp(dent1->d_name, dent2->d_name)
1054 : strcmp(dent1->d_name, dent2->d_name);
1055 if (pos == 0) {
1056 diffit(dent1, path1, dirlen1, dent2, path2, dirlen2, flags);
1057 dp1++;
1058 dp2++;
1059 } else if (pos < 0) {
1060 if (Nflag)
1061 diffit(dent1, path1, dirlen1, dent2, path2, dirlen2, flags);
1062 else {
1063 print_only(path1, dirlen1, dent1->d_name);
1064 status |= 1;
1065 }
1066 dp1++;
1067 } else {
1068 if (Nflag || Pflag)
1069 diffit(dent2, path1, dirlen1, dent1, path2, dirlen2, flags);
1070 else {
1071 print_only(path2, dirlen2, dent2->d_name);
1072 status |= 1;
1073 }
1074 dp2++;
1075 }
1076 }
1077
1078closem:
1079 if (dirp1 != NULL) {
1080 for (dp1 = dirp1; dp1 < edp1; dp1++)
1081 free(*dp1);
1082 free(dirp1);
1083 }
1084 if (dirp2 != NULL) {
1085 for (dp2 = dirp2; dp2 < edp2; dp2++)
1086 free(*dp2);
1087 free(dirp2);
1088 }
1089}
1090
1091static void
1092diffit(
1093 struct dirent *dp,
1094 char *path1,
1095 size_t plen1,
1096 struct dirent *dp2,
1097 char *path2,
1098 size_t plen2,
1099 int flags
1100)
1101{
1102 flags |= D_HEADER;
1103 strlcpy(path1 + plen1, dp->d_name, PATH_MAX - plen1);
1104
1105 if (ignore_file_case && strcasecmp(dp->d_name, dp2->d_name) == 0)
1106 strlcpy(path2 + plen2, dp2->d_name, PATH_MAX - plen2);
1107 else
1108 strlcpy(path2 + plen2, dp->d_name, PATH_MAX - plen2);
1109
1110 if (noderef) {
1111 if (lstat(path1, &stb1) != 0) {
1112 if (!(Nflag || Pflag) || errno != ENOENT) {
1113 weprintf("%s", path1);
1114 return;
1115 }
1116 flags |= D_EMPTY1;
1117 memset(&stb1, 0, sizeof(stb1));
1118 }
1119
1120 if (lstat(path2, &stb2) != 0) {
1121 if (!Nflag || errno != ENOENT) {
1122 weprintf("%s", path2);
1123 return;
1124 }
1125 flags |= D_EMPTY2;
1126 memset(&stb2, 0, sizeof(stb2));
1127 stb2.st_mode = stb1.st_mode;
1128 }
1129 if (stb1.st_mode == 0)
1130 stb1.st_mode = stb2.st_mode;
1131 if (S_ISLNK(stb1.st_mode) || S_ISLNK(stb2.st_mode)) {
1132 if (S_ISLNK(stb1.st_mode) && S_ISLNK(stb2.st_mode)) {
1133 char buf1[PATH_MAX];
1134 char buf2[PATH_MAX];
1135 ssize_t len1;
1136 ssize_t len2;
1137
1138 len1 = readlink(path1, buf1, sizeof(buf1));
1139 len2 = readlink(path2, buf2, sizeof(buf2));
1140 if (len1 < 0 || len2 < 0) {
1141 perror("reading links");
1142 return;
1143 }
1144 buf1[len1] = '\0';
1145 buf2[len2] = '\0';
1146 if (len1 != len2 || strncmp(buf1, buf2, len1) != 0) {
1147 printf(
1148 "Symbolic links %s and %s "
1149 "differ\n",
1150 path1,
1151 path2
1152 );
1153 status |= 1;
1154 }
1155 return;
1156 }
1157 printf(
1158 "File %s is a %s while file %s is a %s\n",
1159 path1,
1160 S_ISLNK(stb1.st_mode) ? "symbolic link"
1161 : S_ISDIR(stb1.st_mode) ? "directory"
1162 : "file",
1163 path2,
1164 S_ISLNK(stb2.st_mode) ? "symbolic link"
1165 : S_ISDIR(stb2.st_mode) ? "directory"
1166 : "file"
1167 );
1168 status |= 1;
1169 return;
1170 }
1171 } else {
1172 if (stat(path1, &stb1) != 0) {
1173 if (!(Nflag || Pflag) || errno != ENOENT) {
1174 weprintf("%s", path1);
1175 return;
1176 }
1177 flags |= D_EMPTY1;
1178 memset(&stb1, 0, sizeof(stb1));
1179 }
1180
1181 if (stat(path2, &stb2) != 0) {
1182 if (!Nflag || errno != ENOENT) {
1183 weprintf("%s", path2);
1184 return;
1185 }
1186 flags |= D_EMPTY2;
1187 memset(&stb2, 0, sizeof(stb2));
1188 stb2.st_mode = stb1.st_mode;
1189 }
1190 if (stb1.st_mode == 0)
1191 stb1.st_mode = stb2.st_mode;
1192 }
1193 if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
1194 if (rflag)
1195 diffdir(path1, path2, flags);
1196 else
1197 printf("Common subdirectories: %s and %s\n", path1, path2);
1198 return;
1199 }
1200 if (!S_ISREG(stb1.st_mode) && !S_ISDIR(stb1.st_mode))
1201 dp->d_status = D_SKIPPED1;
1202 else if (!S_ISREG(stb2.st_mode) && !S_ISDIR(stb2.st_mode))
1203 dp->d_status = D_SKIPPED2;
1204 else
1205 dp->d_status = diffreg(path1, path2, flags, 0);
1206 print_status(dp->d_status, path1, path2, "");
1207}
1208
1209static int
1210selectfile(const struct dirent *dp)
1211{
1212 struct Excludes *excl;
1213
1214 if (dp->d_fileno == 0)
1215 return 0;
1216
1217 if (dp->d_name[0] == '.'
1218 && (dp->d_name[1] == '\0' || (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
1219 return 0;
1220
1221 for (excl = excludes_list; excl != NULL; excl = excl->next)
1222 if (fnmatch(excl->pattern, dp->d_name, FNM_PATHNAME) == 0)
1223 return 0;
1224
1225 return 1;
1226}
1227
1228void
1229print_only(const char *path, size_t dirlen, const char *entry)
1230{
1231 if (dirlen > 1)
1232 dirlen--;
1233 printf("Only in %.*s: %s\n", (int)dirlen, path, entry);
1234}
1235
1236int
1237diffreg(char *file1, char *file2, int flags, int capsicum)
1238{
1239 FILE *f1, *f2;
1240 int i, rval;
1241 struct Pr *pr = NULL;
1242
1243 f1 = f2 = NULL;
1244 rval = D_SAME;
1245 anychange = 0;
1246 lastline = 0;
1247 lastmatchline = 0;
1248
1249 if (diff_format == D_SIDEBYSIDE) {
1250 if (flags & D_EXPANDTABS) {
1251 if (width > 3)
1252 hw = (width - 3) / 2;
1253 else
1254 hw = 0;
1255 } else if (width <= 3 || width <= tabsize) {
1256 hw = 0;
1257 } else {
1258 hw = (width - 3) / 2;
1259 while (hw > 0 && roundup(hw + 3, tabsize) + hw > width)
1260 hw--;
1261 if (width - (roundup(hw + 3, tabsize) + hw) < tabsize)
1262 width = roundup(hw + 3, tabsize) + hw;
1263 }
1264 lpad = (width - hw * 2 - 1) / 2;
1265 rpad = (width - hw * 2 - 1) - lpad;
1266 }
1267
1268 if (flags & D_IGNORECASE)
1269 chrtran = cup2low;
1270 else
1271 chrtran = clow2low;
1272 if (S_ISDIR(stb1.st_mode) != S_ISDIR(stb2.st_mode))
1273 return S_ISDIR(stb1.st_mode) ? D_MISMATCH1 : D_MISMATCH2;
1274 if (strcmp(file1, "-") == 0 && strcmp(file2, "-") == 0)
1275 goto closem;
1276
1277 if (flags & D_EMPTY1)
1278 f1 = fopen(ARUU_PATH_DEVNULL, "r");
1279 else {
1280 if (!S_ISREG(stb1.st_mode)) {
1281 if ((f1 = opentemp(file1)) == NULL || fstat(fileno(f1), &stb1) == -1) {
1282 weprintf("%s", file1);
1283 rval = D_ERROR;
1284 status |= 2;
1285 goto closem;
1286 }
1287 } else if (strcmp(file1, "-") == 0)
1288 f1 = stdin;
1289 else
1290 f1 = fopen(file1, "r");
1291 }
1292 if (f1 == NULL) {
1293 weprintf("%s", file1);
1294 rval = D_ERROR;
1295 status |= 2;
1296 goto closem;
1297 }
1298
1299 if (flags & D_EMPTY2)
1300 f2 = fopen(ARUU_PATH_DEVNULL, "r");
1301 else {
1302 if (!S_ISREG(stb2.st_mode)) {
1303 if ((f2 = opentemp(file2)) == NULL || fstat(fileno(f2), &stb2) == -1) {
1304 weprintf("%s", file2);
1305 rval = D_ERROR;
1306 status |= 2;
1307 goto closem;
1308 }
1309 } else if (strcmp(file2, "-") == 0)
1310 f2 = stdin;
1311 else
1312 f2 = fopen(file2, "r");
1313 }
1314 if (f2 == NULL) {
1315 weprintf("%s", file2);
1316 rval = D_ERROR;
1317 status |= 2;
1318 goto closem;
1319 }
1320
1321 if (lflag)
1322 pr = start_pr(file1, file2);
1323
1324 (void)capsicum;
1325
1326 switch (files_differ(f1, f2, flags)) {
1327 case 0:
1328 goto closem;
1329 case 1:
1330 break;
1331 default:
1332 rval = D_ERROR;
1333 status |= 2;
1334 goto closem;
1335 }
1336
1337 if (diff_format == D_BRIEF && ignore_pats == NULL
1338 && (flags & (D_FOLDBLANKS | D_IGNOREBLANKS | D_IGNORECASE | D_SKIPBLANKLINES | D_STRIPCR))
1339 == 0) {
1340 rval = D_DIFFER;
1341 status |= 1;
1342 goto closem;
1343 }
1344 if ((flags & D_FORCEASCII) != 0) {
1345 prepare(0, f1, stb1.st_size, flags);
1346 prepare(1, f2, stb2.st_size, flags);
1347 } else if (
1348 !asciifile(f1) || !asciifile(f2) || !prepare(0, f1, stb1.st_size, flags)
1349 || !prepare(1, f2, stb2.st_size, flags)
1350 ) {
1351 rval = D_BINARY;
1352 status |= 1;
1353 goto closem;
1354 }
1355 if (len[0] > INT_MAX - 2)
1356 errno = EFBIG, enprintf(1, "%s", file1);
1357 if (len[1] > INT_MAX - 2)
1358 errno = EFBIG, enprintf(1, "%s", file2);
1359
1360 prune();
1361 sort(sfile[0], slen[0]);
1362 sort(sfile[1], slen[1]);
1363
1364 member = (int *)file[1];
1365 equiv(sfile[0], slen[0], sfile[1], slen[1], member);
1366 member = ereallocarray(member, slen[1] + 2, sizeof(*member));
1367
1368 class = (int *)file[0];
1369 unsort(sfile[0], slen[0], class);
1370 class = ereallocarray(class, slen[0] + 2, sizeof(*class));
1371
1372 klist = ecalloc(slen[0] + 2, sizeof(*klist));
1373 clen = 0;
1374 clistlen = 100;
1375 clist = ecalloc(clistlen, sizeof(*clist));
1376 i = stone(class, slen[0], member, klist, flags);
1377 free(member);
1378 free(class);
1379
1380 J = ereallocarray(J, len[0] + 2, sizeof(*J));
1381 unravel(klist[i]);
1382 free(clist);
1383 free(klist);
1384
1385 ixold = ereallocarray(ixold, len[0] + 2, sizeof(*ixold));
1386 ixnew = ereallocarray(ixnew, len[1] + 2, sizeof(*ixnew));
1387 check(f1, f2, flags);
1388 output(file1, f1, file2, f2, flags);
1389
1390closem:
1391 if (pr != NULL)
1392 stop_pr(pr);
1393 if (anychange) {
1394 status |= 1;
1395 if (rval == D_SAME)
1396 rval = D_DIFFER;
1397 }
1398 if (f1 != NULL && f1 != stdin)
1399 fclose(f1);
1400 if (f2 != NULL && f2 != stdin)
1401 fclose(f2);
1402
1403 return rval;
1404}
1405
1406static int
1407files_differ(FILE *f1, FILE *f2, int flags)
1408{
1409 char buf1[BUFSIZ], buf2[BUFSIZ];
1410 size_t i, j;
1411
1412 if ((flags & (D_EMPTY1 | D_EMPTY2)) || stb1.st_size != stb2.st_size
1413 || (stb1.st_mode & S_IFMT) != (stb2.st_mode & S_IFMT))
1414 return 1;
1415
1416 if (stb1.st_dev == stb2.st_dev && stb1.st_ino == stb2.st_ino)
1417 return 0;
1418
1419 for (;;) {
1420 i = fread(buf1, 1, sizeof(buf1), f1);
1421 j = fread(buf2, 1, sizeof(buf2), f2);
1422 if ((!i && ferror(f1)) || (!j && ferror(f2)))
1423 return -1;
1424 if (i != j)
1425 return 1;
1426 if (i == 0)
1427 return 0;
1428 if (memcmp(buf1, buf2, i) != 0)
1429 return 1;
1430 }
1431}
1432
1433static FILE *
1434opentemp(const char *f)
1435{
1436 char buf[BUFSIZ], tempfile[PATH_MAX];
1437 ssize_t nread;
1438 int ifd, ofd;
1439
1440 if (strcmp(f, "-") == 0)
1441 ifd = STDIN_FILENO;
1442 else if ((ifd = open(f, O_RDONLY, 0644)) == -1)
1443 return NULL;
1444
1445 strlcpy(tempfile, ARUU_PATH_TMP "/diff.XXXXXXXX", sizeof(tempfile));
1446 if ((ofd = mkstemp(tempfile)) == -1) {
1447 close(ifd);
1448 return NULL;
1449 }
1450 unlink(tempfile);
1451 while ((nread = read(ifd, buf, BUFSIZ)) > 0) {
1452 if (write(ofd, buf, nread) != nread) {
1453 close(ifd);
1454 close(ofd);
1455 return NULL;
1456 }
1457 }
1458 close(ifd);
1459 lseek(ofd, (off_t)0, SEEK_SET);
1460 return fdopen(ofd, "r");
1461}
1462
1463static int
1464prepare(int i, FILE *fd, size_t filesize, int flags)
1465{
1466 struct Line *p;
1467 unsigned h;
1468 size_t sz, j = 0;
1469 enum Readhash r;
1470
1471 rewind(fd);
1472 sz = filesize / 25;
1473 if (sz < 100)
1474 sz = 100;
1475
1476 p = ecalloc(sz + 3, sizeof(*p));
1477 while ((r = readhash(fd, flags, &h)) != RH_EOF) {
1478 if (r == RH_BINARY)
1479 return 0;
1480 if (j == SIZE_MAX)
1481 break;
1482 if (j == sz) {
1483 sz = sz * 3 / 2;
1484 p = ereallocarray(p, sz + 3, sizeof(*p));
1485 }
1486 p[++j].value = h;
1487 }
1488 len[i] = j;
1489 file[i] = p;
1490 return 1;
1491}
1492
1493static void
1494prune(void)
1495{
1496 size_t i, j;
1497
1498 for (pref = 0;
1499 pref < len[0] && pref < len[1] && file[0][pref + 1].value == file[1][pref + 1].value;
1500 pref++)
1501 ;
1502 for (suff = 0; suff < len[0] - pref && suff < len[1] - pref
1503 && file[0][len[0] - suff].value == file[1][len[1] - suff].value;
1504 suff++)
1505 ;
1506 for (j = 0; j < 2; j++) {
1507 sfile[j] = file[j] + pref;
1508 slen[j] = len[j] - pref - suff;
1509 for (i = 0; i <= slen[j]; i++)
1510 sfile[j][i].serial = i;
1511 }
1512}
1513
1514static void
1515equiv(struct Line *a, int n, struct Line *b, int m, int *c)
1516{
1517 int i, j;
1518
1519 i = j = 1;
1520 while (i <= n && j <= m) {
1521 if (a[i].value < b[j].value)
1522 a[i++].value = 0;
1523 else if (a[i].value == b[j].value)
1524 a[i++].value = j;
1525 else
1526 j++;
1527 }
1528 while (i <= n)
1529 a[i++].value = 0;
1530 b[m + 1].value = 0;
1531 j = 0;
1532 while (++j <= m) {
1533 c[j] = -b[j].serial;
1534 while (b[j + 1].value == b[j].value) {
1535 j++;
1536 c[j] = b[j].serial;
1537 }
1538 }
1539 c[j] = -1;
1540}
1541
1542static int
1543stone(int *a, int n, int *b, int *c, int flags)
1544{
1545 int i, k, y, j, l;
1546 int oldc, tc, oldl, sq;
1547 unsigned numtries, bound;
1548
1549 if (flags & D_MINIMAL)
1550 bound = UINT_MAX;
1551 else {
1552 sq = sqrt(n);
1553 bound = MAX(256, sq);
1554 }
1555
1556 k = 0;
1557 c[0] = newcand(0, 0, 0);
1558 for (i = 1; i <= n; i++) {
1559 j = a[i];
1560 if (j == 0)
1561 continue;
1562 y = -b[j];
1563 oldl = 0;
1564 oldc = c[0];
1565 numtries = 0;
1566 do {
1567 if (y <= clist[oldc].y)
1568 continue;
1569 l = search(c, k, y);
1570 if (l != oldl + 1)
1571 oldc = c[l - 1];
1572 if (l <= k) {
1573 if (clist[c[l]].y <= y)
1574 continue;
1575 tc = c[l];
1576 c[l] = newcand(i, y, oldc);
1577 oldc = tc;
1578 oldl = l;
1579 numtries++;
1580 } else {
1581 c[l] = newcand(i, y, oldc);
1582 k++;
1583 break;
1584 }
1585 } while ((y = b[++j]) > 0 && numtries < bound);
1586 }
1587 return k;
1588}
1589
1590static int
1591newcand(int x, int y, int pred)
1592{
1593 struct Cand *q;
1594
1595 if (clen == clistlen) {
1596 clistlen = clistlen * 11 / 10;
1597 clist = ereallocarray(clist, clistlen, sizeof(*clist));
1598 }
1599 q = clist + clen;
1600 q->x = x;
1601 q->y = y;
1602 q->pred = pred;
1603 return clen++;
1604}
1605
1606static int
1607search(int *c, int k, int y)
1608{
1609 int i, j, l, t;
1610
1611 if (clist[c[k]].y < y)
1612 return k + 1;
1613 i = 0;
1614 j = k + 1;
1615 for (;;) {
1616 l = (i + j) / 2;
1617 if (l <= i)
1618 break;
1619 t = clist[c[l]].y;
1620 if (t > y)
1621 j = l;
1622 else if (t < y)
1623 i = l;
1624 else
1625 return l;
1626 }
1627 return l + 1;
1628}
1629
1630static void
1631unravel(int p)
1632{
1633 struct Cand *q;
1634 size_t i;
1635
1636 for (i = 0; i <= len[0]; i++)
1637 J[i] = i <= pref ? i : i > len[0] - suff ? i + len[1] - len[0] : 0;
1638 for (q = clist + p; q->y != 0; q = clist + q->pred)
1639 J[q->x + pref] = q->y + pref;
1640}
1641
1642static void
1643check(FILE *f1, FILE *f2, int flags)
1644{
1645 int i, j, c, d;
1646 long ctold, ctnew;
1647
1648 rewind(f1);
1649 rewind(f2);
1650 j = 1;
1651 ixold[0] = ixnew[0] = 0;
1652 ctold = ctnew = 0;
1653 for (i = 1; i <= (int)len[0]; i++) {
1654 if (J[i] == 0) {
1655 ixold[i] = ctold += skipline(f1);
1656 continue;
1657 }
1658 while (j < J[i]) {
1659 ixnew[j] = ctnew += skipline(f2);
1660 j++;
1661 }
1662 if (flags & (D_FOLDBLANKS | D_IGNOREBLANKS | D_IGNORECASE | D_STRIPCR)) {
1663 for (;;) {
1664 c = getc(f1);
1665 d = getc(f2);
1666 if (flags & (D_FOLDBLANKS | D_IGNOREBLANKS)) {
1667 if (c == EOF && isspace(d)) {
1668 ctnew++;
1669 break;
1670 } else if (isspace(c) && d == EOF) {
1671 ctold++;
1672 break;
1673 }
1674 }
1675 ctold++;
1676 ctnew++;
1677 if (flags & D_STRIPCR && (c == '\r' || d == '\r')) {
1678 if (c == '\r') {
1679 if ((c = getc(f1)) == '\n')
1680 ctold++;
1681 else
1682 ungetc(c, f1);
1683 }
1684 if (d == '\r') {
1685 if ((d = getc(f2)) == '\n')
1686 ctnew++;
1687 else
1688 ungetc(d, f2);
1689 }
1690 break;
1691 }
1692 if ((flags & D_FOLDBLANKS) && isspace(c) && isspace(d)) {
1693 do {
1694 if (c == '\n')
1695 break;
1696 ctold++;
1697 } while (isspace(c = getc(f1)));
1698 do {
1699 if (d == '\n')
1700 break;
1701 ctnew++;
1702 } while (isspace(d = getc(f2)));
1703 } else if (flags & D_IGNOREBLANKS) {
1704 while (isspace(c) && c != '\n') {
1705 c = getc(f1);
1706 ctold++;
1707 }
1708 while (isspace(d) && d != '\n') {
1709 d = getc(f2);
1710 ctnew++;
1711 }
1712 }
1713 if (chrtran(c) != chrtran(d)) {
1714 J[i] = 0;
1715 if (c != '\n' && c != EOF)
1716 ctold += skipline(f1);
1717 if (d != '\n' && c != EOF)
1718 ctnew += skipline(f2);
1719 break;
1720 }
1721 if (c == '\n' || c == EOF)
1722 break;
1723 }
1724 } else {
1725 for (;;) {
1726 ctold++;
1727 ctnew++;
1728 if ((c = getc(f1)) != (d = getc(f2))) {
1729 J[i] = 0;
1730 if (c != '\n' && c != EOF)
1731 ctold += skipline(f1);
1732 if (d != '\n' && c != EOF)
1733 ctnew += skipline(f2);
1734 break;
1735 }
1736 if (c == '\n' || c == EOF)
1737 break;
1738 }
1739 }
1740 ixold[i] = ctold;
1741 ixnew[j] = ctnew;
1742 j++;
1743 }
1744 for (; j <= (int)len[1]; j++) {
1745 ixnew[j] = ctnew += skipline(f2);
1746 }
1747}
1748
1749static void
1750sort(struct Line *a, int n)
1751{
1752 struct Line *ai, *aim, w;
1753 int j, m = 0, k;
1754
1755 if (n == 0)
1756 return;
1757 for (j = 1; j <= n; j *= 2)
1758 m = 2 * j - 1;
1759 for (m /= 2; m != 0; m /= 2) {
1760 k = n - m;
1761 for (j = 1; j <= k; j++) {
1762 for (ai = &a[j]; ai > a; ai -= m) {
1763 aim = &ai[m];
1764 if (aim < ai)
1765 break;
1766 if (aim->value > ai[0].value || (aim->value == ai[0].value && aim->serial > ai[0].serial))
1767 break;
1768 w.value = ai[0].value;
1769 ai[0].value = aim->value;
1770 aim->value = w.value;
1771 w.serial = ai[0].serial;
1772 ai[0].serial = aim->serial;
1773 aim->serial = w.serial;
1774 }
1775 }
1776 }
1777}
1778
1779static void
1780unsort(struct Line *f, int l, int *b)
1781{
1782 int *a, i;
1783
1784 a = ecalloc(l + 1, sizeof(*a));
1785 for (i = 1; i <= l; i++)
1786 a[f[i].serial] = f[i].value;
1787 for (i = 1; i <= l; i++)
1788 b[i] = a[i];
1789 free(a);
1790}
1791
1792static int
1793skipline(FILE *f)
1794{
1795 int i, c;
1796
1797 for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
1798 continue;
1799 return i;
1800}
1801
1802static void
1803output(char *file1, FILE *f1, char *file2, FILE *f2, int flags)
1804{
1805 int i, j, m, i0, i1, j0, j1, nc;
1806
1807 rewind(f1);
1808 rewind(f2);
1809 m = len[0];
1810 J[0] = 0;
1811 J[m + 1] = len[1] + 1;
1812 if (diff_format != D_EDIT) {
1813 for (i0 = 1; i0 <= m; i0 = i1 + 1) {
1814 while (i0 <= m && J[i0] == J[i0 - 1] + 1) {
1815 if (diff_format == D_SIDEBYSIDE && suppress_common != 1) {
1816 nc = fetch(ixold, i0, i0, f1, '\0', 1, flags);
1817 print_space(nc, hw - nc + lpad + 1 + rpad, flags);
1818 fetch(ixnew, J[i0], J[i0], f2, '\0', 0, flags);
1819 printf("\n");
1820 }
1821 i0++;
1822 }
1823 j0 = J[i0 - 1] + 1;
1824 i1 = i0 - 1;
1825 while (i1 < m && J[i1 + 1] == 0)
1826 i1++;
1827 j1 = J[i1 + 1] - 1;
1828 J[i1] = j1;
1829
1830 if (diff_format == D_SIDEBYSIDE) {
1831 for (i = i0, j = j0; i <= i1 && j <= j1; i++, j++)
1832 change(file1, f1, file2, f2, i, i, j, j, &flags);
1833 while (i <= i1) {
1834 change(file1, f1, file2, f2, i, i, j + 1, j, &flags);
1835 i++;
1836 }
1837 while (j <= j1) {
1838 change(file1, f1, file2, f2, i + 1, i, j, j, &flags);
1839 j++;
1840 }
1841 } else
1842 change(file1, f1, file2, f2, i0, i1, j0, j1, &flags);
1843 }
1844 } else {
1845 for (i0 = m; i0 >= 1; i0 = i1 - 1) {
1846 while (i0 >= 1 && J[i0] == J[i0 + 1] - 1 && J[i0] != 0)
1847 i0--;
1848 j0 = J[i0 + 1] - 1;
1849 i1 = i0 + 1;
1850 while (i1 > 1 && J[i1 - 1] == 0)
1851 i1--;
1852 j1 = J[i1 - 1] + 1;
1853 J[i1] = j1;
1854 change(file1, f1, file2, f2, i1, i0, j1, j0, &flags);
1855 }
1856 }
1857 if (m == 0)
1858 change(file1, f1, file2, f2, 1, 0, 1, len[1], &flags);
1859 if (diff_format == D_IFDEF || diff_format == D_GFORMAT) {
1860 for (;;) {
1861#define c i0
1862 if ((c = getc(f1)) == EOF)
1863 return;
1864 printf("%c", c);
1865 }
1866#undef c
1867 }
1868 if (anychange != 0) {
1869 if (diff_format == D_CONTEXT)
1870 dump_context_vec(f1, f2, flags);
1871 else if (diff_format == D_UNIFIED)
1872 dump_unified_vec(f1, f2, flags);
1873 }
1874}
1875
1876static void
1877range(int a, int b, const char *separator)
1878{
1879 printf("%d", a > b ? b : a);
1880 if (a < b)
1881 printf("%s%d", separator, b);
1882}
1883
1884static void
1885uni_range(int a, int b)
1886{
1887 if (a < b)
1888 printf("%d,%d", a, b - a + 1);
1889 else if (a == b)
1890 printf("%d", b);
1891 else
1892 printf("%d,0", b);
1893}
1894
1895static char *
1896preadline(int fd, size_t rlen, off_t off)
1897{
1898 char *line;
1899 ssize_t nr;
1900
1901 line = emalloc(rlen + 1);
1902 if ((nr = pread(fd, line, rlen, off)) == -1)
1903 enprintf(2, "preadline");
1904 if (nr > 0 && line[nr - 1] == '\n')
1905 nr--;
1906 line[nr] = '\0';
1907 return line;
1908}
1909
1910static int
1911ignoreline_pattern(char *line)
1912{
1913 int ret;
1914
1915 ret = regexec(&ignore_re, line, 0, NULL, 0);
1916 return ret == 0;
1917}
1918
1919static int
1920ignoreline(char *line, int skip_blanks)
1921{
1922 if (skip_blanks && *line == '\0')
1923 return 1;
1924 if (ignore_pats != NULL && ignoreline_pattern(line))
1925 return 1;
1926 return 0;
1927}
1928
1929static void
1930change(char *file1, FILE *f1, char *file2, FILE *f2, int a, int b, int c, int d, int *pflags)
1931{
1932 static size_t max_context = 64;
1933 long curpos;
1934 int i, nc;
1935 const char *walk;
1936 int skip_blanks, ignore;
1937
1938 skip_blanks = (*pflags & D_SKIPBLANKLINES);
1939restart:
1940 if ((diff_format != D_IFDEF || diff_format == D_GFORMAT) && a > b && c > d)
1941 return;
1942 if (ignore_pats != NULL || skip_blanks) {
1943 char *line;
1944 if (a <= b) {
1945 for (i = a; i <= b; i++) {
1946 line = preadline(fileno(f1), ixold[i] - ixold[i - 1], ixold[i - 1]);
1947 ignore = ignoreline(line, skip_blanks);
1948 free(line);
1949 if (!ignore)
1950 goto proceed;
1951 }
1952 }
1953 if (a > b || c <= d) {
1954 for (i = c; i <= d; i++) {
1955 line = preadline(fileno(f2), ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
1956 ignore = ignoreline(line, skip_blanks);
1957 free(line);
1958 if (!ignore)
1959 goto proceed;
1960 }
1961 }
1962 return;
1963 }
1964proceed:
1965 if (*pflags & D_HEADER && diff_format != D_BRIEF) {
1966 printf("%s %s %s\n", diffargs, file1, file2);
1967 *pflags &= ~D_HEADER;
1968 }
1969 if (diff_format == D_CONTEXT || diff_format == D_UNIFIED) {
1970 if (context_vec_start == NULL || context_vec_ptr == context_vec_end - 1) {
1971 ptrdiff_t offset = -1;
1972
1973 if (context_vec_start != NULL)
1974 offset = context_vec_ptr - context_vec_start;
1975 max_context <<= 1;
1976 context_vec_start = ereallocarray(context_vec_start, max_context, sizeof(*context_vec_start));
1977 context_vec_end = context_vec_start + max_context;
1978 context_vec_ptr = context_vec_start + offset;
1979 }
1980 if (anychange == 0) {
1981 print_header(file1, file2);
1982 anychange = 1;
1983 } else if (
1984 a > context_vec_ptr->b + (2 * diff_context) + 1
1985 && c > context_vec_ptr->d + (2 * diff_context) + 1
1986 ) {
1987 if (diff_format == D_CONTEXT)
1988 dump_context_vec(f1, f2, *pflags);
1989 else
1990 dump_unified_vec(f1, f2, *pflags);
1991 }
1992 context_vec_ptr++;
1993 context_vec_ptr->a = a;
1994 context_vec_ptr->b = b;
1995 context_vec_ptr->c = c;
1996 context_vec_ptr->d = d;
1997 return;
1998 }
1999 if (anychange == 0)
2000 anychange = 1;
2001 switch (diff_format) {
2002 case D_BRIEF:
2003 return;
2004 case D_NORMAL:
2005 case D_EDIT:
2006 range(a, b, ",");
2007 printf("%c", a > b ? 'a' : c > d ? 'd' : 'c');
2008 if (diff_format == D_NORMAL)
2009 range(c, d, ",");
2010 printf("\n");
2011 break;
2012 case D_REVERSE:
2013 printf("%c", a > b ? 'a' : c > d ? 'd' : 'c');
2014 range(a, b, " ");
2015 printf("\n");
2016 break;
2017 case D_NREVERSE:
2018 if (a > b)
2019 printf("a%d %d\n", b, d - c + 1);
2020 else {
2021 printf("d%d %d\n", a, b - a + 1);
2022 if (!(c > d))
2023 printf("a%d %d\n", b, d - c + 1);
2024 }
2025 break;
2026 }
2027 if (diff_format == D_GFORMAT) {
2028 curpos = ftell(f1);
2029 nc = ixold[a > b ? b : a - 1] - curpos;
2030 for (i = 0; i < nc; i++)
2031 printf("%c", getc(f1));
2032 for (walk = group_format; *walk != '\0'; walk++) {
2033 if (*walk == '%') {
2034 walk++;
2035 switch (*walk) {
2036 case '<':
2037 fetch(ixold, a, b, f1, '<', 1, *pflags);
2038 break;
2039 case '>':
2040 fetch(ixnew, c, d, f2, '>', 0, *pflags);
2041 break;
2042 default:
2043 printf("%%%c", *walk);
2044 break;
2045 }
2046 continue;
2047 }
2048 printf("%c", *walk);
2049 }
2050 }
2051 if (diff_format == D_SIDEBYSIDE) {
2052 if (color && a > b)
2053 printf("\033[%sm", add_code);
2054 else if (color && c > d)
2055 printf("\033[%sm", del_code);
2056 if (a > b) {
2057 print_space(0, hw + lpad, *pflags);
2058 } else {
2059 nc = fetch(ixold, a, b, f1, '\0', 1, *pflags);
2060 print_space(nc, hw - nc + lpad, *pflags);
2061 }
2062 if (color && a > b)
2063 printf("\033[%sm", add_code);
2064 else if (color && c > d)
2065 printf("\033[%sm", del_code);
2066 printf("%c", (a > b) ? '>' : ((c > d) ? '<' : '|'));
2067 if (color && c > d)
2068 printf("\033[m");
2069 print_space(hw + lpad + 1, rpad, *pflags);
2070 fetch(ixnew, c, d, f2, '\0', 0, *pflags);
2071 printf("\n");
2072 }
2073 if (diff_format == D_NORMAL || diff_format == D_IFDEF) {
2074 fetch(ixold, a, b, f1, '<', 1, *pflags);
2075 if (a <= b && c <= d && diff_format == D_NORMAL)
2076 printf("---\n");
2077 }
2078 if (diff_format != D_GFORMAT && diff_format != D_SIDEBYSIDE)
2079 fetch(ixnew, c, d, f2, diff_format == D_NORMAL ? '>' : '\0', 0, *pflags);
2080 if (edoffset != 0 && diff_format == D_EDIT) {
2081 printf(".\n");
2082 printf("%ds/.//\n", a + edoffset - 1);
2083 b = a + edoffset - 1;
2084 a = b + 1;
2085 c += edoffset;
2086 goto restart;
2087 }
2088 if ((diff_format == D_EDIT || diff_format == D_REVERSE) && c <= d)
2089 printf(".\n");
2090 if (inifdef) {
2091 printf("#endif /* %s */\n", ifdefname);
2092 inifdef = 0;
2093 }
2094}
2095
2096static int
2097fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile, int flags)
2098{
2099 int i, j, c, lastc, col, nc, newcol;
2100
2101 edoffset = 0;
2102 nc = 0;
2103 col = 0;
2104 if ((diff_format == D_IFDEF) && oldfile) {
2105 long curpos = ftell(lb);
2106 nc = f[a > b ? b : a - 1] - curpos;
2107 for (i = 0; i < nc; i++)
2108 printf("%c", getc(lb));
2109 }
2110 if (a > b)
2111 return 0;
2112 if (diff_format == D_IFDEF) {
2113 if (inifdef) {
2114 printf("#else /* %s%s */\n", oldfile == 1 ? "!" : "", ifdefname);
2115 } else {
2116 if (oldfile)
2117 printf("#ifndef %s\n", ifdefname);
2118 else
2119 printf("#ifdef %s\n", ifdefname);
2120 }
2121 inifdef = 1 + oldfile;
2122 }
2123 for (i = a; i <= b; i++) {
2124 fseek(lb, f[i - 1], SEEK_SET);
2125 nc = f[i] - f[i - 1];
2126 if (diff_format == D_SIDEBYSIDE && hw < nc)
2127 nc = hw;
2128 if (diff_format != D_IFDEF && diff_format != D_GFORMAT && ch != '\0') {
2129 if (color && (ch == '>' || ch == '+'))
2130 printf("\033[%sm", add_code);
2131 else if (color && (ch == '<' || ch == '-'))
2132 printf("\033[%sm", del_code);
2133 printf("%c", ch);
2134 if (Tflag
2135 && (diff_format == D_NORMAL || diff_format == D_CONTEXT || diff_format == D_UNIFIED))
2136 printf("\t");
2137 else if (diff_format != D_UNIFIED)
2138 printf(" ");
2139 }
2140 col = j = 0;
2141 lastc = '\0';
2142 while (j < nc && (hw == 0 || col < hw)) {
2143 c = getc(lb);
2144 if (flags & D_STRIPCR && c == '\r') {
2145 if ((c = getc(lb)) == '\n')
2146 j++;
2147 else {
2148 ungetc(c, lb);
2149 c = '\r';
2150 }
2151 }
2152 if (c == EOF) {
2153 if (diff_format == D_EDIT || diff_format == D_REVERSE || diff_format == D_NREVERSE)
2154 weprintf("No newline at end of file");
2155 else
2156 printf(
2157 "\n\\ No newline at end of "
2158 "file\n"
2159 );
2160 return col;
2161 }
2162 if (c == '\t') {
2163 newcol = roundup(col + 1, tabsize);
2164 if ((flags & D_EXPANDTABS) == 0) {
2165 if (hw > 0 && newcol >= hw)
2166 return col;
2167 printf("\t");
2168 } else {
2169 if (hw > 0 && newcol > hw)
2170 newcol = hw;
2171 printf("%*s", newcol - col, "");
2172 }
2173 col = newcol;
2174 } else {
2175 if (diff_format == D_EDIT && j == 1 && c == '\n' && lastc == '.') {
2176 printf(".\n");
2177 edoffset = i - a + 1;
2178 return edoffset;
2179 }
2180 if (diff_format != D_SIDEBYSIDE || c != '\n') {
2181 if (color && c == '\n')
2182 printf("\033[m%c", c);
2183 else
2184 printf("%c", c);
2185 col++;
2186 }
2187 }
2188 j++;
2189 lastc = c;
2190 }
2191 }
2192 if (color && diff_format == D_SIDEBYSIDE)
2193 printf("\033[m");
2194 return col;
2195}
2196
2197static enum Readhash
2198readhash(FILE *f, int flags, unsigned *hash)
2199{
2200 int i, t, space;
2201 unsigned sum;
2202
2203 sum = 1;
2204 space = 0;
2205 for (i = 0;;) {
2206 switch (t = getc(f)) {
2207 case '\0':
2208 if ((flags & D_FORCEASCII) == 0)
2209 return RH_BINARY;
2210 goto hashchar;
2211 case '\r':
2212 if (flags & D_STRIPCR) {
2213 t = getc(f);
2214 if (t == '\n')
2215 break;
2216 ungetc(t, f);
2217 }
2218 /* FALLTHROUGH */
2219 case '\t':
2220 case '\v':
2221 case '\f':
2222 case ' ':
2223 if ((flags & (D_FOLDBLANKS | D_IGNOREBLANKS)) != 0) {
2224 space++;
2225 continue;
2226 }
2227 /* FALLTHROUGH */
2228 default:
2229 hashchar:
2230 if (space && (flags & D_IGNOREBLANKS) == 0) {
2231 i++;
2232 space = 0;
2233 }
2234 sum = sum * 127 + chrtran(t);
2235 i++;
2236 continue;
2237 case EOF:
2238 if (i == 0)
2239 return RH_EOF;
2240 /* FALLTHROUGH */
2241 case '\n':
2242 break;
2243 }
2244 break;
2245 }
2246 *hash = sum;
2247 return RH_OK;
2248}
2249
2250static int
2251asciifile(FILE *f)
2252{
2253 unsigned char buf[BUFSIZ];
2254 size_t cnt;
2255
2256 if (f == NULL)
2257 return 1;
2258
2259 rewind(f);
2260 cnt = fread(buf, 1, sizeof(buf), f);
2261 return memchr(buf, '\0', cnt) == NULL;
2262}
2263
2264#define begins_with(s, pre) (strncmp(s, pre, sizeof(pre) - 1) == 0)
2265
2266static char *
2267match_function(const long *f, int pos, FILE *fp)
2268{
2269 char buf[FUNCTION_CONTEXT_SIZE];
2270 size_t nc;
2271 int last = lastline;
2272 const char *state = NULL;
2273
2274 lastline = pos;
2275 for (; pos > last; pos--) {
2276 fseek(fp, f[pos - 1], SEEK_SET);
2277 nc = f[pos] - f[pos - 1];
2278 if (nc >= sizeof(buf))
2279 nc = sizeof(buf) - 1;
2280 nc = fread(buf, 1, nc, fp);
2281 if (nc == 0)
2282 continue;
2283 buf[nc] = '\0';
2284 buf[strcspn(buf, "\n")] = '\0';
2285 if (most_recent_pat != NULL) {
2286 int ret = regexec(&most_recent_re, buf, 0, NULL, 0);
2287 if (ret != 0)
2288 continue;
2289 strlcpy(lastbuf, buf, sizeof(lastbuf));
2290 lastmatchline = pos;
2291 return lastbuf;
2292 } else if (
2293 isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$' || buf[0] == '-' || buf[0] == '+'
2294 ) {
2295 if (begins_with(buf, "private:")) {
2296 if (!state)
2297 state = " (private)";
2298 } else if (begins_with(buf, "protected:")) {
2299 if (!state)
2300 state = " (protected)";
2301 } else if (begins_with(buf, "public:")) {
2302 if (!state)
2303 state = " (public)";
2304 } else {
2305 strlcpy(lastbuf, buf, sizeof(lastbuf));
2306 if (state)
2307 strlcat(lastbuf, state, sizeof(lastbuf));
2308 lastmatchline = pos;
2309 return lastbuf;
2310 }
2311 }
2312 }
2313 return lastmatchline > 0 ? lastbuf : NULL;
2314}
2315
2316static void
2317dump_context_vec(FILE *f1, FILE *f2, int flags)
2318{
2319 struct ContextVec *cvp = context_vec_start;
2320 int lowa, upb, lowc, upd, do_output;
2321 int a, b, c, d;
2322 char ch, *f;
2323
2324 if (context_vec_start > context_vec_ptr)
2325 return;
2326
2327 b = d = 0;
2328 lowa = MAX(1, cvp->a - diff_context);
2329 upb = MIN((int)len[0], context_vec_ptr->b + diff_context);
2330 lowc = MAX(1, cvp->c - diff_context);
2331 upd = MIN((int)len[1], context_vec_ptr->d + diff_context);
2332
2333 printf("***************");
2334 if (flags & (D_PROTOTYPE | D_MATCHLAST)) {
2335 f = match_function(ixold, cvp->a - 1, f1);
2336 if (f != NULL)
2337 printf(" %s", f);
2338 }
2339 printf("\n*** ");
2340 range(lowa, upb, ",");
2341 printf(" ****\n");
2342
2343 do_output = 0;
2344 for (; cvp <= context_vec_ptr; cvp++)
2345 if (cvp->a <= cvp->b) {
2346 cvp = context_vec_start;
2347 do_output++;
2348 break;
2349 }
2350 if (do_output) {
2351 while (cvp <= context_vec_ptr) {
2352 a = cvp->a;
2353 b = cvp->b;
2354 c = cvp->c;
2355 d = cvp->d;
2356
2357 if (a <= b && c <= d)
2358 ch = 'c';
2359 else
2360 ch = (a <= b) ? 'd' : 'a';
2361
2362 if (ch == 'a')
2363 fetch(ixold, lowa, b, f1, ' ', 0, flags);
2364 else {
2365 fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
2366 fetch(ixold, a, b, f1, ch == 'c' ? '!' : '-', 0, flags);
2367 }
2368 lowa = b + 1;
2369 cvp++;
2370 }
2371 fetch(ixold, b + 1, upb, f1, ' ', 0, flags);
2372 }
2373
2374 printf("--- ");
2375 range(lowc, upd, ",");
2376 printf(" ----\n");
2377
2378 do_output = 0;
2379 for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
2380 if (cvp->c <= cvp->d) {
2381 cvp = context_vec_start;
2382 do_output++;
2383 break;
2384 }
2385 if (do_output) {
2386 while (cvp <= context_vec_ptr) {
2387 a = cvp->a;
2388 b = cvp->b;
2389 c = cvp->c;
2390 d = cvp->d;
2391
2392 if (a <= b && c <= d)
2393 ch = 'c';
2394 else
2395 ch = (a <= b) ? 'd' : 'a';
2396
2397 if (ch == 'd')
2398 fetch(ixnew, lowc, d, f2, ' ', 0, flags);
2399 else {
2400 fetch(ixnew, lowc, c - 1, f2, ' ', 0, flags);
2401 fetch(ixnew, c, d, f2, ch == 'c' ? '!' : '+', 0, flags);
2402 }
2403 lowc = d + 1;
2404 cvp++;
2405 }
2406 fetch(ixnew, d + 1, upd, f2, ' ', 0, flags);
2407 }
2408 context_vec_ptr = context_vec_start - 1;
2409}
2410
2411static void
2412dump_unified_vec(FILE *f1, FILE *f2, int flags)
2413{
2414 struct ContextVec *cvp = context_vec_start;
2415 int lowa, upb, lowc, upd;
2416 int a, b, c, d;
2417 char ch, *f;
2418
2419 if (context_vec_start > context_vec_ptr)
2420 return;
2421
2422 b = d = 0;
2423 lowa = MAX(1, cvp->a - diff_context);
2424 upb = MIN((int)len[0], context_vec_ptr->b + diff_context);
2425 lowc = MAX(1, cvp->c - diff_context);
2426 upd = MIN((int)len[1], context_vec_ptr->d + diff_context);
2427
2428 printf("@@ -");
2429 uni_range(lowa, upb);
2430 printf(" +");
2431 uni_range(lowc, upd);
2432 printf(" @@");
2433 if (flags & (D_PROTOTYPE | D_MATCHLAST)) {
2434 f = match_function(ixold, cvp->a - 1, f1);
2435 if (f != NULL)
2436 printf(" %s", f);
2437 }
2438 printf("\n");
2439
2440 for (; cvp <= context_vec_ptr; cvp++) {
2441 a = cvp->a;
2442 b = cvp->b;
2443 c = cvp->c;
2444 d = cvp->d;
2445
2446 if (a <= b && c <= d)
2447 ch = 'c';
2448 else
2449 ch = (a <= b) ? 'd' : 'a';
2450
2451 switch (ch) {
2452 case 'c':
2453 fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
2454 fetch(ixold, a, b, f1, '-', 0, flags);
2455 fetch(ixnew, c, d, f2, '+', 0, flags);
2456 break;
2457 case 'd':
2458 fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
2459 fetch(ixold, a, b, f1, '-', 0, flags);
2460 break;
2461 case 'a':
2462 fetch(ixnew, lowc, c - 1, f2, ' ', 0, flags);
2463 fetch(ixnew, c, d, f2, '+', 0, flags);
2464 break;
2465 }
2466 lowa = b + 1;
2467 lowc = d + 1;
2468 }
2469 fetch(ixnew, d + 1, upd, f2, ' ', 0, flags);
2470 context_vec_ptr = context_vec_start - 1;
2471}
2472
2473static void
2474print_header(const char *file1, const char *file2)
2475{
2476 const char *time_format;
2477 char buf[256];
2478 struct tm tm1, tm2, *tm_ptr1, *tm_ptr2;
2479 int nsec1 = stb1.st_mtim.tv_nsec;
2480 int nsec2 = stb2.st_mtim.tv_nsec;
2481
2482 time_format = "%Y-%m-%d %H:%M:%S";
2483 if (cflag)
2484 time_format = "%c";
2485 tm_ptr1 = localtime_r(&stb1.st_mtime, &tm1);
2486 tm_ptr2 = localtime_r(&stb2.st_mtime, &tm2);
2487 if (label[0] != NULL)
2488 printf("%s %s\n", diff_format == D_CONTEXT ? "***" : "---", label[0]);
2489 else {
2490 strftime(buf, sizeof(buf), time_format, tm_ptr1);
2491 printf("%s %s\t%s", diff_format == D_CONTEXT ? "***" : "---", file1, buf);
2492 if (!cflag) {
2493 strftime(buf, sizeof(buf), "%z", tm_ptr1);
2494 printf(".%.9d %s", nsec1, buf);
2495 }
2496 printf("\n");
2497 }
2498 if (label[1] != NULL)
2499 printf("%s %s\n", diff_format == D_CONTEXT ? "---" : "+++", label[1]);
2500 else {
2501 strftime(buf, sizeof(buf), time_format, tm_ptr2);
2502 printf("%s %s\t%s", diff_format == D_CONTEXT ? "---" : "+++", file2, buf);
2503 if (!cflag) {
2504 strftime(buf, sizeof(buf), "%z", tm_ptr2);
2505 printf(".%.9d %s", nsec2, buf);
2506 }
2507 printf("\n");
2508 }
2509}
2510
2511static void
2512print_space(int nc, int n, int flags)
2513{
2514 int col, newcol, tabstop;
2515
2516 col = nc;
2517 newcol = nc + n;
2518 if ((flags & D_EXPANDTABS) == 0) {
2519 while ((tabstop = roundup(col + 1, tabsize)) <= newcol) {
2520 printf("\t");
2521 col = tabstop;
2522 }
2523 }
2524 printf("%*s", newcol - col, "");
2525}