]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/diff/diffreg.c
Merge libcxxrt master 8049924686b8414d8e652cbd2a52c763b48e8456
[FreeBSD/FreeBSD.git] / usr.bin / diff / diffreg.c
1 /*      $OpenBSD: diffreg.c,v 1.93 2019/06/28 13:35:00 deraadt Exp $    */
2
3 /*-
4  * SPDX-License-Identifier: BSD-4-Clause
5  *
6  * Copyright (C) Caldera International Inc.  2001-2002.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code and documentation must retain the above
13  *    copyright notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *      This product includes software developed or owned by Caldera
20  *      International, Inc.
21  * 4. Neither the name of Caldera International, Inc. nor the names of other
22  *    contributors may be used to endorse or promote products derived from
23  *    this software without specific prior written permission.
24  *
25  * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
26  * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
27  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29  * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
30  * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
35  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 /*-
39  * Copyright (c) 1991, 1993
40  *      The Regents of the University of California.  All rights reserved.
41  *
42  * Redistribution and use in source and binary forms, with or without
43  * modification, are permitted provided that the following conditions
44  * are met:
45  * 1. Redistributions of source code must retain the above copyright
46  *    notice, this list of conditions and the following disclaimer.
47  * 2. Redistributions in binary form must reproduce the above copyright
48  *    notice, this list of conditions and the following disclaimer in the
49  *    documentation and/or other materials provided with the distribution.
50  * 3. Neither the name of the University nor the names of its contributors
51  *    may be used to endorse or promote products derived from this software
52  *    without specific prior written permission.
53  *
54  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
55  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
56  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
57  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
58  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
59  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
60  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
61  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
62  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
63  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
64  * SUCH DAMAGE.
65  *
66  *      @(#)diffreg.c   8.1 (Berkeley) 6/6/93
67  */
68
69 #include <sys/cdefs.h>
70 __FBSDID("$FreeBSD$");
71
72 #include <sys/capsicum.h>
73 #include <sys/stat.h>
74
75 #include <capsicum_helpers.h>
76 #include <ctype.h>
77 #include <err.h>
78 #include <errno.h>
79 #include <fcntl.h>
80 #include <paths.h>
81 #include <regex.h>
82 #include <stdbool.h>
83 #include <stddef.h>
84 #include <stdint.h>
85 #include <stdio.h>
86 #include <stdlib.h>
87 #include <string.h>
88
89 #include "pr.h"
90 #include "diff.h"
91 #include "xmalloc.h"
92
93 /*
94  * diff - compare two files.
95  */
96
97 /*
98  *      Uses an algorithm due to Harold Stone, which finds
99  *      a pair of longest identical subsequences in the two
100  *      files.
101  *
102  *      The major goal is to generate the match vector J.
103  *      J[i] is the index of the line in file1 corresponding
104  *      to line i file0. J[i] = 0 if there is no
105  *      such line in file1.
106  *
107  *      Lines are hashed so as to work in core. All potential
108  *      matches are located by sorting the lines of each file
109  *      on the hash (called ``value''). In particular, this
110  *      collects the equivalence classes in file1 together.
111  *      Subroutine equiv replaces the value of each line in
112  *      file0 by the index of the first element of its
113  *      matching equivalence in (the reordered) file1.
114  *      To save space equiv squeezes file1 into a single
115  *      array member in which the equivalence classes
116  *      are simply concatenated, except that their first
117  *      members are flagged by changing sign.
118  *
119  *      Next the indices that point into member are unsorted into
120  *      array class according to the original order of file0.
121  *
122  *      The cleverness lies in routine stone. This marches
123  *      through the lines of file0, developing a vector klist
124  *      of "k-candidates". At step i a k-candidate is a matched
125  *      pair of lines x,y (x in file0 y in file1) such that
126  *      there is a common subsequence of length k
127  *      between the first i lines of file0 and the first y
128  *      lines of file1, but there is no such subsequence for
129  *      any smaller y. x is the earliest possible mate to y
130  *      that occurs in such a subsequence.
131  *
132  *      Whenever any of the members of the equivalence class of
133  *      lines in file1 matable to a line in file0 has serial number
134  *      less than the y of some k-candidate, that k-candidate
135  *      with the smallest such y is replaced. The new
136  *      k-candidate is chained (via pred) to the current
137  *      k-1 candidate so that the actual subsequence can
138  *      be recovered. When a member has serial number greater
139  *      that the y of all k-candidates, the klist is extended.
140  *      At the end, the longest subsequence is pulled out
141  *      and placed in the array J by unravel
142  *
143  *      With J in hand, the matches there recorded are
144  *      check'ed against reality to assure that no spurious
145  *      matches have crept in due to hashing. If they have,
146  *      they are broken, and "jackpot" is recorded--a harmless
147  *      matter except that a true match for a spuriously
148  *      mated line may now be unnecessarily reported as a change.
149  *
150  *      Much of the complexity of the program comes simply
151  *      from trying to minimize core utilization and
152  *      maximize the range of doable problems by dynamically
153  *      allocating what is needed and reusing what is not.
154  *      The core requirements for problems larger than somewhat
155  *      are (in words) 2*length(file0) + length(file1) +
156  *      3*(number of k-candidates installed),  typically about
157  *      6n words for files of length n.
158  */
159
160 struct cand {
161         int     x;
162         int     y;
163         int     pred;
164 };
165
166 static struct line {
167         int     serial;
168         int     value;
169 } *file[2];
170
171 /*
172  * The following struct is used to record change information when
173  * doing a "context" or "unified" diff.  (see routine "change" to
174  * understand the highly mnemonic field names)
175  */
176 struct context_vec {
177         int     a;              /* start line in old file */
178         int     b;              /* end line in old file */
179         int     c;              /* start line in new file */
180         int     d;              /* end line in new file */
181 };
182
183 #define MIN_PAD         1
184 static FILE     *opentemp(const char *);
185 static void      output(char *, FILE *, char *, FILE *, int);
186 static void      check(FILE *, FILE *, int);
187 static void      range(int, int, const char *);
188 static void      uni_range(int, int);
189 static void      dump_context_vec(FILE *, FILE *, int);
190 static void      dump_unified_vec(FILE *, FILE *, int);
191 static void      prepare(int, FILE *, size_t, int);
192 static void      prune(void);
193 static void      equiv(struct line *, int, struct line *, int, int *);
194 static void      unravel(int);
195 static void      unsort(struct line *, int, int *);
196 static void      change(char *, FILE *, char *, FILE *, int, int, int, int, int *);
197 static void      sort(struct line *, int);
198 static void      print_header(const char *, const char *);
199 static void      print_space(int, int, int);
200 static bool      ignoreline_pattern(char *);
201 static bool      ignoreline(char *, bool);
202 static int       asciifile(FILE *);
203 static int       fetch(long *, int, int, FILE *, int, int, int);
204 static int       newcand(int, int, int);
205 static int       search(int *, int, int);
206 static int       skipline(FILE *);
207 static int       isqrt(int);
208 static int       stone(int *, int, int *, int *, int);
209 static int       readhash(FILE *, int);
210 static int       files_differ(FILE *, FILE *, int);
211 static char     *match_function(const long *, int, FILE *);
212 static char     *preadline(int, size_t, off_t);
213
214 static int  *J;                 /* will be overlaid on class */
215 static int  *class;             /* will be overlaid on file[0] */
216 static int  *klist;             /* will be overlaid on file[0] after class */
217 static int  *member;            /* will be overlaid on file[1] */
218 static int   clen;
219 static int   inifdef;           /* whether or not we are in a #ifdef block */
220 static int   len[2];
221 static int   pref, suff;        /* length of prefix and suffix */
222 static int   slen[2];
223 static int   anychange;
224 static int   hw, padding;       /* half width and padding */
225 static int   edoffset;
226 static long *ixnew;             /* will be overlaid on file[1] */
227 static long *ixold;             /* will be overlaid on klist */
228 static struct cand *clist;      /* merely a free storage pot for candidates */
229 static int   clistlen;          /* the length of clist */
230 static struct line *sfile[2];   /* shortened by pruning common prefix/suffix */
231 static int (*chrtran)(int);     /* translation table for case-folding */
232 static struct context_vec *context_vec_start;
233 static struct context_vec *context_vec_end;
234 static struct context_vec *context_vec_ptr;
235
236 #define FUNCTION_CONTEXT_SIZE   55
237 static char lastbuf[FUNCTION_CONTEXT_SIZE];
238 static int lastline;
239 static int lastmatchline;
240
241 static int
242 clow2low(int c)
243 {
244
245         return (c);
246 }
247
248 static int
249 cup2low(int c)
250 {
251
252         return tolower(c);
253 }
254
255 int
256 diffreg(char *file1, char *file2, int flags, int capsicum)
257 {
258         FILE *f1, *f2;
259         int i, rval;
260         struct pr *pr = NULL;
261         cap_rights_t rights_ro;
262
263         f1 = f2 = NULL;
264         rval = D_SAME;
265         anychange = 0;
266         lastline = 0;
267         lastmatchline = 0;
268         context_vec_ptr = context_vec_start - 1;
269
270          /*
271           * hw excludes padding and make sure when -t is not used,
272           * the second column always starts from the closest tab stop
273           */
274         if (diff_format == D_SIDEBYSIDE) {
275                 hw = width >> 1;
276                 padding = tabsize - (hw % tabsize);
277                 if ((flags & D_EXPANDTABS) != 0 || (padding % tabsize == 0))
278                         padding = MIN_PAD;
279         
280                 hw = (width >> 1) -
281                     ((padding == MIN_PAD) ? (padding << 1) : padding) - 1;
282         }
283         
284
285         if (flags & D_IGNORECASE)
286                 chrtran = cup2low;
287         else
288                 chrtran = clow2low;
289         if (S_ISDIR(stb1.st_mode) != S_ISDIR(stb2.st_mode))
290                 return (S_ISDIR(stb1.st_mode) ? D_MISMATCH1 : D_MISMATCH2);
291         if (strcmp(file1, "-") == 0 && strcmp(file2, "-") == 0)
292                 goto closem;
293
294         if (flags & D_EMPTY1)
295                 f1 = fopen(_PATH_DEVNULL, "r");
296         else {
297                 if (!S_ISREG(stb1.st_mode)) {
298                         if ((f1 = opentemp(file1)) == NULL ||
299                             fstat(fileno(f1), &stb1) == -1) {
300                                 warn("%s", file1);
301                                 rval = D_ERROR;
302                                 status |= 2;
303                                 goto closem;
304                         }
305                 } else if (strcmp(file1, "-") == 0)
306                         f1 = stdin;
307                 else
308                         f1 = fopen(file1, "r");
309         }
310         if (f1 == NULL) {
311                 warn("%s", file1);
312                 rval = D_ERROR;
313                 status |= 2;
314                 goto closem;
315         }
316
317         if (flags & D_EMPTY2)
318                 f2 = fopen(_PATH_DEVNULL, "r");
319         else {
320                 if (!S_ISREG(stb2.st_mode)) {
321                         if ((f2 = opentemp(file2)) == NULL ||
322                             fstat(fileno(f2), &stb2) == -1) {
323                                 warn("%s", file2);
324                                 rval = D_ERROR;
325                                 status |= 2;
326                                 goto closem;
327                         }
328                 } else if (strcmp(file2, "-") == 0)
329                         f2 = stdin;
330                 else
331                         f2 = fopen(file2, "r");
332         }
333         if (f2 == NULL) {
334                 warn("%s", file2);
335                 rval = D_ERROR;
336                 status |= 2;
337                 goto closem;
338         }
339
340         if (lflag)
341                 pr = start_pr(file1, file2);
342
343         if (capsicum) {
344                 cap_rights_init(&rights_ro, CAP_READ, CAP_FSTAT, CAP_SEEK);
345                 if (caph_rights_limit(fileno(f1), &rights_ro) < 0)
346                         err(2, "unable to limit rights on: %s", file1);
347                 if (caph_rights_limit(fileno(f2), &rights_ro) < 0)
348                         err(2, "unable to limit rights on: %s", file2);
349                 if (fileno(f1) == STDIN_FILENO || fileno(f2) == STDIN_FILENO) {
350                         /* stdin has already been limited */
351                         if (caph_limit_stderr() == -1)
352                                 err(2, "unable to limit stderr");
353                         if (caph_limit_stdout() == -1)
354                                 err(2, "unable to limit stdout");
355                 } else if (caph_limit_stdio() == -1)
356                                 err(2, "unable to limit stdio");
357
358                 caph_cache_catpages();
359                 caph_cache_tzdata();
360                 if (caph_enter() < 0)
361                         err(2, "unable to enter capability mode");
362         }
363
364         switch (files_differ(f1, f2, flags)) {
365         case 0:
366                 goto closem;
367         case 1:
368                 break;
369         default:
370                 /* error */
371                 rval = D_ERROR;
372                 status |= 2;
373                 goto closem;
374         }
375
376         if (diff_format == D_BRIEF && ignore_pats == NULL &&
377             (flags & (D_FOLDBLANKS|D_IGNOREBLANKS|D_IGNORECASE|D_STRIPCR)) == 0)
378         {
379                 rval = D_DIFFER;
380                 status |= 1;
381                 goto closem;
382         }
383         if ((flags & D_FORCEASCII) == 0 &&
384             (!asciifile(f1) || !asciifile(f2))) {
385                 rval = D_BINARY;
386                 status |= 1;
387                 goto closem;
388         }
389         prepare(0, f1, stb1.st_size, flags);
390         prepare(1, f2, stb2.st_size, flags);
391
392         prune();
393         sort(sfile[0], slen[0]);
394         sort(sfile[1], slen[1]);
395
396         member = (int *)file[1];
397         equiv(sfile[0], slen[0], sfile[1], slen[1], member);
398         member = xreallocarray(member, slen[1] + 2, sizeof(*member));
399
400         class = (int *)file[0];
401         unsort(sfile[0], slen[0], class);
402         class = xreallocarray(class, slen[0] + 2, sizeof(*class));
403
404         klist = xcalloc(slen[0] + 2, sizeof(*klist));
405         clen = 0;
406         clistlen = 100;
407         clist = xcalloc(clistlen, sizeof(*clist));
408         i = stone(class, slen[0], member, klist, flags);
409         free(member);
410         free(class);
411
412         J = xreallocarray(J, len[0] + 2, sizeof(*J));
413         unravel(klist[i]);
414         free(clist);
415         free(klist);
416
417         ixold = xreallocarray(ixold, len[0] + 2, sizeof(*ixold));
418         ixnew = xreallocarray(ixnew, len[1] + 2, sizeof(*ixnew));
419         check(f1, f2, flags);
420         output(file1, f1, file2, f2, flags);
421
422 closem:
423         if (pr != NULL)
424                 stop_pr(pr);
425         if (anychange) {
426                 status |= 1;
427                 if (rval == D_SAME)
428                         rval = D_DIFFER;
429         }
430         if (f1 != NULL)
431                 fclose(f1);
432         if (f2 != NULL)
433                 fclose(f2);
434
435         return (rval);
436 }
437
438 /*
439  * Check to see if the given files differ.
440  * Returns 0 if they are the same, 1 if different, and -1 on error.
441  * XXX - could use code from cmp(1) [faster]
442  */
443 static int
444 files_differ(FILE *f1, FILE *f2, int flags)
445 {
446         char buf1[BUFSIZ], buf2[BUFSIZ];
447         size_t i, j;
448
449         if ((flags & (D_EMPTY1|D_EMPTY2)) || stb1.st_size != stb2.st_size ||
450             (stb1.st_mode & S_IFMT) != (stb2.st_mode & S_IFMT))
451                 return (1);
452         for (;;) {
453                 i = fread(buf1, 1, sizeof(buf1), f1);
454                 j = fread(buf2, 1, sizeof(buf2), f2);
455                 if ((!i && ferror(f1)) || (!j && ferror(f2)))
456                         return (-1);
457                 if (i != j)
458                         return (1);
459                 if (i == 0)
460                         return (0);
461                 if (memcmp(buf1, buf2, i) != 0)
462                         return (1);
463         }
464 }
465
466 static FILE *
467 opentemp(const char *f)
468 {
469         char buf[BUFSIZ], tempfile[PATH_MAX];
470         ssize_t nread;
471         int ifd, ofd;
472
473         if (strcmp(f, "-") == 0)
474                 ifd = STDIN_FILENO;
475         else if ((ifd = open(f, O_RDONLY, 0644)) == -1)
476                 return (NULL);
477
478         (void)strlcpy(tempfile, _PATH_TMP "/diff.XXXXXXXX", sizeof(tempfile));
479
480         if ((ofd = mkstemp(tempfile)) == -1) {
481                 close(ifd);
482                 return (NULL);
483         }
484         unlink(tempfile);
485         while ((nread = read(ifd, buf, BUFSIZ)) > 0) {
486                 if (write(ofd, buf, nread) != nread) {
487                         close(ifd);
488                         close(ofd);
489                         return (NULL);
490                 }
491         }
492         close(ifd);
493         lseek(ofd, (off_t)0, SEEK_SET);
494         return (fdopen(ofd, "r"));
495 }
496
497 char *
498 splice(char *dir, char *path)
499 {
500         char *tail, *buf;
501         size_t dirlen;
502
503         dirlen = strlen(dir);
504         while (dirlen != 0 && dir[dirlen - 1] == '/')
505             dirlen--;
506         if ((tail = strrchr(path, '/')) == NULL)
507                 tail = path;
508         else
509                 tail++;
510         xasprintf(&buf, "%.*s/%s", (int)dirlen, dir, tail);
511         return (buf);
512 }
513
514 static void
515 prepare(int i, FILE *fd, size_t filesize, int flags)
516 {
517         struct line *p;
518         int h;
519         size_t sz, j;
520
521         rewind(fd);
522
523         sz = MIN(filesize, SIZE_MAX) / 25;
524         if (sz < 100)
525                 sz = 100;
526
527         p = xcalloc(sz + 3, sizeof(*p));
528         for (j = 0; (h = readhash(fd, flags));) {
529                 if (j == sz) {
530                         sz = sz * 3 / 2;
531                         p = xreallocarray(p, sz + 3, sizeof(*p));
532                 }
533                 p[++j].value = h;
534         }
535         len[i] = j;
536         file[i] = p;
537 }
538
539 static void
540 prune(void)
541 {
542         int i, j;
543
544         for (pref = 0; pref < len[0] && pref < len[1] &&
545             file[0][pref + 1].value == file[1][pref + 1].value;
546             pref++)
547                 ;
548         for (suff = 0; suff < len[0] - pref && suff < len[1] - pref &&
549             file[0][len[0] - suff].value == file[1][len[1] - suff].value;
550             suff++)
551                 ;
552         for (j = 0; j < 2; j++) {
553                 sfile[j] = file[j] + pref;
554                 slen[j] = len[j] - pref - suff;
555                 for (i = 0; i <= slen[j]; i++)
556                         sfile[j][i].serial = i;
557         }
558 }
559
560 static void
561 equiv(struct line *a, int n, struct line *b, int m, int *c)
562 {
563         int i, j;
564
565         i = j = 1;
566         while (i <= n && j <= m) {
567                 if (a[i].value < b[j].value)
568                         a[i++].value = 0;
569                 else if (a[i].value == b[j].value)
570                         a[i++].value = j;
571                 else
572                         j++;
573         }
574         while (i <= n)
575                 a[i++].value = 0;
576         b[m + 1].value = 0;
577         j = 0;
578         while (++j <= m) {
579                 c[j] = -b[j].serial;
580                 while (b[j + 1].value == b[j].value) {
581                         j++;
582                         c[j] = b[j].serial;
583                 }
584         }
585         c[j] = -1;
586 }
587
588 /* Code taken from ping.c */
589 static int
590 isqrt(int n)
591 {
592         int y, x = 1;
593
594         if (n == 0)
595                 return (0);
596
597         do { /* newton was a stinker */
598                 y = x;
599                 x = n / x;
600                 x += y;
601                 x /= 2;
602         } while ((x - y) > 1 || (x - y) < -1);
603
604         return (x);
605 }
606
607 static int
608 stone(int *a, int n, int *b, int *c, int flags)
609 {
610         int i, k, y, j, l;
611         int oldc, tc, oldl, sq;
612         u_int numtries, bound;
613
614         if (flags & D_MINIMAL)
615                 bound = UINT_MAX;
616         else {
617                 sq = isqrt(n);
618                 bound = MAX(256, sq);
619         }
620
621         k = 0;
622         c[0] = newcand(0, 0, 0);
623         for (i = 1; i <= n; i++) {
624                 j = a[i];
625                 if (j == 0)
626                         continue;
627                 y = -b[j];
628                 oldl = 0;
629                 oldc = c[0];
630                 numtries = 0;
631                 do {
632                         if (y <= clist[oldc].y)
633                                 continue;
634                         l = search(c, k, y);
635                         if (l != oldl + 1)
636                                 oldc = c[l - 1];
637                         if (l <= k) {
638                                 if (clist[c[l]].y <= y)
639                                         continue;
640                                 tc = c[l];
641                                 c[l] = newcand(i, y, oldc);
642                                 oldc = tc;
643                                 oldl = l;
644                                 numtries++;
645                         } else {
646                                 c[l] = newcand(i, y, oldc);
647                                 k++;
648                                 break;
649                         }
650                 } while ((y = b[++j]) > 0 && numtries < bound);
651         }
652         return (k);
653 }
654
655 static int
656 newcand(int x, int y, int pred)
657 {
658         struct cand *q;
659
660         if (clen == clistlen) {
661                 clistlen = clistlen * 11 / 10;
662                 clist = xreallocarray(clist, clistlen, sizeof(*clist));
663         }
664         q = clist + clen;
665         q->x = x;
666         q->y = y;
667         q->pred = pred;
668         return (clen++);
669 }
670
671 static int
672 search(int *c, int k, int y)
673 {
674         int i, j, l, t;
675
676         if (clist[c[k]].y < y)  /* quick look for typical case */
677                 return (k + 1);
678         i = 0;
679         j = k + 1;
680         for (;;) {
681                 l = (i + j) / 2;
682                 if (l <= i)
683                         break;
684                 t = clist[c[l]].y;
685                 if (t > y)
686                         j = l;
687                 else if (t < y)
688                         i = l;
689                 else
690                         return (l);
691         }
692         return (l + 1);
693 }
694
695 static void
696 unravel(int p)
697 {
698         struct cand *q;
699         int i;
700
701         for (i = 0; i <= len[0]; i++)
702                 J[i] = i <= pref ? i :
703                     i > len[0] - suff ? i + len[1] - len[0] : 0;
704         for (q = clist + p; q->y != 0; q = clist + q->pred)
705                 J[q->x + pref] = q->y + pref;
706 }
707
708 /*
709  * Check does double duty:
710  *  1.  ferret out any fortuitous correspondences due
711  *      to confounding by hashing (which result in "jackpot")
712  *  2.  collect random access indexes to the two files
713  */
714 static void
715 check(FILE *f1, FILE *f2, int flags)
716 {
717         int i, j, jackpot, c, d;
718         long ctold, ctnew;
719
720         rewind(f1);
721         rewind(f2);
722         j = 1;
723         ixold[0] = ixnew[0] = 0;
724         jackpot = 0;
725         ctold = ctnew = 0;
726         for (i = 1; i <= len[0]; i++) {
727                 if (J[i] == 0) {
728                         ixold[i] = ctold += skipline(f1);
729                         continue;
730                 }
731                 while (j < J[i]) {
732                         ixnew[j] = ctnew += skipline(f2);
733                         j++;
734                 }
735                 if (flags & (D_FOLDBLANKS|D_IGNOREBLANKS|D_IGNORECASE|D_STRIPCR)) {
736                         for (;;) {
737                                 c = getc(f1);
738                                 d = getc(f2);
739                                 /*
740                                  * GNU diff ignores a missing newline
741                                  * in one file for -b or -w.
742                                  */
743                                 if (flags & (D_FOLDBLANKS|D_IGNOREBLANKS)) {
744                                         if (c == EOF && d == '\n') {
745                                                 ctnew++;
746                                                 break;
747                                         } else if (c == '\n' && d == EOF) {
748                                                 ctold++;
749                                                 break;
750                                         }
751                                 }
752                                 ctold++;
753                                 ctnew++;
754                                 if (flags & D_STRIPCR && (c == '\r' || d == '\r')) {
755                                         if (c == '\r') {
756                                                 if ((c = getc(f1)) == '\n') {
757                                                         ctold++;
758                                                 } else {
759                                                         ungetc(c, f1);
760                                                 }
761                                         }
762                                         if (d == '\r') {
763                                                 if ((d = getc(f2)) == '\n') {
764                                                         ctnew++;
765                                                 } else {
766                                                         ungetc(d, f2);
767                                                 }
768                                         }
769                                         break;
770                                 }
771                                 if ((flags & D_FOLDBLANKS) && isspace(c) &&
772                                     isspace(d)) {
773                                         do {
774                                                 if (c == '\n')
775                                                         break;
776                                                 ctold++;
777                                         } while (isspace(c = getc(f1)));
778                                         do {
779                                                 if (d == '\n')
780                                                         break;
781                                                 ctnew++;
782                                         } while (isspace(d = getc(f2)));
783                                 } else if ((flags & D_IGNOREBLANKS)) {
784                                         while (isspace(c) && c != '\n') {
785                                                 c = getc(f1);
786                                                 ctold++;
787                                         }
788                                         while (isspace(d) && d != '\n') {
789                                                 d = getc(f2);
790                                                 ctnew++;
791                                         }
792                                 }
793                                 if (chrtran(c) != chrtran(d)) {
794                                         jackpot++;
795                                         J[i] = 0;
796                                         if (c != '\n' && c != EOF)
797                                                 ctold += skipline(f1);
798                                         if (d != '\n' && c != EOF)
799                                                 ctnew += skipline(f2);
800                                         break;
801                                 }
802                                 if (c == '\n' || c == EOF)
803                                         break;
804                         }
805                 } else {
806                         for (;;) {
807                                 ctold++;
808                                 ctnew++;
809                                 if ((c = getc(f1)) != (d = getc(f2))) {
810                                         /* jackpot++; */
811                                         J[i] = 0;
812                                         if (c != '\n' && c != EOF)
813                                                 ctold += skipline(f1);
814                                         if (d != '\n' && c != EOF)
815                                                 ctnew += skipline(f2);
816                                         break;
817                                 }
818                                 if (c == '\n' || c == EOF)
819                                         break;
820                         }
821                 }
822                 ixold[i] = ctold;
823                 ixnew[j] = ctnew;
824                 j++;
825         }
826         for (; j <= len[1]; j++) {
827                 ixnew[j] = ctnew += skipline(f2);
828         }
829         /*
830          * if (jackpot)
831          *      fprintf(stderr, "jackpot\n");
832          */
833 }
834
835 /* shellsort CACM #201 */
836 static void
837 sort(struct line *a, int n)
838 {
839         struct line *ai, *aim, w;
840         int j, m = 0, k;
841
842         if (n == 0)
843                 return;
844         for (j = 1; j <= n; j *= 2)
845                 m = 2 * j - 1;
846         for (m /= 2; m != 0; m /= 2) {
847                 k = n - m;
848                 for (j = 1; j <= k; j++) {
849                         for (ai = &a[j]; ai > a; ai -= m) {
850                                 aim = &ai[m];
851                                 if (aim < ai)
852                                         break;  /* wraparound */
853                                 if (aim->value > ai[0].value ||
854                                     (aim->value == ai[0].value &&
855                                         aim->serial > ai[0].serial))
856                                         break;
857                                 w.value = ai[0].value;
858                                 ai[0].value = aim->value;
859                                 aim->value = w.value;
860                                 w.serial = ai[0].serial;
861                                 ai[0].serial = aim->serial;
862                                 aim->serial = w.serial;
863                         }
864                 }
865         }
866 }
867
868 static void
869 unsort(struct line *f, int l, int *b)
870 {
871         int *a, i;
872
873         a = xcalloc(l + 1, sizeof(*a));
874         for (i = 1; i <= l; i++)
875                 a[f[i].serial] = f[i].value;
876         for (i = 1; i <= l; i++)
877                 b[i] = a[i];
878         free(a);
879 }
880
881 static int
882 skipline(FILE *f)
883 {
884         int i, c;
885
886         for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
887                 continue;
888         return (i);
889 }
890
891 static void
892 output(char *file1, FILE *f1, char *file2, FILE *f2, int flags)
893 {
894         int i, j, m, i0, i1, j0, j1, nc;
895
896         rewind(f1);
897         rewind(f2);
898         m = len[0];
899         J[0] = 0;
900         J[m + 1] = len[1] + 1;
901         if (diff_format != D_EDIT) {
902                 for (i0 = 1; i0 <= m; i0 = i1 + 1) {
903                         while (i0 <= m && J[i0] == J[i0 - 1] + 1){
904                                 if (diff_format == D_SIDEBYSIDE &&
905                                     suppress_common != 1) {
906                                         nc = fetch(ixold, i0, i0, f1, '\0',
907                                             1, flags);
908                                         print_space(nc,
909                                             (hw - nc) + (padding << 1) + 1,
910                                             flags);
911                                         fetch(ixnew, J[i0], J[i0], f2, '\0',
912                                             0, flags);
913                                         printf("\n");
914                                 }
915                                 i0++;
916                         }
917                         j0 = J[i0 - 1] + 1;
918                         i1 = i0 - 1;
919                         while (i1 < m && J[i1 + 1] == 0)
920                                 i1++;
921                         j1 = J[i1 + 1] - 1;
922                         J[i1] = j1;
923
924                         /*
925                          * When using side-by-side, lines from both of the
926                          * files are printed. The algorithm used by diff(1)
927                          * identifies the ranges in which two files differ.
928                          * See the change() function below.
929                          * The for loop below consumes the shorter range,
930                          * whereas one of the while loops deals with the
931                          * longer one.
932                          */
933                         if (diff_format == D_SIDEBYSIDE) {
934                                 for (i=i0, j=j0; i<=i1 && j<=j1; i++, j++)
935                                         change(file1, f1, file2, f2, i, i,
936                                             j, j, &flags);
937
938                                 while (i <= i1) {
939                                         change(file1, f1, file2, f2,
940                                             i, i, j+1, j, &flags);
941                                         i++;
942                                 }
943
944                                 while (j <= j1) {
945                                         change(file1, f1, file2, f2,
946                                             i+1, i, j, j, &flags);
947                                         j++;
948                                 }
949                         } else
950                                 change(file1, f1, file2, f2, i0, i1, j0,
951                                     j1, &flags);
952                 }
953         } else {
954                 for (i0 = m; i0 >= 1; i0 = i1 - 1) {
955                         while (i0 >= 1 && J[i0] == J[i0 + 1] - 1 && J[i0] != 0)
956                                 i0--;
957                         j0 = J[i0 + 1] - 1;
958                         i1 = i0 + 1;
959                         while (i1 > 1 && J[i1 - 1] == 0)
960                                 i1--;
961                         j1 = J[i1 - 1] + 1;
962                         J[i1] = j1;
963                         change(file1, f1, file2, f2, i1, i0, j1, j0, &flags);
964                 }
965         }
966         if (m == 0)
967                 change(file1, f1, file2, f2, 1, 0, 1, len[1], &flags);
968         if (diff_format == D_IFDEF || diff_format == D_GFORMAT) {
969                 for (;;) {
970 #define c i0
971                         if ((c = getc(f1)) == EOF)
972                                 return;
973                         printf("%c", c);
974                 }
975 #undef c
976         }
977         if (anychange != 0) {
978                 if (diff_format == D_CONTEXT)
979                         dump_context_vec(f1, f2, flags);
980                 else if (diff_format == D_UNIFIED)
981                         dump_unified_vec(f1, f2, flags);
982         }
983 }
984
985 static void
986 range(int a, int b, const char *separator)
987 {
988         printf("%d", a > b ? b : a);
989         if (a < b)
990                 printf("%s%d", separator, b);
991 }
992
993 static void
994 uni_range(int a, int b)
995 {
996         if (a < b)
997                 printf("%d,%d", a, b - a + 1);
998         else if (a == b)
999                 printf("%d", b);
1000         else
1001                 printf("%d,0", b);
1002 }
1003
1004 static char *
1005 preadline(int fd, size_t rlen, off_t off)
1006 {
1007         char *line;
1008         ssize_t nr;
1009
1010         line = xmalloc(rlen + 1);
1011         if ((nr = pread(fd, line, rlen, off)) == -1)
1012                 err(2, "preadline");
1013         if (nr > 0 && line[nr-1] == '\n')
1014                 nr--;
1015         line[nr] = '\0';
1016         return (line);
1017 }
1018
1019 static bool
1020 ignoreline_pattern(char *line)
1021 {
1022         int ret;
1023
1024         ret = regexec(&ignore_re, line, 0, NULL, 0);
1025         free(line);
1026         return (ret == 0);      /* if it matched, it should be ignored. */
1027 }
1028
1029 static bool
1030 ignoreline(char *line, bool skip_blanks)
1031 {
1032
1033         if (ignore_pats != NULL && skip_blanks)
1034                 return (ignoreline_pattern(line) || *line == '\0');
1035         if (ignore_pats != NULL)
1036                 return (ignoreline_pattern(line));
1037         if (skip_blanks)
1038                 return (*line == '\0');
1039         /* No ignore criteria specified */
1040         return (false);
1041 }
1042
1043 /*
1044  * Indicate that there is a difference between lines a and b of the from file
1045  * to get to lines c to d of the to file.  If a is greater then b then there
1046  * are no lines in the from file involved and this means that there were
1047  * lines appended (beginning at b).  If c is greater than d then there are
1048  * lines missing from the to file.
1049  */
1050 static void
1051 change(char *file1, FILE *f1, char *file2, FILE *f2, int a, int b, int c, int d,
1052     int *pflags)
1053 {
1054         static size_t max_context = 64;
1055         long curpos;
1056         int i, nc;
1057         const char *walk;
1058         bool skip_blanks;
1059
1060         skip_blanks = (*pflags & D_SKIPBLANKLINES);
1061 restart:
1062         if ((diff_format != D_IFDEF || diff_format == D_GFORMAT) &&
1063             a > b && c > d)
1064                 return;
1065         if (ignore_pats != NULL || skip_blanks) {
1066                 char *line;
1067                 /*
1068                  * All lines in the change, insert, or delete must
1069                  * match an ignore pattern for the change to be
1070                  * ignored.
1071                  */
1072                 if (a <= b) {           /* Changes and deletes. */
1073                         for (i = a; i <= b; i++) {
1074                                 line = preadline(fileno(f1),
1075                                     ixold[i] - ixold[i - 1], ixold[i - 1]);
1076                                 if (!ignoreline(line, skip_blanks))
1077                                         goto proceed;
1078                         }
1079                 }
1080                 if (a > b || c <= d) {  /* Changes and inserts. */
1081                         for (i = c; i <= d; i++) {
1082                                 line = preadline(fileno(f2),
1083                                     ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
1084                                 if (!ignoreline(line, skip_blanks))
1085                                         goto proceed;
1086                         }
1087                 }
1088                 return;
1089         }
1090 proceed:
1091         if (*pflags & D_HEADER && diff_format != D_BRIEF) {
1092                 printf("%s %s %s\n", diffargs, file1, file2);
1093                 *pflags &= ~D_HEADER;
1094         }
1095         if (diff_format == D_CONTEXT || diff_format == D_UNIFIED) {
1096                 /*
1097                  * Allocate change records as needed.
1098                  */
1099                 if (context_vec_ptr == context_vec_end - 1) {
1100                         ptrdiff_t offset = context_vec_ptr - context_vec_start;
1101                         max_context <<= 1;
1102                         context_vec_start = xreallocarray(context_vec_start,
1103                             max_context, sizeof(*context_vec_start));
1104                         context_vec_end = context_vec_start + max_context;
1105                         context_vec_ptr = context_vec_start + offset;
1106                 }
1107                 if (anychange == 0) {
1108                         /*
1109                          * Print the context/unidiff header first time through.
1110                          */
1111                         print_header(file1, file2);
1112                         anychange = 1;
1113                 } else if (a > context_vec_ptr->b + (2 * diff_context) + 1 &&
1114                     c > context_vec_ptr->d + (2 * diff_context) + 1) {
1115                         /*
1116                          * If this change is more than 'diff_context' lines from the
1117                          * previous change, dump the record and reset it.
1118                          */
1119                         if (diff_format == D_CONTEXT)
1120                                 dump_context_vec(f1, f2, *pflags);
1121                         else
1122                                 dump_unified_vec(f1, f2, *pflags);
1123                 }
1124                 context_vec_ptr++;
1125                 context_vec_ptr->a = a;
1126                 context_vec_ptr->b = b;
1127                 context_vec_ptr->c = c;
1128                 context_vec_ptr->d = d;
1129                 return;
1130         }
1131         if (anychange == 0)
1132                 anychange = 1;
1133         switch (diff_format) {
1134         case D_BRIEF:
1135                 return;
1136         case D_NORMAL:
1137         case D_EDIT:
1138                 range(a, b, ",");
1139                 printf("%c", a > b ? 'a' : c > d ? 'd' : 'c');
1140                 if (diff_format == D_NORMAL)
1141                         range(c, d, ",");
1142                 printf("\n");
1143                 break;
1144         case D_REVERSE:
1145                 printf("%c", a > b ? 'a' : c > d ? 'd' : 'c');
1146                 range(a, b, " ");
1147                 printf("\n");
1148                 break;
1149         case D_NREVERSE:
1150                 if (a > b)
1151                         printf("a%d %d\n", b, d - c + 1);
1152                 else {
1153                         printf("d%d %d\n", a, b - a + 1);
1154                         if (!(c > d))
1155                                 /* add changed lines */
1156                                 printf("a%d %d\n", b, d - c + 1);
1157                 }
1158                 break;
1159         }
1160         if (diff_format == D_GFORMAT) {
1161                 curpos = ftell(f1);
1162                 /* print through if append (a>b), else to (nb: 0 vs 1 orig) */
1163                 nc = ixold[a > b ? b : a - 1] - curpos;
1164                 for (i = 0; i < nc; i++)
1165                         printf("%c", getc(f1));
1166                 for (walk = group_format; *walk != '\0'; walk++) {
1167                         if (*walk == '%') {
1168                                 walk++;
1169                                 switch (*walk) {
1170                                 case '<':
1171                                         fetch(ixold, a, b, f1, '<', 1, *pflags);
1172                                         break;
1173                                 case '>':
1174                                         fetch(ixnew, c, d, f2, '>', 0, *pflags);
1175                                         break;
1176                                 default:
1177                                         printf("%%%c", *walk);
1178                                         break;
1179                                 }
1180                                 continue;
1181                         }
1182                         printf("%c", *walk);
1183                 }
1184         }
1185         if (diff_format == D_SIDEBYSIDE) {
1186                 if (a > b) {
1187                         print_space(0, hw + padding , *pflags);
1188                 } else {
1189                         nc = fetch(ixold, a, b, f1, '\0', 1, *pflags);
1190                         print_space(nc, hw - nc + padding, *pflags);
1191                 }
1192                 printf("%c", (a>b)? '>' : ((c>d)? '<' : '|'));
1193                 print_space(hw + padding + 1 , padding, *pflags);
1194                 fetch(ixnew, c, d, f2, '\0', 0, *pflags);
1195                 printf("\n");
1196         }
1197         if (diff_format == D_NORMAL || diff_format == D_IFDEF) {
1198                 fetch(ixold, a, b, f1, '<', 1, *pflags);
1199                 if (a <= b && c <= d && diff_format == D_NORMAL)
1200                         printf("---\n");
1201         }
1202         if (diff_format != D_GFORMAT && diff_format != D_SIDEBYSIDE)
1203                 fetch(ixnew, c, d, f2, diff_format == D_NORMAL ? '>' : '\0', 0, *pflags);
1204         if (edoffset != 0 && diff_format == D_EDIT) {
1205                 /*
1206                  * A non-zero edoffset value for D_EDIT indicates that the
1207                  * last line printed was a bare dot (".") that has been
1208                  * escaped as ".." to prevent ed(1) from misinterpreting
1209                  * it.  We have to add a substitute command to change this
1210                  * back and restart where we left off.
1211                  */
1212                 printf(".\n");
1213                 printf("%ds/.//\n", a + edoffset - 1);
1214                 b = a + edoffset - 1;
1215                 a = b + 1;
1216                 c += edoffset;
1217                 goto restart;
1218         }
1219         if ((diff_format == D_EDIT || diff_format == D_REVERSE) && c <= d)
1220                 printf(".\n");
1221         if (inifdef) {
1222                 printf("#endif /* %s */\n", ifdefname);
1223                 inifdef = 0;
1224         }
1225 }
1226
1227 static int
1228 fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile, int flags)
1229 {
1230         int i, j, c, lastc, col, nc, newcol;
1231
1232         edoffset = 0;
1233         nc = 0;
1234         /*
1235          * When doing #ifdef's, copy down to current line
1236          * if this is the first file, so that stuff makes it to output.
1237          */
1238         if ((diff_format == D_IFDEF) && oldfile) {
1239                 long curpos = ftell(lb);
1240                 /* print through if append (a>b), else to (nb: 0 vs 1 orig) */
1241                 nc = f[a > b ? b : a - 1] - curpos;
1242                 for (i = 0; i < nc; i++)
1243                         printf("%c", getc(lb));
1244         }
1245         if (a > b)
1246                 return (0);
1247         if (diff_format == D_IFDEF) {
1248                 if (inifdef) {
1249                         printf("#else /* %s%s */\n",
1250                             oldfile == 1 ? "!" : "", ifdefname);
1251                 } else {
1252                         if (oldfile)
1253                                 printf("#ifndef %s\n", ifdefname);
1254                         else
1255                                 printf("#ifdef %s\n", ifdefname);
1256                 }
1257                 inifdef = 1 + oldfile;
1258         }
1259         for (i = a; i <= b; i++) {
1260                 fseek(lb, f[i - 1], SEEK_SET);
1261                 nc = (f[i] - f[i - 1]);
1262                 if (diff_format == D_SIDEBYSIDE && hw < nc)
1263                         nc = hw;
1264                 if ((diff_format != D_IFDEF && diff_format != D_GFORMAT) &&
1265                     ch != '\0') {
1266                         printf("%c", ch);
1267                         if (Tflag && (diff_format == D_NORMAL ||
1268                             diff_format == D_CONTEXT ||
1269                             diff_format == D_UNIFIED))
1270                                 printf("\t");
1271                         else if (diff_format != D_UNIFIED)
1272                                 printf(" ");
1273                 }
1274                 col = 0;
1275                 for (j = 0, lastc = '\0'; j < nc; j++, lastc = c) {
1276                         if ((c = getc(lb)) == EOF) {
1277                                 if (diff_format == D_EDIT ||
1278                                     diff_format == D_REVERSE ||
1279                                     diff_format == D_NREVERSE)
1280                                         warnx("No newline at end of file");
1281                                 else
1282                                         printf("\n\\ No newline at end of "
1283                                             "file\n");
1284                                 return col;
1285                         }
1286                         /*
1287                          * when using --side-by-side, col needs to be increased
1288                          * in any case to keep the columns aligned
1289                          */
1290                         if (c == '\t') {
1291                                 if (flags & D_EXPANDTABS) {
1292                                         newcol = ((col/tabsize)+1)*tabsize;
1293                                         do {    
1294                                                 if (diff_format == D_SIDEBYSIDE)
1295                                                         j++;
1296                                                 printf(" ");
1297                                         } while (++col < newcol && j < nc);
1298                                 } else {
1299                                         if (diff_format == D_SIDEBYSIDE) {
1300                                                 if ((j + tabsize) > nc) {
1301                                                         printf("%*s",
1302                                                         nc - j,"");
1303                                                         j = col = nc;
1304                                                 } else {
1305                                                         printf("\t");
1306                                                         col += tabsize - 1;
1307                                                         j += tabsize - 1;
1308                                                 }
1309                                         } else {
1310                                                 printf("\t");
1311                                                 col++;
1312                                         }
1313                                 }
1314                         } else {
1315                                 if (diff_format == D_EDIT && j == 1 && c == '\n'
1316                                     && lastc == '.') {
1317                                         /*
1318                                          * Don't print a bare "." line
1319                                          * since that will confuse ed(1).
1320                                          * Print ".." instead and set the,
1321                                          * global variable edoffset to an
1322                                          * offset from which to restart.
1323                                          * The caller must check the value
1324                                          * of edoffset
1325                                          */
1326                                         printf(".\n");
1327                                         edoffset = i - a + 1;
1328                                         return edoffset;
1329                                 }
1330                                 /* when side-by-side, do not print a newline */
1331                                 if (diff_format != D_SIDEBYSIDE || c != '\n') {
1332                                         printf("%c", c);
1333                                         col++;
1334                                 }
1335                         }
1336                 }
1337         }
1338         return col;
1339 }
1340
1341 /*
1342  * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
1343  */
1344 static int
1345 readhash(FILE *f, int flags)
1346 {
1347         int i, t, space;
1348         int sum;
1349
1350         sum = 1;
1351         space = 0;
1352         for (i = 0;;) {
1353                 switch (t = getc(f)) {
1354                 case '\r':
1355                         if (flags & D_STRIPCR) {
1356                                 t = getc(f);
1357                                 if (t == '\n')
1358                                         break;
1359                                 ungetc(t, f);
1360                         }
1361                         /* FALLTHROUGH */
1362                 case '\t':
1363                 case '\v':
1364                 case '\f':
1365                 case ' ':
1366                         if ((flags & (D_FOLDBLANKS|D_IGNOREBLANKS)) != 0) {
1367                                 space++;
1368                                 continue;
1369                         }
1370                         /* FALLTHROUGH */
1371                 default:
1372                         if (space && (flags & D_IGNOREBLANKS) == 0) {
1373                                 i++;
1374                                 space = 0;
1375                         }
1376                         sum = sum * 127 + chrtran(t);
1377                         i++;
1378                         continue;
1379                 case EOF:
1380                         if (i == 0)
1381                                 return (0);
1382                         /* FALLTHROUGH */
1383                 case '\n':
1384                         break;
1385                 }
1386                 break;
1387         }
1388         /*
1389          * There is a remote possibility that we end up with a zero sum.
1390          * Zero is used as an EOF marker, so return 1 instead.
1391          */
1392         return (sum == 0 ? 1 : sum);
1393 }
1394
1395 static int
1396 asciifile(FILE *f)
1397 {
1398         unsigned char buf[BUFSIZ];
1399         size_t cnt;
1400
1401         if (f == NULL)
1402                 return (1);
1403
1404         rewind(f);
1405         cnt = fread(buf, 1, sizeof(buf), f);
1406         return (memchr(buf, '\0', cnt) == NULL);
1407 }
1408
1409 #define begins_with(s, pre) (strncmp(s, pre, sizeof(pre)-1) == 0)
1410
1411 static char *
1412 match_function(const long *f, int pos, FILE *fp)
1413 {
1414         unsigned char buf[FUNCTION_CONTEXT_SIZE];
1415         size_t nc;
1416         int last = lastline;
1417         const char *state = NULL;
1418
1419         lastline = pos;
1420         while (pos > last) {
1421                 fseek(fp, f[pos - 1], SEEK_SET);
1422                 nc = f[pos] - f[pos - 1];
1423                 if (nc >= sizeof(buf))
1424                         nc = sizeof(buf) - 1;
1425                 nc = fread(buf, 1, nc, fp);
1426                 if (nc > 0) {
1427                         buf[nc] = '\0';
1428                         buf[strcspn(buf, "\n")] = '\0';
1429                         if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') {
1430                                 if (begins_with(buf, "private:")) {
1431                                         if (!state)
1432                                                 state = " (private)";
1433                                 } else if (begins_with(buf, "protected:")) {
1434                                         if (!state)
1435                                                 state = " (protected)";
1436                                 } else if (begins_with(buf, "public:")) {
1437                                         if (!state)
1438                                                 state = " (public)";
1439                                 } else {
1440                                         strlcpy(lastbuf, buf, sizeof lastbuf);
1441                                         if (state)
1442                                                 strlcat(lastbuf, state,
1443                                                     sizeof lastbuf);
1444                                         lastmatchline = pos;
1445                                         return lastbuf;
1446                                 }
1447                         }
1448                 }
1449                 pos--;
1450         }
1451         return lastmatchline > 0 ? lastbuf : NULL;
1452 }
1453
1454 /* dump accumulated "context" diff changes */
1455 static void
1456 dump_context_vec(FILE *f1, FILE *f2, int flags)
1457 {
1458         struct context_vec *cvp = context_vec_start;
1459         int lowa, upb, lowc, upd, do_output;
1460         int a, b, c, d;
1461         char ch, *f;
1462
1463         if (context_vec_start > context_vec_ptr)
1464                 return;
1465
1466         b = d = 0;              /* gcc */
1467         lowa = MAX(1, cvp->a - diff_context);
1468         upb = MIN(len[0], context_vec_ptr->b + diff_context);
1469         lowc = MAX(1, cvp->c - diff_context);
1470         upd = MIN(len[1], context_vec_ptr->d + diff_context);
1471
1472         printf("***************");
1473         if ((flags & D_PROTOTYPE)) {
1474                 f = match_function(ixold, lowa-1, f1);
1475                 if (f != NULL)
1476                         printf(" %s", f);
1477         }
1478         printf("\n*** ");
1479         range(lowa, upb, ",");
1480         printf(" ****\n");
1481
1482         /*
1483          * Output changes to the "old" file.  The first loop suppresses
1484          * output if there were no changes to the "old" file (we'll see
1485          * the "old" lines as context in the "new" list).
1486          */
1487         do_output = 0;
1488         for (; cvp <= context_vec_ptr; cvp++)
1489                 if (cvp->a <= cvp->b) {
1490                         cvp = context_vec_start;
1491                         do_output++;
1492                         break;
1493                 }
1494         if (do_output) {
1495                 while (cvp <= context_vec_ptr) {
1496                         a = cvp->a;
1497                         b = cvp->b;
1498                         c = cvp->c;
1499                         d = cvp->d;
1500
1501                         if (a <= b && c <= d)
1502                                 ch = 'c';
1503                         else
1504                                 ch = (a <= b) ? 'd' : 'a';
1505
1506                         if (ch == 'a')
1507                                 fetch(ixold, lowa, b, f1, ' ', 0, flags);
1508                         else {
1509                                 fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
1510                                 fetch(ixold, a, b, f1,
1511                                     ch == 'c' ? '!' : '-', 0, flags);
1512                         }
1513                         lowa = b + 1;
1514                         cvp++;
1515                 }
1516                 fetch(ixold, b + 1, upb, f1, ' ', 0, flags);
1517         }
1518         /* output changes to the "new" file */
1519         printf("--- ");
1520         range(lowc, upd, ",");
1521         printf(" ----\n");
1522
1523         do_output = 0;
1524         for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
1525                 if (cvp->c <= cvp->d) {
1526                         cvp = context_vec_start;
1527                         do_output++;
1528                         break;
1529                 }
1530         if (do_output) {
1531                 while (cvp <= context_vec_ptr) {
1532                         a = cvp->a;
1533                         b = cvp->b;
1534                         c = cvp->c;
1535                         d = cvp->d;
1536
1537                         if (a <= b && c <= d)
1538                                 ch = 'c';
1539                         else
1540                                 ch = (a <= b) ? 'd' : 'a';
1541
1542                         if (ch == 'd')
1543                                 fetch(ixnew, lowc, d, f2, ' ', 0, flags);
1544                         else {
1545                                 fetch(ixnew, lowc, c - 1, f2, ' ', 0, flags);
1546                                 fetch(ixnew, c, d, f2,
1547                                     ch == 'c' ? '!' : '+', 0, flags);
1548                         }
1549                         lowc = d + 1;
1550                         cvp++;
1551                 }
1552                 fetch(ixnew, d + 1, upd, f2, ' ', 0, flags);
1553         }
1554         context_vec_ptr = context_vec_start - 1;
1555 }
1556
1557 /* dump accumulated "unified" diff changes */
1558 static void
1559 dump_unified_vec(FILE *f1, FILE *f2, int flags)
1560 {
1561         struct context_vec *cvp = context_vec_start;
1562         int lowa, upb, lowc, upd;
1563         int a, b, c, d;
1564         char ch, *f;
1565
1566         if (context_vec_start > context_vec_ptr)
1567                 return;
1568
1569         b = d = 0;              /* gcc */
1570         lowa = MAX(1, cvp->a - diff_context);
1571         upb = MIN(len[0], context_vec_ptr->b + diff_context);
1572         lowc = MAX(1, cvp->c - diff_context);
1573         upd = MIN(len[1], context_vec_ptr->d + diff_context);
1574
1575         printf("@@ -");
1576         uni_range(lowa, upb);
1577         printf(" +");
1578         uni_range(lowc, upd);
1579         printf(" @@");
1580         if ((flags & D_PROTOTYPE)) {
1581                 f = match_function(ixold, lowa-1, f1);
1582                 if (f != NULL)
1583                         printf(" %s", f);
1584         }
1585         printf("\n");
1586
1587         /*
1588          * Output changes in "unified" diff format--the old and new lines
1589          * are printed together.
1590          */
1591         for (; cvp <= context_vec_ptr; cvp++) {
1592                 a = cvp->a;
1593                 b = cvp->b;
1594                 c = cvp->c;
1595                 d = cvp->d;
1596
1597                 /*
1598                  * c: both new and old changes
1599                  * d: only changes in the old file
1600                  * a: only changes in the new file
1601                  */
1602                 if (a <= b && c <= d)
1603                         ch = 'c';
1604                 else
1605                         ch = (a <= b) ? 'd' : 'a';
1606
1607                 switch (ch) {
1608                 case 'c':
1609                         fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
1610                         fetch(ixold, a, b, f1, '-', 0, flags);
1611                         fetch(ixnew, c, d, f2, '+', 0, flags);
1612                         break;
1613                 case 'd':
1614                         fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
1615                         fetch(ixold, a, b, f1, '-', 0, flags);
1616                         break;
1617                 case 'a':
1618                         fetch(ixnew, lowc, c - 1, f2, ' ', 0, flags);
1619                         fetch(ixnew, c, d, f2, '+', 0, flags);
1620                         break;
1621                 }
1622                 lowa = b + 1;
1623                 lowc = d + 1;
1624         }
1625         fetch(ixnew, d + 1, upd, f2, ' ', 0, flags);
1626
1627         context_vec_ptr = context_vec_start - 1;
1628 }
1629
1630 static void
1631 print_header(const char *file1, const char *file2)
1632 {
1633         const char *time_format;
1634         char buf1[256];
1635         char buf2[256];
1636         char end1[10];
1637         char end2[10];
1638         struct tm tm1, tm2, *tm_ptr1, *tm_ptr2;
1639         int nsec1 = stb1.st_mtim.tv_nsec;
1640         int nsec2 = stb2.st_mtim.tv_nsec;
1641
1642         time_format = "%Y-%m-%d %H:%M:%S";
1643
1644         if (cflag)
1645                 time_format = "%c";
1646         tm_ptr1 = localtime_r(&stb1.st_mtime, &tm1);
1647         tm_ptr2 = localtime_r(&stb2.st_mtime, &tm2);
1648         strftime(buf1, 256, time_format, tm_ptr1);
1649         strftime(buf2, 256, time_format, tm_ptr2);
1650         if (!cflag) {
1651                 strftime(end1, 10, "%z", tm_ptr1);
1652                 strftime(end2, 10, "%z", tm_ptr2);
1653                 sprintf(buf1, "%s.%.9d %s", buf1, nsec1, end1);
1654                 sprintf(buf2, "%s.%.9d %s", buf2, nsec2, end2);
1655         }
1656         if (label[0] != NULL)
1657                 printf("%s %s\n", diff_format == D_CONTEXT ? "***" : "---",
1658                     label[0]);
1659         else
1660                 printf("%s %s\t%s\n", diff_format == D_CONTEXT ? "***" : "---",
1661                     file1, buf1);
1662         if (label[1] != NULL)
1663                 printf("%s %s\n", diff_format == D_CONTEXT ? "---" : "+++",
1664                     label[1]);
1665         else
1666                 printf("%s %s\t%s\n", diff_format == D_CONTEXT ? "---" : "+++",
1667                     file2, buf2);
1668 }
1669
1670 /*
1671  * Prints n number of space characters either by using tab
1672  * or single space characters.
1673  * nc is the preceding number of characters
1674  */
1675 static void
1676 print_space(int nc, int n, int flags) {
1677         int i, col;
1678
1679         col = n;
1680         if ((flags & D_EXPANDTABS) == 0) {
1681                 /* first tabstop may be closer than tabsize */
1682                 i = tabsize - (nc % tabsize);
1683                 while (col >= tabsize) {
1684                         printf("\t");
1685                         col -= i;
1686                         i = tabsize;
1687                 }
1688         }
1689         printf("%*s", col, "");
1690 }