[BACK]Return to diff.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / cvs

Annotation of src/usr.bin/cvs/diff.c, Revision 1.1

1.1     ! jfb         1: /*     $OpenBSD$       */
        !             2: /*
        !             3:  * Copyright (C) Caldera International Inc.  2001-2002.
        !             4:  * All rights reserved.
        !             5:  *
        !             6:  * Redistribution and use in source and binary forms, with or without
        !             7:  * modification, are permitted provided that the following conditions
        !             8:  * are met:
        !             9:  * 1. Redistributions of source code and documentation must retain the above
        !            10:  *    copyright notice, this list of conditions and the following disclaimer.
        !            11:  * 2. Redistributions in binary form must reproduce the above copyright
        !            12:  *    notice, this list of conditions and the following disclaimer in the
        !            13:  *    documentation and/or other materials provided with the distribution.
        !            14:  * 3. All advertising materials mentioning features or use of this software
        !            15:  *    must display the following acknowledgement:
        !            16:  *     This product includes software developed or owned by Caldera
        !            17:  *     International, Inc.
        !            18:  * 4. Neither the name of Caldera International, Inc. nor the names of other
        !            19:  *    contributors may be used to endorse or promote products derived from
        !            20:  *    this software without specific prior written permission.
        !            21:  *
        !            22:  * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
        !            23:  * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
        !            24:  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
        !            25:  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
        !            26:  * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
        !            27:  * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
        !            28:  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
        !            29:  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
        !            30:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
        !            31:  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
        !            32:  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
        !            33:  * POSSIBILITY OF SUCH DAMAGE.
        !            34:  */
        !            35: /*-
        !            36:  * Copyright (c) 1991, 1993
        !            37:  *     The Regents of the University of California.  All rights reserved.
        !            38:  * Copyright (c) 2004 Jean-Francois Brousseau.  All rights reserved.
        !            39:  *
        !            40:  * Redistribution and use in source and binary forms, with or without
        !            41:  * modification, are permitted provided that the following conditions
        !            42:  * are met:
        !            43:  * 1. Redistributions of source code must retain the above copyright
        !            44:  *    notice, this list of conditions and the following disclaimer.
        !            45:  * 2. Redistributions in binary form must reproduce the above copyright
        !            46:  *    notice, this list of conditions and the following disclaimer in the
        !            47:  *    documentation and/or other materials provided with the distribution.
        !            48:  * 3. Neither the name of the University nor the names of its contributors
        !            49:  *    may be used to endorse or promote products derived from this software
        !            50:  *    without specific prior written permission.
        !            51:  *
        !            52:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
        !            53:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
        !            54:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
        !            55:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
        !            56:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
        !            57:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
        !            58:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
        !            59:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
        !            60:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
        !            61:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
        !            62:  * SUCH DAMAGE.
        !            63:  *
        !            64:  *     @(#)diffreg.c   8.1 (Berkeley) 6/6/93
        !            65:  */
        !            66: /*
        !            67:  *     Uses an algorithm due to Harold Stone, which finds
        !            68:  *     a pair of longest identical subsequences in the two
        !            69:  *     files.
        !            70:  *
        !            71:  *     The major goal is to generate the match vector J.
        !            72:  *     J[i] is the index of the line in file1 corresponding
        !            73:  *     to line i file0. J[i] = 0 if there is no
        !            74:  *     such line in file1.
        !            75:  *
        !            76:  *     Lines are hashed so as to work in core. All potential
        !            77:  *     matches are located by sorting the lines of each file
        !            78:  *     on the hash (called ``value''). In particular, this
        !            79:  *     collects the equivalence classes in file1 together.
        !            80:  *     Subroutine equiv replaces the value of each line in
        !            81:  *     file0 by the index of the first element of its
        !            82:  *     matching equivalence in (the reordered) file1.
        !            83:  *     To save space equiv squeezes file1 into a single
        !            84:  *     array member in which the equivalence classes
        !            85:  *     are simply concatenated, except that their first
        !            86:  *     members are flagged by changing sign.
        !            87:  *
        !            88:  *     Next the indices that point into member are unsorted into
        !            89:  *     array class according to the original order of file0.
        !            90:  *
        !            91:  *     The cleverness lies in routine stone. This marches
        !            92:  *     through the lines of file0, developing a vector klist
        !            93:  *     of "k-candidates". At step i a k-candidate is a matched
        !            94:  *     pair of lines x,y (x in file0 y in file1) such that
        !            95:  *     there is a common subsequence of length k
        !            96:  *     between the first i lines of file0 and the first y
        !            97:  *     lines of file1, but there is no such subsequence for
        !            98:  *     any smaller y. x is the earliest possible mate to y
        !            99:  *     that occurs in such a subsequence.
        !           100:  *
        !           101:  *     Whenever any of the members of the equivalence class of
        !           102:  *     lines in file1 matable to a line in file0 has serial number
        !           103:  *     less than the y of some k-candidate, that k-candidate
        !           104:  *     with the smallest such y is replaced. The new
        !           105:  *     k-candidate is chained (via pred) to the current
        !           106:  *     k-1 candidate so that the actual subsequence can
        !           107:  *     be recovered. When a member has serial number greater
        !           108:  *     that the y of all k-candidates, the klist is extended.
        !           109:  *     At the end, the longest subsequence is pulled out
        !           110:  *     and placed in the array J by unravel
        !           111:  *
        !           112:  *     With J in hand, the matches there recorded are
        !           113:  *     check'ed against reality to assure that no spurious
        !           114:  *     matches have crept in due to hashing. If they have,
        !           115:  *     they are broken, and "jackpot" is recorded--a harmless
        !           116:  *     matter except that a true match for a spuriously
        !           117:  *     mated line may now be unnecessarily reported as a change.
        !           118:  *
        !           119:  *     Much of the complexity of the program comes simply
        !           120:  *     from trying to minimize core utilization and
        !           121:  *     maximize the range of doable problems by dynamically
        !           122:  *     allocating what is needed and reusing what is not.
        !           123:  *     The core requirements for problems larger than somewhat
        !           124:  *     are (in words) 2*length(file0) + length(file1) +
        !           125:  *     3*(number of k-candidates installed),  typically about
        !           126:  *     6n words for files of length n.
        !           127:  */
        !           128:
        !           129: #include <sys/param.h>
        !           130: #include <sys/stat.h>
        !           131: #include <sys/wait.h>
        !           132:
        !           133: #include <errno.h>
        !           134: #include <ctype.h>
        !           135: #include <stdio.h>
        !           136: #include <fcntl.h>
        !           137: #include <paths.h>
        !           138: #include <regex.h>
        !           139: #include <dirent.h>
        !           140: #include <stdlib.h>
        !           141: #include <stddef.h>
        !           142: #include <unistd.h>
        !           143: #include <string.h>
        !           144: #include <sysexits.h>
        !           145:
        !           146: #include "cvs.h"
        !           147: #include "log.h"
        !           148: #include "buf.h"
        !           149:
        !           150:
        !           151: #define CVS_DIFF_DEFCTX    3   /* default context length */
        !           152:
        !           153:
        !           154: /*
        !           155:  * Output format options
        !           156:  */
        !           157: #define        D_NORMAL        0       /* Normal output */
        !           158: #define        D_CONTEXT       1       /* Diff with context */
        !           159: #define        D_UNIFIED       2       /* Unified context diff */
        !           160: #define        D_IFDEF         3       /* Diff with merged #ifdef's */
        !           161: #define        D_BRIEF         4       /* Say if the files differ */
        !           162:
        !           163: /*
        !           164:  * Status values for print_status() and diffreg() return values
        !           165:  */
        !           166: #define        D_SAME          0       /* Files are the same */
        !           167: #define        D_DIFFER        1       /* Files are different */
        !           168: #define        D_BINARY        2       /* Binary files are different */
        !           169: #define        D_COMMON        3       /* Subdirectory common to both dirs */
        !           170: #define        D_ONLY          4       /* Only exists in one directory */
        !           171: #define        D_MISMATCH1     5       /* path1 was a dir, path2 a file */
        !           172: #define        D_MISMATCH2     6       /* path1 was a file, path2 a dir */
        !           173: #define        D_ERROR         7       /* An error occurred */
        !           174: #define        D_SKIPPED1      8       /* path1 was a special file */
        !           175: #define        D_SKIPPED2      9       /* path2 was a special file */
        !           176:
        !           177: struct cand {
        !           178:        int x;
        !           179:        int y;
        !           180:        int pred;
        !           181: } cand;
        !           182:
        !           183: struct line {
        !           184:        int serial;
        !           185:        int value;
        !           186: } *file[2];
        !           187:
        !           188: /*
        !           189:  * The following struct is used to record change information when
        !           190:  * doing a "context" or "unified" diff.  (see routine "change" to
        !           191:  * understand the highly mnemonic field names)
        !           192:  */
        !           193: struct context_vec {
        !           194:        int a;                  /* start line in old file */
        !           195:        int b;                  /* end line in old file */
        !           196:        int c;                  /* start line in new file */
        !           197:        int d;                  /* end line in new file */
        !           198: };
        !           199:
        !           200:
        !           201: struct excludes {
        !           202:        char *pattern;
        !           203:        struct excludes *next;
        !           204: };
        !           205:
        !           206:
        !           207: char   *splice(char *, char *);
        !           208: int  cvs_diffreg(const char *, const char *);
        !           209: int  cvs_diff_file  (const char *, const char *, const char *);
        !           210: int  cvs_diff_dir   (const char *, int);
        !           211: static void output(const char *, FILE *, const char *, FILE *);
        !           212: static void check(FILE *, FILE *);
        !           213: static void range(int, int, char *);
        !           214: static void uni_range(int, int);
        !           215: static void dump_context_vec(FILE *, FILE *);
        !           216: static void dump_unified_vec(FILE *, FILE *);
        !           217: static void prepare(int, FILE *, off_t);
        !           218: static void prune(void);
        !           219: static void equiv(struct line *, int, struct line *, int, int *);
        !           220: static void unravel(int);
        !           221: static void unsort(struct line *, int, int *);
        !           222: static void change(const char *, FILE *, const char *, FILE *, int, int, int, int);
        !           223: static void sort(struct line *, int);
        !           224: static int  ignoreline(char *);
        !           225: static int  asciifile(FILE *);
        !           226: static int  fetch(long *, int, int, FILE *, int, int);
        !           227: static int  newcand(int, int, int);
        !           228: static int  search(int *, int, int);
        !           229: static int  skipline(FILE *);
        !           230: static int  isqrt(int);
        !           231: static int  stone(int *, int, int *, int *);
        !           232: static int  readhash(FILE *);
        !           233: static int  files_differ(FILE *, FILE *);
        !           234: static __inline int min(int, int);
        !           235: static __inline int max(int, int);
        !           236: static char *match_function(const long *, int, FILE *);
        !           237: static char *preadline(int, size_t, off_t);
        !           238:
        !           239:
        !           240:
        !           241: extern int cvs_client;
        !           242: extern struct cvsroot *cvs_root;
        !           243:
        !           244:
        !           245:
        !           246: static int aflag, bflag, dflag, iflag, tflag, Tflag, wflag;
        !           247: static int context, status;
        !           248: static int format = D_NORMAL;
        !           249: static struct stat stb1, stb2;
        !           250: static char *ifdefname, *diffargs, *ignore_pats, *diff_file;
        !           251: regex_t ignore_re;
        !           252:
        !           253: static int  *J;                        /* will be overlaid on class */
        !           254: static int  *class;            /* will be overlaid on file[0] */
        !           255: static int  *klist;            /* will be overlaid on file[0] after class */
        !           256: static int  *member;           /* will be overlaid on file[1] */
        !           257: static int   clen;
        !           258: static int   inifdef;          /* whether or not we are in a #ifdef block */
        !           259: static int   len[2];
        !           260: static int   pref, suff;       /* length of prefix and suffix */
        !           261: static int   slen[2];
        !           262: static int   anychange;
        !           263: static long *ixnew;            /* will be overlaid on file[1] */
        !           264: static long *ixold;            /* will be overlaid on klist */
        !           265: static struct cand *clist;     /* merely a free storage pot for candidates */
        !           266: static int   clistlen;         /* the length of clist */
        !           267: static struct line *sfile[2];  /* shortened by pruning common prefix/suffix */
        !           268: static u_char *chrtran;                /* translation table for case-folding */
        !           269: static struct context_vec *context_vec_start;
        !           270: static struct context_vec *context_vec_end;
        !           271: static struct context_vec *context_vec_ptr;
        !           272:
        !           273: #define FUNCTION_CONTEXT_SIZE  41
        !           274: static char lastbuf[FUNCTION_CONTEXT_SIZE];
        !           275: static int lastline;
        !           276: static int lastmatchline;
        !           277:
        !           278:
        !           279:
        !           280:
        !           281: /*
        !           282:  * chrtran points to one of 2 translation tables: cup2low if folding upper to
        !           283:  * lower case clow2low if not folding case
        !           284:  */
        !           285: u_char clow2low[256] = {
        !           286:        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
        !           287:        0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
        !           288:        0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
        !           289:        0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
        !           290:        0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
        !           291:        0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
        !           292:        0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
        !           293:        0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
        !           294:        0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
        !           295:        0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
        !           296:        0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
        !           297:        0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
        !           298:        0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
        !           299:        0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
        !           300:        0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
        !           301:        0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
        !           302:        0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
        !           303:        0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
        !           304:        0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
        !           305:        0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
        !           306:        0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
        !           307:        0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
        !           308:        0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
        !           309:        0xfd, 0xfe, 0xff
        !           310: };
        !           311:
        !           312: u_char cup2low[256] = {
        !           313:        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
        !           314:        0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
        !           315:        0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
        !           316:        0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
        !           317:        0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
        !           318:        0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x60, 0x61,
        !           319:        0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
        !           320:        0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
        !           321:        0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x60, 0x61, 0x62,
        !           322:        0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
        !           323:        0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
        !           324:        0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
        !           325:        0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
        !           326:        0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
        !           327:        0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
        !           328:        0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
        !           329:        0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
        !           330:        0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
        !           331:        0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
        !           332:        0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
        !           333:        0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
        !           334:        0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
        !           335:        0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
        !           336:        0xfd, 0xfe, 0xff
        !           337: };
        !           338:
        !           339:
        !           340:
        !           341: /*
        !           342:  * cvs_diff()
        !           343:  *
        !           344:  * Handler for the `cvs diff' command.
        !           345:  *
        !           346:  * SYNOPSIS: cvs [args] diff [-clipu] [-D date] [-r rev]
        !           347:  */
        !           348:
        !           349: int
        !           350: cvs_diff(int argc, char **argv)
        !           351: {
        !           352:        int i, ch, recurse;
        !           353:        size_t dalen;
        !           354:        char dir[MAXPATHLEN], file[MAXPATHLEN], *d1, *d2, *r1, *r2;
        !           355:
        !           356:        context = CVS_DIFF_DEFCTX;
        !           357:
        !           358:        d1 = d2 = NULL;
        !           359:        r1 = r2 = NULL;
        !           360:        recurse = 1;
        !           361:
        !           362:        while ((ch = getopt(argc, argv, "cD:lir:u")) != -1) {
        !           363:                switch (ch) {
        !           364:                case 'c':
        !           365:                        format = D_CONTEXT;
        !           366:                        break;
        !           367:                case 'D':
        !           368:                        if (d1 == NULL && r1 == NULL)
        !           369:                                d1 = optarg;
        !           370:                        else if (d2 == NULL && r2 == NULL)
        !           371:                                d2 = optarg;
        !           372:                        else {
        !           373:                                cvs_log(LP_ERR,
        !           374:                                    "no more than two revisions/dates can "
        !           375:                                    "be specified");
        !           376:                        }
        !           377:                        break;
        !           378:                case 'l':
        !           379:                        recurse = 0;
        !           380:                        break;
        !           381:                case 'i':
        !           382:                        iflag = 1;
        !           383:                        break;
        !           384:                case 'r':
        !           385:                        if ((r1 == NULL) && (d1 == NULL))
        !           386:                                r1 = optarg;
        !           387:                        else if ((r2 == NULL) && (d2 == NULL))
        !           388:                                r2 = optarg;
        !           389:                        else {
        !           390:                                cvs_log(LP_ERR,
        !           391:                                    "no more than two revisions/dates can "
        !           392:                                    "be specified");
        !           393:                                return (EX_USAGE);
        !           394:                        }
        !           395:                        break;
        !           396:                case 'u':
        !           397:                        format = D_UNIFIED;
        !           398:                        break;
        !           399:                default:
        !           400:                        return (EX_USAGE);
        !           401:                }
        !           402:        }
        !           403:
        !           404:        argc -= optind;
        !           405:        argv += optind;
        !           406:
        !           407:        if (argc == 0) {
        !           408:                /* get the CVSROOT from current dir */
        !           409:                strlcpy(dir, ".", sizeof(dir));
        !           410:
        !           411:                cvs_root = cvsroot_get(dir);
        !           412:                if (cvs_root == NULL)
        !           413:                        return (EX_USAGE);
        !           414:
        !           415:                if (cvs_root->cr_method != CVS_METHOD_LOCAL) {
        !           416:                        cvs_client_connect();
        !           417:
        !           418:                        /* send the flags */
        !           419:                        if (format == D_CONTEXT)
        !           420:                                cvs_client_sendarg("-c", 0);
        !           421:                        else if (format == D_UNIFIED)
        !           422:                                cvs_client_sendarg("-u", 0);
        !           423:
        !           424:                        if (r1 != NULL) {
        !           425:                                cvs_client_sendarg("-r", 0);
        !           426:                                cvs_client_sendarg(r1, 1);
        !           427:                        }
        !           428:                        else if (d1 != NULL) {
        !           429:                                cvs_client_sendarg("-D", 0);
        !           430:                                cvs_client_sendarg(d1, 1);
        !           431:                        }
        !           432:                        if (r2 != NULL) {
        !           433:                                cvs_client_sendarg("-r", 0);
        !           434:                                cvs_client_sendarg(r2, 1);
        !           435:                        }
        !           436:                        else if (d2 != NULL) {
        !           437:                                cvs_client_sendarg("-D", 0);
        !           438:                                cvs_client_sendarg(d2, 1);
        !           439:                        }
        !           440:                }
        !           441:
        !           442:                cvs_diff_dir(dir, recurse);
        !           443:        }
        !           444:        else {
        !           445:                for (i = 0; i < argc; i++) {
        !           446:                        cvs_splitpath(argv[i], dir, sizeof(dir),
        !           447:                            file, sizeof(file));
        !           448:                        cvs_root = cvsroot_get(dir);
        !           449:                        if (cvs_root == NULL)
        !           450:                                return (EX_USAGE);
        !           451:
        !           452:                        if (cvs_root->cr_method != CVS_METHOD_LOCAL) {
        !           453:                                cvs_client_connect();
        !           454:
        !           455:                                if (i == 0) {
        !           456:                                        /* send the flags */
        !           457:                                        if (format == D_CONTEXT)
        !           458:                                                cvs_client_sendarg("-c", 0);
        !           459:                                        else if (format == D_UNIFIED)
        !           460:                                                cvs_client_sendarg("-u", 0);
        !           461:                                }
        !           462:                        }
        !           463:
        !           464:                        cvs_diff_file(argv[i], r1, r2);
        !           465:                }
        !           466:        }
        !           467:
        !           468:        if (cvs_root->cr_method != CVS_METHOD_LOCAL)
        !           469:                cvs_client_sendreq(CVS_REQ_DIFF, NULL, 1);
        !           470:
        !           471:        return (0);
        !           472: }
        !           473:
        !           474:
        !           475: /*
        !           476:  * cvs_diff_file()
        !           477:  *
        !           478:  * Diff a single file.
        !           479:  */
        !           480:
        !           481: int
        !           482: cvs_diff_file(const char *path, const char *rev1, const char *rev2)
        !           483: {
        !           484:        int modif;
        !           485:        char dir[MAXPATHLEN], file[MAXPATHLEN], rcspath[MAXPATHLEN];
        !           486:        char repo[MAXPATHLEN], buf[64];
        !           487:        time_t tsec;
        !           488:        BUF *b1, *b2;
        !           489:        RCSNUM *r1, *r2;
        !           490:        RCSFILE *rf;
        !           491:        CVSENTRIES *entf;
        !           492:        struct tm tmstamp;
        !           493:        struct stat fst;
        !           494:        struct cvs_ent *entp;
        !           495:
        !           496:        rf = NULL;
        !           497:        diff_file = path;
        !           498:
        !           499:        if (stat(path, &fst) == -1) {
        !           500:                cvs_log(LP_ERRNO, "cannot find %s", path);
        !           501:                return (-1);
        !           502:        }
        !           503:
        !           504:        cvs_splitpath(path, dir, sizeof(dir), file, sizeof(file));
        !           505:        cvs_readrepo(dir, repo, sizeof(repo));
        !           506:
        !           507:        entf = cvs_ent_open(dir);
        !           508:        if (entf == NULL) {
        !           509:                cvs_log(LP_ERR, "no CVS/Entries file in `%s'", dir);
        !           510:                return (-1);
        !           511:        }
        !           512:
        !           513:        entp = cvs_ent_get(entf, file);
        !           514:        if ((entp == NULL) && (cvs_root->cr_method == CVS_METHOD_LOCAL)) {
        !           515:                cvs_log(LP_WARN, "I know nothing about %s", path);
        !           516:                return (-1);
        !           517:        }
        !           518:
        !           519:        if (cvs_root->cr_method != CVS_METHOD_LOCAL) {
        !           520:                if (cvs_client_senddir(dir) < 0)
        !           521:                        return (-1);
        !           522:        }
        !           523:
        !           524:        tsec = (time_t)fst.st_mtimespec.tv_sec;
        !           525:
        !           526:        if ((gmtime_r(&tsec, &tmstamp) == NULL) ||
        !           527:            (asctime_r(&tmstamp, buf) == NULL)) {
        !           528:                cvs_log(LP_ERR, "failed to generate file timestamp");
        !           529:                return (-1);
        !           530:        }
        !           531:        modif = (strcmp(buf, entp->ce_timestamp) == 0) ? 0 : 1;
        !           532:
        !           533:        if (cvs_root->cr_method != CVS_METHOD_LOCAL)
        !           534:                cvs_client_sendentry(entp);
        !           535:
        !           536:        if (!modif) {
        !           537:                if (cvs_root->cr_method != CVS_METHOD_LOCAL)
        !           538:                        cvs_client_sendreq(CVS_REQ_UNCHANGED, file, 0);
        !           539:                cvs_ent_close(entf);
        !           540:                return (0);
        !           541:        }
        !           542:
        !           543:        /* at this point, the file is modified */
        !           544:        if (cvs_root->cr_method != CVS_METHOD_LOCAL) {
        !           545:                cvs_client_sendreq(CVS_REQ_MODIFIED, file, 0);
        !           546:                cvs_sendfile(path);
        !           547:        }
        !           548:        else {
        !           549:                snprintf(rcspath, sizeof(rcspath), "%s/%s/%s%s",
        !           550:                    cvs_root->cr_dir, repo, path, RCS_FILE_EXT);
        !           551:
        !           552:                rf = rcs_open(rcspath, RCS_MODE_READ);
        !           553:                if (rf == NULL)
        !           554:                        return (-1);
        !           555:
        !           556:                printf("Index: %s\n%s\nRCS file: %s\n", path,
        !           557:                    RCS_DIFF_DIV, rcspath);
        !           558:
        !           559:                if (rev1 == NULL)
        !           560:                        r1 = entp->ce_rev;
        !           561:                else {
        !           562:                        r1 = rcsnum_alloc();
        !           563:                        rcsnum_aton(rev1, NULL, r1);
        !           564:                }
        !           565:
        !           566:                printf("retrieving revision %s\n",
        !           567:                    rcsnum_tostr(r1, buf, sizeof(buf)));
        !           568:                b1 = rcs_getrev(rf, r1);
        !           569:
        !           570:                if (rev2 != NULL) {
        !           571:                        printf("retrieving revision %s\n", rev2);
        !           572:                        r2 = rcsnum_alloc();
        !           573:                        rcsnum_aton(rev2, NULL, r2);
        !           574:                        b2 = rcs_getrev(rf, r2);
        !           575:                }
        !           576:                else {
        !           577:                        b2 = cvs_buf_load(path, BUF_AUTOEXT);
        !           578:                }
        !           579:
        !           580:                printf("%s\n", diffargs);
        !           581:                cvs_buf_write(b1, "/tmp/diff1", 0600);
        !           582:                cvs_buf_write(b2, "/tmp/diff2", 0600);
        !           583:                cvs_diffreg("/tmp/diff1", "/tmp/diff2");
        !           584:        }
        !           585:
        !           586:        cvs_ent_close(entf);
        !           587:
        !           588:        return (0);
        !           589: }
        !           590:
        !           591:
        !           592: /*
        !           593:  * cvs_diff_dir()
        !           594:  *
        !           595:  */
        !           596:
        !           597: int
        !           598: cvs_diff_dir(const char *dir, int recurse)
        !           599: {
        !           600:        char path[MAXPATHLEN];
        !           601:        DIR *dirp;
        !           602:        CVSENTRIES *entf;
        !           603:        struct dirent *dentp;
        !           604:        struct cvs_ent *entp;
        !           605:
        !           606:        printf("cvs_diff_dir(%s)\n", dir);
        !           607:
        !           608:        dirp = opendir(dir);
        !           609:        if (dirp == NULL) {
        !           610:                cvs_log(LP_ERRNO, "failed to open directory `%s'", dir);
        !           611:                return (-1);
        !           612:        }
        !           613:
        !           614:        entf = cvs_ent_open(dir);
        !           615:        if (entf == NULL) {
        !           616:                cvs_log(LP_ERR, "no CVS/Entries file in `%s'", dir);
        !           617:                (void)closedir(dirp);
        !           618:                return (-1);
        !           619:        }
        !           620:
        !           621:        while ((dentp = readdir(dirp)) != NULL) {
        !           622:                if ((strcmp(dentp->d_name, "CVS") == 0) ||
        !           623:                    (dentp->d_name[0] == '.'))
        !           624:                        continue;
        !           625:
        !           626:                if (strcmp(dir, ".") != 0) {
        !           627:                        strlcpy(path, dir, sizeof(path));
        !           628:                        strlcat(path, "/", sizeof(path));
        !           629:                }
        !           630:                else
        !           631:                        path[0] = '\0';
        !           632:                strlcat(path, dentp->d_name, sizeof(path));
        !           633:                if (dentp->d_type == DT_DIR) {
        !           634:                        if (!recurse)
        !           635:                                continue;
        !           636:                        cvs_diff_dir(path, recurse);
        !           637:                }
        !           638:                else {
        !           639:                        entp = cvs_ent_get(entf, dentp->d_name);
        !           640:                        if (entp == NULL) {
        !           641:                                cvs_client_sendreq(CVS_REQ_QUESTIONABLE, path,
        !           642:                                    0);
        !           643:                        }
        !           644:                        else {
        !           645: #if 0
        !           646:                                cvs_diff_file(path);
        !           647: #endif
        !           648:                        }
        !           649:                }
        !           650:        }
        !           651:
        !           652:        return (0);
        !           653: }
        !           654:
        !           655:
        !           656:
        !           657:
        !           658: int
        !           659: cvs_diffreg(const char *file1, const char *file2)
        !           660: {
        !           661:        FILE *f1, *f2;
        !           662:        int i, rval;
        !           663:
        !           664:        f1 = f2 = NULL;
        !           665:        rval = D_SAME;
        !           666:        anychange = 0;
        !           667:        lastline = 0;
        !           668:        lastmatchline = 0;
        !           669:        context_vec_ptr = context_vec_start - 1;
        !           670:        chrtran = (iflag ? cup2low : clow2low);
        !           671:
        !           672:        f1 = fopen(file1, "r");
        !           673:        if (f1 == NULL) {
        !           674:                cvs_log(LP_ERRNO, "%s", file1);
        !           675:                status |= 2;
        !           676:                goto closem;
        !           677:        }
        !           678:
        !           679:        f2 = fopen(file2, "r");
        !           680:        if (f2 == NULL) {
        !           681:                cvs_log(LP_ERRNO, "%s", file2);
        !           682:                status |= 2;
        !           683:                goto closem;
        !           684:        }
        !           685:
        !           686:        switch (files_differ(f1, f2)) {
        !           687:        case 0:
        !           688:                goto closem;
        !           689:        case 1:
        !           690:                break;
        !           691:        default:
        !           692:                /* error */
        !           693:                status |= 2;
        !           694:                goto closem;
        !           695:        }
        !           696:
        !           697:        if (!asciifile(f1) || !asciifile(f2)) {
        !           698:                rval = D_BINARY;
        !           699:                status |= 1;
        !           700:                goto closem;
        !           701:        }
        !           702:        prepare(0, f1, stb1.st_size);
        !           703:        prepare(1, f2, stb2.st_size);
        !           704:        prune();
        !           705:        sort(sfile[0], slen[0]);
        !           706:        sort(sfile[1], slen[1]);
        !           707:
        !           708:        member = (int *)file[1];
        !           709:        equiv(sfile[0], slen[0], sfile[1], slen[1], member);
        !           710:        member = realloc(member, (slen[1] + 2) * sizeof(int));
        !           711:
        !           712:        class = (int *)file[0];
        !           713:        unsort(sfile[0], slen[0], class);
        !           714:        class = realloc(class, (slen[0] + 2) * sizeof(int));
        !           715:
        !           716:        klist = malloc((slen[0] + 2) * sizeof(int));
        !           717:        clen = 0;
        !           718:        clistlen = 100;
        !           719:        clist = malloc(clistlen * sizeof(cand));
        !           720:        i = stone(class, slen[0], member, klist);
        !           721:        free(member);
        !           722:        free(class);
        !           723:
        !           724:        J = realloc(J, (len[0] + 2) * sizeof(int));
        !           725:        unravel(klist[i]);
        !           726:        free(clist);
        !           727:        free(klist);
        !           728:
        !           729:        ixold = realloc(ixold, (len[0] + 2) * sizeof(long));
        !           730:        ixnew = realloc(ixnew, (len[1] + 2) * sizeof(long));
        !           731:        check(f1, f2);
        !           732:        output(file1, f1, file2, f2);
        !           733:
        !           734: closem:
        !           735:        if (anychange) {
        !           736:                status |= 1;
        !           737:                if (rval == D_SAME)
        !           738:                        rval = D_DIFFER;
        !           739:        }
        !           740:        if (f1 != NULL)
        !           741:                fclose(f1);
        !           742:        if (f2 != NULL)
        !           743:                fclose(f2);
        !           744:        return (rval);
        !           745: }
        !           746:
        !           747: /*
        !           748:  * Check to see if the given files differ.
        !           749:  * Returns 0 if they are the same, 1 if different, and -1 on error.
        !           750:  * XXX - could use code from cmp(1) [faster]
        !           751:  */
        !           752: static int
        !           753: files_differ(FILE *f1, FILE *f2)
        !           754: {
        !           755:        char buf1[BUFSIZ], buf2[BUFSIZ];
        !           756:        size_t i, j;
        !           757:
        !           758:        if (stb1.st_size != stb2.st_size)
        !           759:                return (1);
        !           760:        for (;;) {
        !           761:                i = fread(buf1, 1, sizeof(buf1), f1);
        !           762:                j = fread(buf2, 1, sizeof(buf2), f2);
        !           763:                if (i != j)
        !           764:                        return (1);
        !           765:                if (i == 0 && j == 0) {
        !           766:                        if (ferror(f1) || ferror(f2))
        !           767:                                return (1);
        !           768:                        return (0);
        !           769:                }
        !           770:                if (memcmp(buf1, buf2, i) != 0)
        !           771:                        return (1);
        !           772:        }
        !           773: }
        !           774:
        !           775:
        !           776: char *
        !           777: splice(char *dir, char *file)
        !           778: {
        !           779:        char *tail, *buf;
        !           780:
        !           781:        if ((tail = strrchr(file, '/')) == NULL)
        !           782:                tail = file;
        !           783:        else
        !           784:                tail++;
        !           785:        asprintf(&buf, "%s/%s", dir, tail);
        !           786:        return (buf);
        !           787: }
        !           788:
        !           789: static void
        !           790: prepare(int i, FILE *fd, off_t filesize)
        !           791: {
        !           792:        struct line *p;
        !           793:        int j, h;
        !           794:        size_t sz;
        !           795:
        !           796:        rewind(fd);
        !           797:
        !           798:        sz = (filesize <= SIZE_MAX ? filesize : SIZE_MAX) / 25;
        !           799:        if (sz < 100)
        !           800:                sz = 100;
        !           801:
        !           802:        p = malloc((sz + 3) * sizeof(struct line));
        !           803:        for (j = 0; (h = readhash(fd));) {
        !           804:                if (j == (int)sz) {
        !           805:                        sz = sz * 3 / 2;
        !           806:                        p = realloc(p, (sz + 3) * sizeof(struct line));
        !           807:                }
        !           808:                p[++j].value = h;
        !           809:        }
        !           810:        len[i] = j;
        !           811:        file[i] = p;
        !           812: }
        !           813:
        !           814: static void
        !           815: prune(void)
        !           816: {
        !           817:        int i, j;
        !           818:
        !           819:        for (pref = 0; pref < len[0] && pref < len[1] &&
        !           820:            file[0][pref + 1].value == file[1][pref + 1].value;
        !           821:            pref++)
        !           822:                ;
        !           823:        for (suff = 0; suff < len[0] - pref && suff < len[1] - pref &&
        !           824:            file[0][len[0] - suff].value == file[1][len[1] - suff].value;
        !           825:            suff++)
        !           826:                ;
        !           827:        for (j = 0; j < 2; j++) {
        !           828:                sfile[j] = file[j] + pref;
        !           829:                slen[j] = len[j] - pref - suff;
        !           830:                for (i = 0; i <= slen[j]; i++)
        !           831:                        sfile[j][i].serial = i;
        !           832:        }
        !           833: }
        !           834:
        !           835: static void
        !           836: equiv(struct line *a, int n, struct line *b, int m, int *c)
        !           837: {
        !           838:        int i, j;
        !           839:
        !           840:        i = j = 1;
        !           841:        while (i <= n && j <= m) {
        !           842:                if (a[i].value < b[j].value)
        !           843:                        a[i++].value = 0;
        !           844:                else if (a[i].value == b[j].value)
        !           845:                        a[i++].value = j;
        !           846:                else
        !           847:                        j++;
        !           848:        }
        !           849:        while (i <= n)
        !           850:                a[i++].value = 0;
        !           851:        b[m + 1].value = 0;
        !           852:        j = 0;
        !           853:        while (++j <= m) {
        !           854:                c[j] = -b[j].serial;
        !           855:                while (b[j + 1].value == b[j].value) {
        !           856:                        j++;
        !           857:                        c[j] = b[j].serial;
        !           858:                }
        !           859:        }
        !           860:        c[j] = -1;
        !           861: }
        !           862:
        !           863: /* Code taken from ping.c */
        !           864: static int
        !           865: isqrt(int n)
        !           866: {
        !           867:        int y, x = 1;
        !           868:
        !           869:        if (n == 0)
        !           870:                return(0);
        !           871:
        !           872:        do { /* newton was a stinker */
        !           873:                y = x;
        !           874:                x = n / x;
        !           875:                x += y;
        !           876:                x /= 2;
        !           877:        } while ((x - y) > 1 || (x - y) < -1);
        !           878:
        !           879:        return (x);
        !           880: }
        !           881:
        !           882: static int
        !           883: stone(int *a, int n, int *b, int *c)
        !           884: {
        !           885:        int i, k, y, j, l;
        !           886:        int oldc, tc, oldl;
        !           887:        u_int numtries;
        !           888:
        !           889:        const u_int bound = dflag ? UINT_MAX : max(256, isqrt(n));
        !           890:
        !           891:        k = 0;
        !           892:        c[0] = newcand(0, 0, 0);
        !           893:        for (i = 1; i <= n; i++) {
        !           894:                j = a[i];
        !           895:                if (j == 0)
        !           896:                        continue;
        !           897:                y = -b[j];
        !           898:                oldl = 0;
        !           899:                oldc = c[0];
        !           900:                numtries = 0;
        !           901:                do {
        !           902:                        if (y <= clist[oldc].y)
        !           903:                                continue;
        !           904:                        l = search(c, k, y);
        !           905:                        if (l != oldl + 1)
        !           906:                                oldc = c[l - 1];
        !           907:                        if (l <= k) {
        !           908:                                if (clist[c[l]].y <= y)
        !           909:                                        continue;
        !           910:                                tc = c[l];
        !           911:                                c[l] = newcand(i, y, oldc);
        !           912:                                oldc = tc;
        !           913:                                oldl = l;
        !           914:                                numtries++;
        !           915:                        } else {
        !           916:                                c[l] = newcand(i, y, oldc);
        !           917:                                k++;
        !           918:                                break;
        !           919:                        }
        !           920:                } while ((y = b[++j]) > 0 && numtries < bound);
        !           921:        }
        !           922:        return (k);
        !           923: }
        !           924:
        !           925: static int
        !           926: newcand(int x, int y, int pred)
        !           927: {
        !           928:        struct cand *q;
        !           929:
        !           930:        if (clen == clistlen) {
        !           931:                clistlen = clistlen * 11 / 10;
        !           932:                clist = realloc(clist, clistlen * sizeof(cand));
        !           933:        }
        !           934:        q = clist + clen;
        !           935:        q->x = x;
        !           936:        q->y = y;
        !           937:        q->pred = pred;
        !           938:        return (clen++);
        !           939: }
        !           940:
        !           941: static int
        !           942: search(int *c, int k, int y)
        !           943: {
        !           944:        int i, j, l, t;
        !           945:
        !           946:        if (clist[c[k]].y < y)  /* quick look for typical case */
        !           947:                return (k + 1);
        !           948:        i = 0;
        !           949:        j = k + 1;
        !           950:        while (1) {
        !           951:                l = i + j;
        !           952:                if ((l >>= 1) <= i)
        !           953:                        break;
        !           954:                t = clist[c[l]].y;
        !           955:                if (t > y)
        !           956:                        j = l;
        !           957:                else if (t < y)
        !           958:                        i = l;
        !           959:                else
        !           960:                        return (l);
        !           961:        }
        !           962:        return (l + 1);
        !           963: }
        !           964:
        !           965: static void
        !           966: unravel(int p)
        !           967: {
        !           968:        struct cand *q;
        !           969:        int i;
        !           970:
        !           971:        for (i = 0; i <= len[0]; i++)
        !           972:                J[i] = i <= pref ? i :
        !           973:                    i > len[0] - suff ? i + len[1] - len[0] : 0;
        !           974:        for (q = clist + p; q->y != 0; q = clist + q->pred)
        !           975:                J[q->x + pref] = q->y + pref;
        !           976: }
        !           977:
        !           978: /*
        !           979:  * Check does double duty:
        !           980:  *  1. ferret out any fortuitous correspondences due
        !           981:  *     to confounding by hashing (which result in "jackpot")
        !           982:  *  2.  collect random access indexes to the two files
        !           983:  */
        !           984: static void
        !           985: check(FILE *f1, FILE *f2)
        !           986: {
        !           987:        int i, j, jackpot, c, d;
        !           988:        long ctold, ctnew;
        !           989:
        !           990:        rewind(f1);
        !           991:        rewind(f2);
        !           992:        j = 1;
        !           993:        ixold[0] = ixnew[0] = 0;
        !           994:        jackpot = 0;
        !           995:        ctold = ctnew = 0;
        !           996:        for (i = 1; i <= len[0]; i++) {
        !           997:                if (J[i] == 0) {
        !           998:                        ixold[i] = ctold += skipline(f1);
        !           999:                        continue;
        !          1000:                }
        !          1001:                while (j < J[i]) {
        !          1002:                        ixnew[j] = ctnew += skipline(f2);
        !          1003:                        j++;
        !          1004:                }
        !          1005:                if (bflag || wflag || iflag) {
        !          1006:                        for (;;) {
        !          1007:                                c = getc(f1);
        !          1008:                                d = getc(f2);
        !          1009:                                /*
        !          1010:                                 * GNU diff ignores a missing newline
        !          1011:                                 * in one file if bflag || wflag.
        !          1012:                                 */
        !          1013:                                if ((bflag || wflag) &&
        !          1014:                                    ((c == EOF && d == '\n') ||
        !          1015:                                    (c == '\n' && d == EOF))) {
        !          1016:                                        break;
        !          1017:                                }
        !          1018:                                ctold++;
        !          1019:                                ctnew++;
        !          1020:                                if (bflag && isspace(c) && isspace(d)) {
        !          1021:                                        do {
        !          1022:                                                if (c == '\n')
        !          1023:                                                        break;
        !          1024:                                                ctold++;
        !          1025:                                        } while (isspace(c = getc(f1)));
        !          1026:                                        do {
        !          1027:                                                if (d == '\n')
        !          1028:                                                        break;
        !          1029:                                                ctnew++;
        !          1030:                                        } while (isspace(d = getc(f2)));
        !          1031:                                } else if (wflag) {
        !          1032:                                        while (isspace(c) && c != '\n') {
        !          1033:                                                c = getc(f1);
        !          1034:                                                ctold++;
        !          1035:                                        }
        !          1036:                                        while (isspace(d) && d != '\n') {
        !          1037:                                                d = getc(f2);
        !          1038:                                                ctnew++;
        !          1039:                                        }
        !          1040:                                }
        !          1041:                                if (chrtran[c] != chrtran[d]) {
        !          1042:                                        jackpot++;
        !          1043:                                        J[i] = 0;
        !          1044:                                        if (c != '\n' && c != EOF)
        !          1045:                                                ctold += skipline(f1);
        !          1046:                                        if (d != '\n' && c != EOF)
        !          1047:                                                ctnew += skipline(f2);
        !          1048:                                        break;
        !          1049:                                }
        !          1050:                                if (c == '\n' || c == EOF)
        !          1051:                                        break;
        !          1052:                        }
        !          1053:                } else {
        !          1054:                        for (;;) {
        !          1055:                                ctold++;
        !          1056:                                ctnew++;
        !          1057:                                if ((c = getc(f1)) != (d = getc(f2))) {
        !          1058:                                        /* jackpot++; */
        !          1059:                                        J[i] = 0;
        !          1060:                                        if (c != '\n' && c != EOF)
        !          1061:                                                ctold += skipline(f1);
        !          1062:                                        if (d != '\n' && c != EOF)
        !          1063:                                                ctnew += skipline(f2);
        !          1064:                                        break;
        !          1065:                                }
        !          1066:                                if (c == '\n' || c == EOF)
        !          1067:                                        break;
        !          1068:                        }
        !          1069:                }
        !          1070:                ixold[i] = ctold;
        !          1071:                ixnew[j] = ctnew;
        !          1072:                j++;
        !          1073:        }
        !          1074:        for (; j <= len[1]; j++)
        !          1075:                ixnew[j] = ctnew += skipline(f2);
        !          1076:        /*
        !          1077:         * if (jackpot)
        !          1078:         *      fprintf(stderr, "jackpot\n");
        !          1079:         */
        !          1080: }
        !          1081:
        !          1082: /* shellsort CACM #201 */
        !          1083: static void
        !          1084: sort(struct line *a, int n)
        !          1085: {
        !          1086:        struct line *ai, *aim, w;
        !          1087:        int j, m = 0, k;
        !          1088:
        !          1089:        if (n == 0)
        !          1090:                return;
        !          1091:        for (j = 1; j <= n; j *= 2)
        !          1092:                m = 2 * j - 1;
        !          1093:        for (m /= 2; m != 0; m /= 2) {
        !          1094:                k = n - m;
        !          1095:                for (j = 1; j <= k; j++) {
        !          1096:                        for (ai = &a[j]; ai > a; ai -= m) {
        !          1097:                                aim = &ai[m];
        !          1098:                                if (aim < ai)
        !          1099:                                        break;  /* wraparound */
        !          1100:                                if (aim->value > ai[0].value ||
        !          1101:                                    (aim->value == ai[0].value &&
        !          1102:                                        aim->serial > ai[0].serial))
        !          1103:                                        break;
        !          1104:                                w.value = ai[0].value;
        !          1105:                                ai[0].value = aim->value;
        !          1106:                                aim->value = w.value;
        !          1107:                                w.serial = ai[0].serial;
        !          1108:                                ai[0].serial = aim->serial;
        !          1109:                                aim->serial = w.serial;
        !          1110:                        }
        !          1111:                }
        !          1112:        }
        !          1113: }
        !          1114:
        !          1115: static void
        !          1116: unsort(struct line *f, int l, int *b)
        !          1117: {
        !          1118:        int *a, i;
        !          1119:
        !          1120:        a = malloc((l + 1) * sizeof(int));
        !          1121:        for (i = 1; i <= l; i++)
        !          1122:                a[f[i].serial] = f[i].value;
        !          1123:        for (i = 1; i <= l; i++)
        !          1124:                b[i] = a[i];
        !          1125:        free(a);
        !          1126: }
        !          1127:
        !          1128: static int
        !          1129: skipline(FILE *f)
        !          1130: {
        !          1131:        int i, c;
        !          1132:
        !          1133:        for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
        !          1134:                continue;
        !          1135:        return (i);
        !          1136: }
        !          1137:
        !          1138: static void
        !          1139: output(const char *file1, FILE *f1, const char *file2, FILE *f2)
        !          1140: {
        !          1141:        int m, i0, i1, j0, j1;
        !          1142:
        !          1143:        rewind(f1);
        !          1144:        rewind(f2);
        !          1145:        m = len[0];
        !          1146:        J[0] = 0;
        !          1147:        J[m + 1] = len[1] + 1;
        !          1148:        for (i0 = 1; i0 <= m; i0 = i1 + 1) {
        !          1149:                while (i0 <= m && J[i0] == J[i0 - 1] + 1)
        !          1150:                        i0++;
        !          1151:                j0 = J[i0 - 1] + 1;
        !          1152:                i1 = i0 - 1;
        !          1153:                while (i1 < m && J[i1 + 1] == 0)
        !          1154:                        i1++;
        !          1155:                j1 = J[i1 + 1] - 1;
        !          1156:                J[i1] = j1;
        !          1157:                change(file1, f1, file2, f2, i0, i1, j0, j1);
        !          1158:        }
        !          1159:        if (m == 0)
        !          1160:                change(file1, f1, file2, f2, 1, 0, 1, len[1]);
        !          1161:        if (format == D_IFDEF) {
        !          1162:                for (;;) {
        !          1163: #define        c i0
        !          1164:                        if ((c = getc(f1)) == EOF)
        !          1165:                                return;
        !          1166:                        putchar(c);
        !          1167:                }
        !          1168: #undef c
        !          1169:        }
        !          1170:        if (anychange != 0) {
        !          1171:                if (format == D_CONTEXT)
        !          1172:                        dump_context_vec(f1, f2);
        !          1173:                else if (format == D_UNIFIED)
        !          1174:                        dump_unified_vec(f1, f2);
        !          1175:        }
        !          1176: }
        !          1177:
        !          1178: static __inline void
        !          1179: range(int a, int b, char *separator)
        !          1180: {
        !          1181:        printf("%d", a > b ? b : a);
        !          1182:        if (a < b)
        !          1183:                printf("%s%d", separator, b);
        !          1184: }
        !          1185:
        !          1186: static __inline void
        !          1187: uni_range(int a, int b)
        !          1188: {
        !          1189:        if (a < b)
        !          1190:                printf("%d,%d", a, b - a + 1);
        !          1191:        else if (a == b)
        !          1192:                printf("%d", b);
        !          1193:        else
        !          1194:                printf("%d,0", b);
        !          1195: }
        !          1196:
        !          1197: static char *
        !          1198: preadline(int fd, size_t len, off_t off)
        !          1199: {
        !          1200:        char *line;
        !          1201:        ssize_t nr;
        !          1202:
        !          1203:        line = malloc(len + 1);
        !          1204:        if (line == NULL) {
        !          1205:                cvs_log(LP_ERRNO, "failed to allocate line");
        !          1206:                return (NULL);
        !          1207:        }
        !          1208:        if ((nr = pread(fd, line, len, off)) < 0) {
        !          1209:                cvs_log(LP_ERRNO, "preadline failed");
        !          1210:                return (NULL);
        !          1211:        }
        !          1212:        line[nr] = '\0';
        !          1213:        return (line);
        !          1214: }
        !          1215:
        !          1216: static int
        !          1217: ignoreline(char *line)
        !          1218: {
        !          1219:        int ret;
        !          1220:
        !          1221:        ret = regexec(&ignore_re, line, 0, NULL, 0);
        !          1222:        free(line);
        !          1223:        return (ret == 0);      /* if it matched, it should be ignored. */
        !          1224: }
        !          1225:
        !          1226: /*
        !          1227:  * Indicate that there is a difference between lines a and b of the from file
        !          1228:  * to get to lines c to d of the to file.  If a is greater then b then there
        !          1229:  * are no lines in the from file involved and this means that there were
        !          1230:  * lines appended (beginning at b).  If c is greater than d then there are
        !          1231:  * lines missing from the to file.
        !          1232:  */
        !          1233: static void
        !          1234: change(const char *file1, FILE *f1, const char *file2, FILE *f2,
        !          1235:        int a, int b, int c, int d)
        !          1236: {
        !          1237:        static size_t max_context = 64;
        !          1238:        int i;
        !          1239:
        !          1240:        if (format != D_IFDEF && a > b && c > d)
        !          1241:                return;
        !          1242:        if (ignore_pats != NULL) {
        !          1243:                char *line;
        !          1244:                /*
        !          1245:                 * All lines in the change, insert, or delete must
        !          1246:                 * match an ignore pattern for the change to be
        !          1247:                 * ignored.
        !          1248:                 */
        !          1249:                if (a <= b) {           /* Changes and deletes. */
        !          1250:                        for (i = a; i <= b; i++) {
        !          1251:                                line = preadline(fileno(f1),
        !          1252:                                    ixold[i] - ixold[i - 1], ixold[i - 1]);
        !          1253:                                if (!ignoreline(line))
        !          1254:                                        goto proceed;
        !          1255:                        }
        !          1256:                }
        !          1257:                if (a > b || c <= d) {  /* Changes and inserts. */
        !          1258:                        for (i = c; i <= d; i++) {
        !          1259:                                line = preadline(fileno(f2),
        !          1260:                                    ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
        !          1261:                                if (!ignoreline(line))
        !          1262:                                        goto proceed;
        !          1263:                        }
        !          1264:                }
        !          1265:                return;
        !          1266:        }
        !          1267: proceed:
        !          1268:        if (format == D_CONTEXT || format == D_UNIFIED) {
        !          1269:                /*
        !          1270:                 * Allocate change records as needed.
        !          1271:                 */
        !          1272:                if (context_vec_ptr == context_vec_end - 1) {
        !          1273:                        ptrdiff_t offset = context_vec_ptr - context_vec_start;
        !          1274:                        max_context <<= 1;
        !          1275:                        context_vec_start = realloc(context_vec_start,
        !          1276:                            max_context * sizeof(struct context_vec));
        !          1277:                        context_vec_end = context_vec_start + max_context;
        !          1278:                        context_vec_ptr = context_vec_start + offset;
        !          1279:                }
        !          1280:                if (anychange == 0) {
        !          1281:                        /*
        !          1282:                         * Print the context/unidiff header first time through.
        !          1283:                         */
        !          1284:                        printf("%s %s   %s",
        !          1285:                            format == D_CONTEXT ? "***" : "---", diff_file,
        !          1286:                            ctime(&stb1.st_mtime));
        !          1287:                        printf("%s %s   %s",
        !          1288:                            format == D_CONTEXT ? "---" : "+++", diff_file,
        !          1289:                            ctime(&stb2.st_mtime));
        !          1290:                        anychange = 1;
        !          1291:                } else if (a > context_vec_ptr->b + (2 * context) + 1 &&
        !          1292:                    c > context_vec_ptr->d + (2 * context) + 1) {
        !          1293:                        /*
        !          1294:                         * If this change is more than 'context' lines from the
        !          1295:                         * previous change, dump the record and reset it.
        !          1296:                         */
        !          1297:                        if (format == D_CONTEXT)
        !          1298:                                dump_context_vec(f1, f2);
        !          1299:                        else
        !          1300:                                dump_unified_vec(f1, f2);
        !          1301:                }
        !          1302:                context_vec_ptr++;
        !          1303:                context_vec_ptr->a = a;
        !          1304:                context_vec_ptr->b = b;
        !          1305:                context_vec_ptr->c = c;
        !          1306:                context_vec_ptr->d = d;
        !          1307:                return;
        !          1308:        }
        !          1309:        if (anychange == 0)
        !          1310:                anychange = 1;
        !          1311:        switch (format) {
        !          1312:        case D_BRIEF:
        !          1313:                return;
        !          1314:        case D_NORMAL:
        !          1315:                range(a, b, ",");
        !          1316:                putchar(a > b ? 'a' : c > d ? 'd' : 'c');
        !          1317:                if (format == D_NORMAL)
        !          1318:                        range(c, d, ",");
        !          1319:                putchar('\n');
        !          1320:                break;
        !          1321:        }
        !          1322:        if (format == D_NORMAL || format == D_IFDEF) {
        !          1323:                fetch(ixold, a, b, f1, '<', 1);
        !          1324:                if (a <= b && c <= d && format == D_NORMAL)
        !          1325:                        puts("---");
        !          1326:        }
        !          1327:        i = fetch(ixnew, c, d, f2, format == D_NORMAL ? '>' : '\0', 0);
        !          1328:        if (inifdef) {
        !          1329:                printf("#endif /* %s */\n", ifdefname);
        !          1330:                inifdef = 0;
        !          1331:        }
        !          1332: }
        !          1333:
        !          1334: static int
        !          1335: fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile)
        !          1336: {
        !          1337:        int i, j, c, lastc, col, nc;
        !          1338:
        !          1339:        /*
        !          1340:         * When doing #ifdef's, copy down to current line
        !          1341:         * if this is the first file, so that stuff makes it to output.
        !          1342:         */
        !          1343:        if (format == D_IFDEF && oldfile) {
        !          1344:                long curpos = ftell(lb);
        !          1345:                /* print through if append (a>b), else to (nb: 0 vs 1 orig) */
        !          1346:                nc = f[a > b ? b : a - 1] - curpos;
        !          1347:                for (i = 0; i < nc; i++)
        !          1348:                        putchar(getc(lb));
        !          1349:        }
        !          1350:        if (a > b)
        !          1351:                return (0);
        !          1352:        if (format == D_IFDEF) {
        !          1353:                if (inifdef) {
        !          1354:                        printf("#else /* %s%s */\n",
        !          1355:                            oldfile == 1 ? "!" : "", ifdefname);
        !          1356:                } else {
        !          1357:                        if (oldfile)
        !          1358:                                printf("#ifndef %s\n", ifdefname);
        !          1359:                        else
        !          1360:                                printf("#ifdef %s\n", ifdefname);
        !          1361:                }
        !          1362:                inifdef = 1 + oldfile;
        !          1363:        }
        !          1364:        for (i = a; i <= b; i++) {
        !          1365:                fseek(lb, f[i - 1], SEEK_SET);
        !          1366:                nc = f[i] - f[i - 1];
        !          1367:                if (format != D_IFDEF && ch != '\0') {
        !          1368:                        putchar(ch);
        !          1369:                        if (Tflag && (format == D_NORMAL || format == D_CONTEXT
        !          1370:                            || format == D_UNIFIED))
        !          1371:                                putchar('\t');
        !          1372:                        else if (format != D_UNIFIED)
        !          1373:                                putchar(' ');
        !          1374:                }
        !          1375:                col = 0;
        !          1376:                for (j = 0, lastc = '\0'; j < nc; j++, lastc = c) {
        !          1377:                        if ((c = getc(lb)) == EOF) {
        !          1378:                                puts("\n\\ No newline at end of file");
        !          1379:                                return (0);
        !          1380:                        }
        !          1381:                        if (c == '\t' && tflag) {
        !          1382:                                do {
        !          1383:                                        putchar(' ');
        !          1384:                                } while (++col & 7);
        !          1385:                        } else {
        !          1386:                                putchar(c);
        !          1387:                                col++;
        !          1388:                        }
        !          1389:                }
        !          1390:        }
        !          1391:        return (0);
        !          1392: }
        !          1393:
        !          1394: /*
        !          1395:  * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
        !          1396:  */
        !          1397: static int
        !          1398: readhash(FILE *f)
        !          1399: {
        !          1400:        int i, t, space;
        !          1401:        int sum;
        !          1402:
        !          1403:        sum = 1;
        !          1404:        space = 0;
        !          1405:        if (!bflag && !wflag) {
        !          1406:                if (iflag)
        !          1407:                        for (i = 0; (t = getc(f)) != '\n'; i++) {
        !          1408:                                if (t == EOF) {
        !          1409:                                        if (i == 0)
        !          1410:                                                return (0);
        !          1411:                                        break;
        !          1412:                                }
        !          1413:                                sum = sum * 127 + chrtran[t];
        !          1414:                        }
        !          1415:                else
        !          1416:                        for (i = 0; (t = getc(f)) != '\n'; i++) {
        !          1417:                                if (t == EOF) {
        !          1418:                                        if (i == 0)
        !          1419:                                                return (0);
        !          1420:                                        break;
        !          1421:                                }
        !          1422:                                sum = sum * 127 + t;
        !          1423:                        }
        !          1424:        } else {
        !          1425:                for (i = 0;;) {
        !          1426:                        switch (t = getc(f)) {
        !          1427:                        case '\t':
        !          1428:                        case ' ':
        !          1429:                                space++;
        !          1430:                                continue;
        !          1431:                        default:
        !          1432:                                if (space && !wflag) {
        !          1433:                                        i++;
        !          1434:                                        space = 0;
        !          1435:                                }
        !          1436:                                sum = sum * 127 + chrtran[t];
        !          1437:                                i++;
        !          1438:                                continue;
        !          1439:                        case EOF:
        !          1440:                                if (i == 0)
        !          1441:                                        return (0);
        !          1442:                                /* FALLTHROUGH */
        !          1443:                        case '\n':
        !          1444:                                break;
        !          1445:                        }
        !          1446:                        break;
        !          1447:                }
        !          1448:        }
        !          1449:        /*
        !          1450:         * There is a remote possibility that we end up with a zero sum.
        !          1451:         * Zero is used as an EOF marker, so return 1 instead.
        !          1452:         */
        !          1453:        return (sum == 0 ? 1 : sum);
        !          1454: }
        !          1455:
        !          1456: static int
        !          1457: asciifile(FILE *f)
        !          1458: {
        !          1459:        char buf[BUFSIZ];
        !          1460:        int i, cnt;
        !          1461:
        !          1462:        if (aflag || f == NULL)
        !          1463:                return (1);
        !          1464:
        !          1465:        rewind(f);
        !          1466:        cnt = fread(buf, 1, sizeof(buf), f);
        !          1467:        for (i = 0; i < cnt; i++)
        !          1468:                if (!isprint(buf[i]) && !isspace(buf[i]))
        !          1469:                        return (0);
        !          1470:        return (1);
        !          1471: }
        !          1472:
        !          1473: static __inline int min(int a, int b)
        !          1474: {
        !          1475:        return (a < b ? a : b);
        !          1476: }
        !          1477:
        !          1478: static __inline int max(int a, int b)
        !          1479: {
        !          1480:        return (a > b ? a : b);
        !          1481: }
        !          1482:
        !          1483: static char *
        !          1484: match_function(const long *f, int pos, FILE *file)
        !          1485: {
        !          1486:        char buf[FUNCTION_CONTEXT_SIZE];
        !          1487:        size_t nc;
        !          1488:        int last = lastline;
        !          1489:        char *p;
        !          1490:
        !          1491:        lastline = pos;
        !          1492:        while (pos > last) {
        !          1493:                fseek(file, f[pos - 1], SEEK_SET);
        !          1494:                nc = f[pos] - f[pos - 1];
        !          1495:                if (nc >= sizeof(buf))
        !          1496:                        nc = sizeof(buf) - 1;
        !          1497:                nc = fread(buf, 1, nc, file);
        !          1498:                if (nc > 0) {
        !          1499:                        buf[nc] = '\0';
        !          1500:                        p = strchr(buf, '\n');
        !          1501:                        if (p != NULL)
        !          1502:                                *p = '\0';
        !          1503:                        if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') {
        !          1504:                                strlcpy(lastbuf, buf, sizeof lastbuf);
        !          1505:                                lastmatchline = pos;
        !          1506:                                return lastbuf;
        !          1507:                        }
        !          1508:                }
        !          1509:                pos--;
        !          1510:        }
        !          1511:        return lastmatchline > 0 ? lastbuf : NULL;
        !          1512: }
        !          1513:
        !          1514: /* dump accumulated "context" diff changes */
        !          1515: static void
        !          1516: dump_context_vec(FILE *f1, FILE *f2)
        !          1517: {
        !          1518:        struct context_vec *cvp = context_vec_start;
        !          1519:        int lowa, upb, lowc, upd, do_output;
        !          1520:        int a, b, c, d;
        !          1521:        char ch, *f;
        !          1522:
        !          1523:        if (context_vec_start > context_vec_ptr)
        !          1524:                return;
        !          1525:
        !          1526:        b = d = 0;              /* gcc */
        !          1527:        lowa = max(1, cvp->a - context);
        !          1528:        upb = min(len[0], context_vec_ptr->b + context);
        !          1529:        lowc = max(1, cvp->c - context);
        !          1530:        upd = min(len[1], context_vec_ptr->d + context);
        !          1531:
        !          1532:        printf("***************");
        !          1533:        printf("\n*** ");
        !          1534:        range(lowa, upb, ",");
        !          1535:        printf(" ****\n");
        !          1536:
        !          1537:        /*
        !          1538:         * Output changes to the "old" file.  The first loop suppresses
        !          1539:         * output if there were no changes to the "old" file (we'll see
        !          1540:         * the "old" lines as context in the "new" list).
        !          1541:         */
        !          1542:        do_output = 0;
        !          1543:        for (; cvp <= context_vec_ptr; cvp++)
        !          1544:                if (cvp->a <= cvp->b) {
        !          1545:                        cvp = context_vec_start;
        !          1546:                        do_output++;
        !          1547:                        break;
        !          1548:                }
        !          1549:        if (do_output) {
        !          1550:                while (cvp <= context_vec_ptr) {
        !          1551:                        a = cvp->a;
        !          1552:                        b = cvp->b;
        !          1553:                        c = cvp->c;
        !          1554:                        d = cvp->d;
        !          1555:
        !          1556:                        if (a <= b && c <= d)
        !          1557:                                ch = 'c';
        !          1558:                        else
        !          1559:                                ch = (a <= b) ? 'd' : 'a';
        !          1560:
        !          1561:                        if (ch == 'a')
        !          1562:                                fetch(ixold, lowa, b, f1, ' ', 0);
        !          1563:                        else {
        !          1564:                                fetch(ixold, lowa, a - 1, f1, ' ', 0);
        !          1565:                                fetch(ixold, a, b, f1,
        !          1566:                                    ch == 'c' ? '!' : '-', 0);
        !          1567:                        }
        !          1568:                        lowa = b + 1;
        !          1569:                        cvp++;
        !          1570:                }
        !          1571:                fetch(ixold, b + 1, upb, f1, ' ', 0);
        !          1572:        }
        !          1573:        /* output changes to the "new" file */
        !          1574:        printf("--- ");
        !          1575:        range(lowc, upd, ",");
        !          1576:        printf(" ----\n");
        !          1577:
        !          1578:        do_output = 0;
        !          1579:        for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
        !          1580:                if (cvp->c <= cvp->d) {
        !          1581:                        cvp = context_vec_start;
        !          1582:                        do_output++;
        !          1583:                        break;
        !          1584:                }
        !          1585:        if (do_output) {
        !          1586:                while (cvp <= context_vec_ptr) {
        !          1587:                        a = cvp->a;
        !          1588:                        b = cvp->b;
        !          1589:                        c = cvp->c;
        !          1590:                        d = cvp->d;
        !          1591:
        !          1592:                        if (a <= b && c <= d)
        !          1593:                                ch = 'c';
        !          1594:                        else
        !          1595:                                ch = (a <= b) ? 'd' : 'a';
        !          1596:
        !          1597:                        if (ch == 'd')
        !          1598:                                fetch(ixnew, lowc, d, f2, ' ', 0);
        !          1599:                        else {
        !          1600:                                fetch(ixnew, lowc, c - 1, f2, ' ', 0);
        !          1601:                                fetch(ixnew, c, d, f2,
        !          1602:                                    ch == 'c' ? '!' : '+', 0);
        !          1603:                        }
        !          1604:                        lowc = d + 1;
        !          1605:                        cvp++;
        !          1606:                }
        !          1607:                fetch(ixnew, d + 1, upd, f2, ' ', 0);
        !          1608:        }
        !          1609:        context_vec_ptr = context_vec_start - 1;
        !          1610: }
        !          1611:
        !          1612: /* dump accumulated "unified" diff changes */
        !          1613: static void
        !          1614: dump_unified_vec(FILE *f1, FILE *f2)
        !          1615: {
        !          1616:        struct context_vec *cvp = context_vec_start;
        !          1617:        int lowa, upb, lowc, upd;
        !          1618:        int a, b, c, d;
        !          1619:        char ch, *f;
        !          1620:
        !          1621:        if (context_vec_start > context_vec_ptr)
        !          1622:                return;
        !          1623:
        !          1624:        b = d = 0;              /* gcc */
        !          1625:        lowa = max(1, cvp->a - context);
        !          1626:        upb = min(len[0], context_vec_ptr->b + context);
        !          1627:        lowc = max(1, cvp->c - context);
        !          1628:        upd = min(len[1], context_vec_ptr->d + context);
        !          1629:
        !          1630:        fputs("@@ -", stdout);
        !          1631:        uni_range(lowa, upb);
        !          1632:        fputs(" +", stdout);
        !          1633:        uni_range(lowc, upd);
        !          1634:        fputs(" @@", stdout);
        !          1635:        putchar('\n');
        !          1636:
        !          1637:        /*
        !          1638:         * Output changes in "unified" diff format--the old and new lines
        !          1639:         * are printed together.
        !          1640:         */
        !          1641:        for (; cvp <= context_vec_ptr; cvp++) {
        !          1642:                a = cvp->a;
        !          1643:                b = cvp->b;
        !          1644:                c = cvp->c;
        !          1645:                d = cvp->d;
        !          1646:
        !          1647:                /*
        !          1648:                 * c: both new and old changes
        !          1649:                 * d: only changes in the old file
        !          1650:                 * a: only changes in the new file
        !          1651:                 */
        !          1652:                if (a <= b && c <= d)
        !          1653:                        ch = 'c';
        !          1654:                else
        !          1655:                        ch = (a <= b) ? 'd' : 'a';
        !          1656:
        !          1657:                switch (ch) {
        !          1658:                case 'c':
        !          1659:                        fetch(ixold, lowa, a - 1, f1, ' ', 0);
        !          1660:                        fetch(ixold, a, b, f1, '-', 0);
        !          1661:                        fetch(ixnew, c, d, f2, '+', 0);
        !          1662:                        break;
        !          1663:                case 'd':
        !          1664:                        fetch(ixold, lowa, a - 1, f1, ' ', 0);
        !          1665:                        fetch(ixold, a, b, f1, '-', 0);
        !          1666:                        break;
        !          1667:                case 'a':
        !          1668:                        fetch(ixnew, lowc, c - 1, f2, ' ', 0);
        !          1669:                        fetch(ixnew, c, d, f2, '+', 0);
        !          1670:                        break;
        !          1671:                }
        !          1672:                lowa = b + 1;
        !          1673:                lowc = d + 1;
        !          1674:        }
        !          1675:        fetch(ixnew, d + 1, upd, f2, ' ', 0);
        !          1676:
        !          1677:        context_vec_ptr = context_vec_start - 1;
        !          1678: }