diff options
author | Todd C. Miller <millert@cvs.openbsd.org> | 2003-07-06 20:49:00 +0000 |
---|---|---|
committer | Todd C. Miller <millert@cvs.openbsd.org> | 2003-07-06 20:49:00 +0000 |
commit | 737cbdaa54f6fa925621ca719208c6d5c8ab77fe (patch) | |
tree | c8048f62eadf7de70c0c27894f168dda8ff12a7e /usr.bin | |
parent | fb77cb0f007d25ab8a99b64eb77a2c87f0f2716b (diff) |
Some fairly major changes:
o -N is implemented
o -X is implemented
o -x is implemented
o diff.c has been rewritten and GNU long options are now supported
o diffdir.c has been rewritten
+ no longer does fork + exec of /usr/bin/diff
+ can be called recursively (and will be for -r)
o diff.h
+ don't include any .h files here any more, do it in the .c files
+ no Bell Labs code in this, gets a UCB copyright (the 32v sources
only have a diff.c and there is nothing in common).
o diffreg.c
+ most all remaining globals are now private to diffreg.c
+ files are only opened once
+ dynamically allocated objects are either freed or realloced
+ added missing UCB copyright (there were lots of UCB changes)
+ print correct thing when -s is specified
OK deraadt@
Diffstat (limited to 'usr.bin')
-rw-r--r-- | usr.bin/diff/diff.1 | 33 | ||||
-rw-r--r-- | usr.bin/diff/diff.c | 315 | ||||
-rw-r--r-- | usr.bin/diff/diff.h | 93 | ||||
-rw-r--r-- | usr.bin/diff/diffdir.c | 533 | ||||
-rw-r--r-- | usr.bin/diff/diffreg.c | 513 | ||||
-rw-r--r-- | usr.bin/diff/pathnames.h | 34 |
6 files changed, 798 insertions, 723 deletions
diff --git a/usr.bin/diff/diff.1 b/usr.bin/diff/diff.1 index 3518ca11573..c555da5ddd0 100644 --- a/usr.bin/diff/diff.1 +++ b/usr.bin/diff/diff.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: diff.1,v 1.10 2003/07/04 17:50:24 millert Exp $ +.\" $OpenBSD: diff.1,v 1.11 2003/07/06 20:48:59 millert Exp $ .\" .\" Copyright (c) 1980, 1990, 1993 .\" The Regents of the University of California. All rights reserved. @@ -56,14 +56,18 @@ .Op Fl U Ar number .Ar file1 file2 .Nm diff -.Op Fl abitw +.Op Fl abiNtw .Oo .Fl c | Fl e | Fl f | .Fl n | Fl u .Oc +.Bk -words .Op Fl r .Op Fl s .Op Fl S Ar name +.Op Fl X Ar file +.Op Fl x Ar pattern +.Ek .Ar dir1 dir2 .Sh DESCRIPTION The @@ -78,8 +82,6 @@ No output is produced if the files are identical. .Pp Output options (mutually exclusive): .Bl -tag -width Ds -.It Fl a -Treat all files as ASCII. .It Fl c Produces a diff with 3 lines of context. With @@ -168,6 +170,8 @@ lines of context. .Pp Comparison options: .Bl -tag -width Ds +.It Fl a +Treat all files as ASCII. .It Fl b Causes trailing blanks (spaces and tabs) to be ignored, and other strings of blanks to compare equal. @@ -197,6 +201,9 @@ will compare equal to .Pp Directory comparison options: .Bl -tag -width Ds +.It Fl N +If a file is found in only one directory, act as if it was found in the +other directory too but was of zero size. .It Fl r Causes application of .Nm @@ -210,6 +217,21 @@ Re-starts a directory .Nm in the middle, beginning with file .Ar name . +.It Fl X Ar file +Exclude files and subdirectories from comparison whose basenames match +lines in +.Ar file . +Multiple +.Fl X +options may be specified. +.It Fl x Ar pattern +Exclude files and subdirectories from comparison whose basenames match +.Ar pattern . +Patterns are matched using shell-style globbing via +.Xr fnmatch 3 . +Multiple +.Fl x +options may be specified. .El .Pp If both arguments are directories, @@ -381,7 +403,8 @@ An error occurred. .Xr cmp 1 , .Xr comm 1 , .Xr diff3 1 , -.Xr ed 1 +.Xr ed 1 , +.Xr fnmatch 3 .Sh HISTORY A .Nm diff --git a/usr.bin/diff/diff.c b/usr.bin/diff/diff.c index 9a6ad78dc5b..6c570cc98e4 100644 --- a/usr.bin/diff/diff.c +++ b/usr.bin/diff/diff.c @@ -1,156 +1,157 @@ -/* $OpenBSD: diff.c,v 1.22 2003/07/04 17:52:35 millert Exp $ */ +/* $OpenBSD: diff.c,v 1.23 2003/07/06 20:48:59 millert Exp $ */ /* - * Copyright (C) Caldera International Inc. 2001-2002. - * All rights reserved. + * Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com> * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code and documentation must retain the above - * copyright notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed or owned by Caldera - * International, Inc. - * 4. Neither the name of Caldera International, Inc. nor the names of other - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA - * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT, - * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. */ +#ifndef lint +static const char rcsid[] = "$OpenBSD: diff.c,v 1.23 2003/07/06 20:48:59 millert Exp $"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> + +#include <err.h> #include <errno.h> +#include <getopt.h> #include <stdlib.h> +#include <stdio.h> #include <stdarg.h> #include <string.h> #include <unistd.h> #include "diff.h" -#include "pathnames.h" -#if 0 -static char const sccsid[] = "@(#)diff.c 4.7 5/11/89"; -#endif - -/* - * diff - driver and subroutines - */ -int opt; -int aflag; /* treat all files as text */ -int tflag; /* expand tabs on output */ -/* Algorithm related options. */ -int bflag; /* ignore blanks in comparisons */ -int wflag; /* totally ignore blanks in comparisons */ -int iflag; /* ignore case in comparisons */ -/* Options on hierarchical diffs. */ -int rflag; /* recursively trace directories */ -int sflag; /* announce files which are same */ -char *start; /* do file only if name >= this */ -/* Variable for -D D_IFDEF option. */ -char *ifdefname; /* What we will print for #ifdef/#endif */ -/* Variables for -c and -u context option. */ -int context; /* lines of context to be printed */ -/* State for exit status. */ -int status; -int anychange; -/* Variables for diffdir. */ -char **diffargv; /* option list to pass to recursive diffs */ +int aflag, bflag, iflag, Nflag, rflag, sflag, tflag, wflag; +int format, context, status; +char *start, *ifdefname, *diffargs; +struct stat stb1, stb2; +struct excludes *excludes_list; -/* - * Input file names. - * With diffdir, file1 and file2 are allocated MAXPATHLEN space, - * and padded with a '/', and then efile1 and efile2 point after - * the '/'. - */ -char *file1, *file2, *efile1, *efile2; -struct stat stb1, stb2; +#define OPTIONS "abC:cD:efhinNrS:stU:uwX:x:" +static struct option longopts[] = { + { "text", no_argument, 0, 'a' }, + { "ignore-space-change", no_argument, 0, 'b' }, + { "context", optional_argument, 0, 'C' }, + { "ifdef", required_argument, 0, 'D' }, + { "ed", no_argument, 0, 'e' }, + { "forward-ed", no_argument, 0, 'f' }, + { "ignore-case", no_argument, 0, 'i' }, + { "new-file", no_argument, 0, 'N' }, + { "rcs", no_argument, 0, 'n' }, + { "recursive", no_argument, 0, 'r' }, + { "report-identical-files", no_argument, 0, 's' }, + { "starting-file", required_argument, 0, 'S' }, + { "expand-tabs", no_argument, 0, 't' }, + { "unified", optional_argument, 0, 'U' }, + { "ignore-all-space", no_argument, 0, 'w' }, + { "exclude", required_argument, 0, 'x' }, + { "exclude-from", required_argument, 0, 'X' }, +}; __dead void usage(void); +void push_excludes(char *); +void read_excludes_file(char *file); +void set_argstr(char **, char **); int main(int argc, char **argv) { - int ch; + char *ep, **oargv; + long l; + int ch, gotstdin; - status = 2; - diffargv = argv; + oargv = argv; + gotstdin = 0; - while ((ch = getopt(argc, argv, "abC:cD:efhinrS:stU:uw")) != -1) { + while ((ch = getopt_long(argc, argv, OPTIONS, longopts, NULL)) != -1) { switch (ch) { case 'a': - aflag++; + aflag = 1; break; case 'b': - bflag++; + bflag = 1; break; case 'C': - opt = D_CONTEXT; - if (!isdigit(*optarg)) - usage(); - context = atoi(optarg); /* XXX - use strtol */ - break; case 'c': - opt = D_CONTEXT; - context = 3; + format = D_CONTEXT; + if (optarg != NULL) { + l = strtol(optarg, &ep, 10); + if (*ep != '\0' || l < 0 || l >= INT_MAX) + usage(); + context = (int)l; + } else + context = 3; break; case 'D': - opt = D_IFDEF; + format = D_IFDEF; ifdefname = optarg; break; case 'e': - opt = D_EDIT; + format = D_EDIT; break; case 'f': - opt = D_REVERSE; + format = D_REVERSE; break; case 'h': /* silently ignore for backwards compatibility */ break; case 'i': - iflag++; + iflag = 1; + break; + case 'N': + Nflag = 1; break; case 'n': - opt = D_NREVERSE; + format = D_NREVERSE; break; case 'r': - rflag++; + rflag = 1; break; case 'S': start = optarg; break; case 's': - sflag++; + sflag = 1; break; case 't': - tflag++; + tflag = 1; break; case 'U': - opt = D_UNIFIED; - if (!isdigit(*optarg)) - usage(); - context = atoi(optarg); /* XXX - use strtol */ - break; case 'u': - opt = D_UNIFIED; - context = 3; + format = D_UNIFIED; + if (optarg != NULL) { + l = strtol(optarg, &ep, 10); + if (*ep != '\0' || l < 0 || l >= INT_MAX) + usage(); + context = (int)l; + } else + context = 3; break; case 'w': - wflag++; + wflag = 1; + break; + case 'X': + read_excludes_file(optarg); + break; + case 'x': + push_excludes(optarg); break; default: usage(); @@ -160,49 +161,42 @@ main(int argc, char **argv) argc -= optind; argv += optind; + /* + * Do sanity checks, fill in stb1 and stb2 and call the appropriate + * driver routine. Both drivers use the contents of stb1 and stb2. + */ if (argc != 2) - errorx("two filename arguments required"); - file1 = argv[0]; - file2 = argv[1]; - if (!strcmp(file1, "-")) + usage(); + if (strcmp(argv[0], "-") == 0) { stb1.st_mode = S_IFREG; - else if (stat(file1, &stb1) < 0) - error("%s", file1); - if (!strcmp(file2, "-")) + gotstdin = 1; + } else if (stat(argv[0], &stb1) != 0) + error("%s", argv[0]); + if (strcmp(argv[1], "-") == 0) { stb2.st_mode = S_IFREG; - else if (stat(file2, &stb2) < 0) - error("%s", file2); - if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) - diffdir(argv); - else - diffreg(); - done(0); -} - -int -min(int a, int b) -{ - - return (a < b ? a : b); -} - -int -max(int a, int b) -{ - - return (a > b ? a : b); + gotstdin = 1; + } else if (stat(argv[1], &stb2) != 0) + error("%s", argv[1]); + if (gotstdin && (S_ISDIR(stb1.st_mode) || S_ISDIR(stb2.st_mode))) + errorx("can't compare - to a directory"); + if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) { + if (format == D_IFDEF) + errorx("-D option not supported with directories"); + set_argstr(oargv, argv); + diffdir(argv[0], argv[1]); + } else + diffreg(argv[0], argv[1], 0); + exit(status); } -__dead void -done(int sig) +void +quit(int signo) { if (tempfiles[0] != NULL) unlink(tempfiles[0]); if (tempfiles[1] != NULL) unlink(tempfiles[1]); - if (sig) - _exit(status); - exit(status); + _exit(status); } void * @@ -237,7 +231,7 @@ error(const char *fmt, ...) unlink(tempfiles[1]); errno = sverrno; va_start(ap, fmt); - verr(status, fmt, ap); + verr(2, fmt, ap); va_end(ap); } @@ -251,10 +245,67 @@ errorx(const char *fmt, ...) if (tempfiles[1] != NULL) unlink(tempfiles[1]); va_start(ap, fmt); - verrx(status, fmt, ap); + verrx(2, fmt, ap); va_end(ap); } +void +set_argstr(char **av, char **ave) +{ + size_t argsize; + char **ap; + + argsize = 4 + (char *)ave - (char *)av + 1; + diffargs = emalloc(argsize); + strlcpy(diffargs, "diff", argsize); + for (ap = av + 1; ap < ave; ap++) { + if (strcmp(*ap, "--") != 0) { + strlcat(diffargs, " ", argsize); + strlcat(diffargs, *ap, argsize); + } + } +} + +/* + * Read in an excludes file and push each line. + */ +void +read_excludes_file(char *file) +{ + FILE *fp; + char *buf, *pattern; + size_t len; + + if (strcmp(file, "-") == 0) + fp = stdin; + else if ((fp = fopen(file, "r")) == NULL) + error("%s", file); + while ((buf = fgetln(fp, &len)) != NULL) { + if (buf[len - 1] == '\n') + len--; + pattern = emalloc(len + 1); + memcpy(pattern, buf, len); + pattern[len] = '\0'; + push_excludes(pattern); + } + if (strcmp(file, "-") != 0) + fclose(fp); +} + +/* + * Push a pattern onto the excludes list. + */ +void +push_excludes(char *pattern) +{ + struct excludes *entry; + + entry = emalloc(sizeof(*entry)); + entry->pattern = pattern; + entry->next = excludes_list; + excludes_list = entry; +} + __dead void usage(void) { @@ -263,8 +314,8 @@ usage(void) " diff [-bitw] -C number file1 file2\n" " diff [-bitw] -D string file1 file2\n" " diff [-bitw] -U number file1 file2\n" - " diff [-biwt] [-c | -e | -f | -n | -u ] " - "[-r] [-s] [-S name]\n dir1 dir2\n"); + " diff [-biNwt] [-c | -e | -f | -n | -u ] [-r] [-s] [-S name]" + " [-X file]\n [-x pattern] dir1 dir2\n"); exit(2); } diff --git a/usr.bin/diff/diff.h b/usr.bin/diff/diff.h index 971c9b3269a..276660f5954 100644 --- a/usr.bin/diff/diff.h +++ b/usr.bin/diff/diff.h @@ -1,53 +1,36 @@ -/* $OpenBSD: diff.h,v 1.14 2003/07/04 17:50:24 millert Exp $ */ +/* $OpenBSD: diff.h,v 1.15 2003/07/06 20:48:59 millert Exp $ */ -/* - * Copyright (C) Caldera International Inc. 2001-2002. - * All rights reserved. +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: - * 1. Redistributions of source code and documentation must retain the above - * copyright notice, this list of conditions and the following disclaimer. + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed or owned by Caldera - * International, Inc. - * 4. Neither the name of Caldera International, Inc. nor the names of other - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. * - * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA - * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT, - * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/* diff.h 4.7 85/08/16 */ - -/* - * diff - common declarations + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)diff.h 8.1 (Berkeley) 6/6/93 */ -#include <sys/param.h> -#include <sys/stat.h> - -#include <ctype.h> -#include <err.h> -#include <signal.h> -#include <stdio.h> - /* * Output format options */ @@ -60,21 +43,31 @@ #define D_NREVERSE 5 /* Reverse ed script with numbered lines and no trailing . */ -extern int aflag, bflag, iflag, rflag, sflag, tflag, wflag; +/* + * Output flags + */ +#define D_HEADER 1 /* Print a header/footer between files */ +#define D_EMPTY1 2 /* Treat first file as empty (/dev/null) */ +#define D_EMPTY2 4 /* Treat second file as empty (/dev/null) */ + +struct excludes { + char *pattern; + struct excludes *next; +}; + +extern int aflag, bflag, iflag, Nflag, rflag, sflag, tflag, wflag; extern char *start, *ifdefname; -extern int opt, wantelses, context, status, anychange; -extern char *tempfiles[], **diffargv; -extern char *file1, *file2, *efile1, *efile2; +extern int format, context, status, anychange; +extern char *tempfiles[], *diffargs; extern struct stat stb1, stb2; +extern struct excludes *excludes_list; +char *copytemp(const char *, int); +char *splice(char *, char *); void *emalloc(size_t); void *erealloc(void *, size_t); -char *splice(char *, char *); -char *copytemp(const char *, int); -void diffdir(char **); -void diffreg(void); -int max(int, int); -int min(int, int); +void diffdir(char *, char *); +void diffreg(char *, char *, int); +void quit(int); __dead void error(const char *, ...); __dead void errorx(const char *, ...); -__dead void done(int); diff --git a/usr.bin/diff/diffdir.c b/usr.bin/diff/diffdir.c index a197b3e357d..6b3ebd01864 100644 --- a/usr.bin/diff/diffdir.c +++ b/usr.bin/diff/diffdir.c @@ -1,365 +1,292 @@ -/* $OpenBSD: diffdir.c,v 1.18 2003/07/06 02:11:12 millert Exp $ */ +/* $OpenBSD: diffdir.c,v 1.19 2003/07/06 20:48:59 millert Exp $ */ /* - * Copyright (C) Caldera International Inc. 2001-2002. - * All rights reserved. + * Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com> * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code and documentation must retain the above - * copyright notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed or owned by Caldera - * International, Inc. - * 4. Neither the name of Caldera International, Inc. nor the names of other - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA - * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT, - * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. */ -#include <sys/types.h> -#include <sys/wait.h> +#ifndef lint +static const char rcsid[] = "$OpenBSD: diffdir.c,v 1.19 2003/07/06 20:48:59 millert Exp $"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> #include <dirent.h> +#include <err.h> #include <errno.h> #include <fcntl.h> +#include <fnmatch.h> +#include <paths.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "diff.h" -#include "pathnames.h" -#if 0 -static const char sccsid[] = "@(#)diffdir.c 4.12 (Berkeley) 4/30/89"; -#endif +static int dircompare(const void *, const void *); +static int excluded(const char *); +static struct dirent **slurpdir(char *, char **); +static void diffit(struct dirent *, char *, size_t, char *, size_t); /* - * diff - directory comparison + * Diff directory traveral. Will be called recursively if -r was specified. */ -#define d_flags d_ino - -#define DIRECT 1 /* Directory */ - -struct dir { - u_long d_ino; - short d_reclen; - short d_namlen; - char *d_entry; -}; +void +diffdir(char *p1, char *p2) +{ + struct dirent **dirp1, **dirp2, **dp1, **dp2; + struct dirent *dent1, *dent2; + size_t dirlen1, dirlen2; + char path1[MAXPATHLEN], path2[MAXPATHLEN]; + char *dirbuf1, *dirbuf2; + int pos; -static int dirstatus; /* exit status from diffdir */ -static char title[2 * BUFSIZ]; + dirlen1 = strlcpy(path1, *p1 ? p1 : ".", sizeof(path1)); + if (dirlen1 >= sizeof(path1) - 1) { + warnx("%s: %s", p1, strerror(ENAMETOOLONG)); + status = 2; + return; + } + if (path1[dirlen1 - 1] != '/') { + path1[dirlen1++] = '/'; + path1[dirlen1] = '\0'; + } + dirlen2 = strlcpy(path2, *p2 ? p2 : ".", sizeof(path2)); + if (dirlen2 >= sizeof(path2) - 1) { + warnx("%s: %s", p2, strerror(ENAMETOOLONG)); + status = 2; + return; + } + if (path2[dirlen2 - 1] != '/') { + path2[dirlen2++] = '/'; + path2[dirlen2] = '\0'; + } + /* get a list of the entries in each directory */ + dp1 = dirp1 = slurpdir(path1, &dirbuf1); + dp2 = dirp2 = slurpdir(path2, &dirbuf2); + if (dirp1 == NULL || dirp2 == NULL) + return; -static struct dir *setupdir(char *); -static int ascii(int); -static void compare(struct dir *); -static void calldiff(void); -static void setfile(char **fpp, char **epp, char *file); -static int useless(char *); -static void only(struct dir *dp, int which); -static int entcmp(const void *, const void *); + /* + * If we were given a starting point, find it. + */ + if (start != NULL) { + while (*dp1 != NULL && strcmp((*dp1)->d_name, start) < 0) + dp1++; + while (*dp2 != NULL && strcmp((*dp2)->d_name, start) < 0) + dp2++; + } -void -diffdir(char **argv) -{ - struct dir *dir1, *dir2; - struct dir *d1, *d2; - int i, cmp; + /* + * Iterate through the two directory lists, diffing as we go. + */ + while (*dp1 != NULL || *dp2 != NULL) { + dent1 = *dp1; + dent2 = *dp2; - if (opt == D_IFDEF) - warnx("can't specify -I with directories"); - if (opt == D_EDIT && sflag) - warnx("warning: shouldn't give -s with -e"); - strlcpy(title, "diff ", sizeof title); - for (i = 1; diffargv[i + 2]; i++) { - if (!strcmp(diffargv[i], "-")) - continue; /* was -S, dont look silly */ - strlcat(title, diffargv[i], sizeof title); - strlcat(title, " ", sizeof title); - } - setfile(&file1, &efile1, file1); - setfile(&file2, &efile2, file2); - argv[0] = file1; - argv[1] = file2; - dir1 = setupdir(file1); - dir2 = setupdir(file2); - d1 = dir1; - d2 = dir2; - while (d1->d_entry != 0 || d2->d_entry != 0) { - if (d1->d_entry && useless(d1->d_entry)) { - d1++; - continue; - } - if (d2->d_entry && useless(d2->d_entry)) { - d2++; - continue; - } - if (d1->d_entry == 0) - cmp = 1; - else if (d2->d_entry == 0) - cmp = -1; - else - cmp = strcmp(d1->d_entry, d2->d_entry); - if (cmp < 0) { - if (opt == D_NORMAL || opt == D_CONTEXT || - opt == D_UNIFIED) - only(d1, 1); - d1++; - dirstatus |= 1; - } else if (cmp == 0) { - compare(d1); - d1++; - d2++; + pos = dent1 == NULL ? 1 : dent2 == NULL ? -1 : + strcmp(dent1->d_name, dent2->d_name); + if (pos == 0) { + /* file exists in both dirs, diff it */ + diffit(dent1, path1, dirlen1, path2, dirlen2); + dp1++; + dp2++; + } else if (pos < 0) { + /* file only in first dir, only diff if -N */ + if (Nflag) + diffit(dent1, path1, dirlen1, path2, dirlen2); + else if (format == D_NORMAL || format == D_CONTEXT || + format == D_UNIFIED) + /* XXX GNU diff always prints this XXX */ + printf("Only in %.*s: %s\n", (int)(dirlen1 - 1), + path1, dent1->d_name); + dp1++; } else { - if (opt == D_NORMAL || opt == D_CONTEXT || - opt == D_UNIFIED) - only(d2, 2); - d2++; - dirstatus |= 1; + /* file only in second dir, only diff if -N */ + if (Nflag) + diffit(dent2, path1, dirlen1, path2, dirlen2); + else if (format == D_NORMAL || format == D_CONTEXT || + format == D_UNIFIED) + /* XXX GNU diff always prints this XXX */ + printf("Only in %.*s: %s\n", (int)(dirlen2 - 1), + path2, dent2->d_name); + dp2++; } } - if (rflag) { - for (d1 = dir1; d1->d_entry; d1++) { - if ((d1->d_flags & DIRECT) == 0) - continue; - strlcpy(efile1, d1->d_entry, - file1 + MAXPATHLEN - efile1); - strlcpy(efile2, d1->d_entry, - file2 + MAXPATHLEN - efile2); - calldiff(); - } + + if (dirbuf1 != NULL) { + free(dirp1); + free(dirbuf1); + } + if (dirbuf2 != NULL) { + free(dirp2); + free(dirbuf2); } - status = dirstatus; } -void -setfile(char **fpp, char **epp, char *file) +/* + * Read in a whole directory's worth of struct dirents, culling + * out the "excluded" ones. + * Returns an array of struct dirent *'s that point into the buffer + * returned via bufp. Caller is responsible for free()ing both of these. + */ +static struct dirent ** +slurpdir(char *path, char **bufp) { - char *cp; - size_t len; + char *buf, *ebuf, *cp; + size_t bufsize; + long base; + int fd, nbytes, entries; + struct stat sb; + struct dirent **dirlist, *dp; - if (*file == '\0') - file = "."; - *fpp = emalloc(MAXPATHLEN); - len = strlcpy(*fpp, file, MAXPATHLEN); - if (len >= MAXPATHLEN - 1) - errorx("%s: %s", file, strerror(ENAMETOOLONG)); - cp = *fpp + len - 1; - if (*cp == '/') - ++cp; - else { - *++cp = '/'; - *++cp = '\0'; - } - *epp = cp; -} + *bufp = NULL; + if ((fd = open(path, O_RDONLY, 0644)) == -1) { + static struct dirent *dummy; -void -only(struct dir *dp, int which) -{ - char *file = which == 1 ? file1 : file2; - char *efile = which == 1 ? efile1 : efile2; + if (!Nflag) { + warn("%s", path); + return (NULL); + } + return (&dummy); + } + fstat(fd, &sb); - printf("Only in %.*s: %s\n", (int)(efile - file - 1), file, dp->d_entry); -} + bufsize = sb.st_size; + if (bufsize < sb.st_blksize) + bufsize = sb.st_blksize; + buf = emalloc(bufsize); -struct dir * -setupdir(char *cp) -{ - struct dir *dp, *ep; - struct dirent *rp; - int nitems; - DIR *dirp; + nbytes = getdirentries(fd, buf, bufsize, &base); + if (nbytes <= 0) { + free(buf); + warn("%s", path); + return (NULL); + } + ebuf = buf + nbytes; + close(fd); - dirp = opendir(cp); - if (dirp == NULL) - error("%s", cp); - nitems = 0; - dp = emalloc(sizeof(struct dir)); - while ((rp = readdir(dirp))) { - ep = &dp[nitems++]; - ep->d_reclen = rp->d_reclen; - ep->d_namlen = rp->d_namlen; - ep->d_entry = 0; - ep->d_flags = 0; - if (ep->d_namlen > 0) { - ep->d_entry = emalloc(ep->d_namlen + 1); - strlcpy(ep->d_entry, rp->d_name, ep->d_namlen + 1); - } - dp = erealloc(dp, (nitems + 1) * sizeof(struct dir)); + /* + * We now have all the directory entries in our buffer. + * However, in order to easily sort them we need to convert + * the buffer into an array. + */ + for (entries = 0, cp = buf; cp < ebuf; ) { + dp = (struct dirent *)cp; + if (dp->d_fileno != 0 && dp->d_type != DT_WHT) + entries++; + if (dp->d_reclen <= 0) + break; + cp += dp->d_reclen; + } + dirlist = emalloc(sizeof(struct dirent *) * (entries + 1)); + for (entries = 0, cp = buf; cp < ebuf; ) { + dp = (struct dirent *)cp; + if (dp->d_fileno != 0 && dp->d_type != DT_WHT && + !excluded(dp->d_name)) + dirlist[entries++] = dp; + if (dp->d_reclen <= 0) + break; + cp += dp->d_reclen; } - dp[nitems].d_entry = 0; /* delimiter */ - closedir(dirp); - qsort(dp, nitems, sizeof(struct dir), entcmp); - return (dp); + dirlist[entries] = NULL; + + qsort(dirlist, entries, sizeof(struct dir *), dircompare); + + *bufp = buf; + return (dirlist); } +/* + * Compare d_name in two dirent structures; for qsort(3). + */ static int -entcmp(const void *v1, const void *v2) +dircompare(const void *vp1, const void *vp2) { - const struct dir *d1, *d2; + struct dirent *dp1 = *((struct dirent **) vp1); + struct dirent *dp2 = *((struct dirent **) vp2); - d1 = v1; - d2 = v2; - return (strcmp(d1->d_entry, d2->d_entry)); + return (strcmp(dp1->d_name, dp2->d_name)); } +/* + * Do the actual diff by calling either diffreg() or diffdir(). + */ static void -compare(struct dir *dp) +diffit(struct dirent *dp, char *path1, size_t plen1, char *path2, size_t plen2) { - char buf1[BUFSIZ], buf2[BUFSIZ]; - int i, j, f1, f2, fmt1, fmt2; - struct stat stb1, stb2; + int flags = D_HEADER; - strlcpy(efile1, dp->d_entry, file1 + MAXPATHLEN - efile1); - strlcpy(efile2, dp->d_entry, file2 + MAXPATHLEN - efile2); - f1 = open(file1, 0); - if (f1 < 0) { - warn("%s", file1); - return; - } - f2 = open(file2, 0); - if (f2 < 0) { - warn("%s", file2); - close(f1); - return; - } - fstat(f1, &stb1); - fstat(f2, &stb2); - fmt1 = stb1.st_mode & S_IFMT; - fmt2 = stb2.st_mode & S_IFMT; - if (fmt1 != S_IFREG || fmt2 != S_IFREG) { - if (fmt1 == fmt2) { - if (fmt1 != S_IFDIR && stb1.st_rdev == stb2.st_rdev) - goto same; - if (fmt1 == S_IFDIR) { - dp->d_flags = DIRECT; - if (opt == D_EDIT) - goto closem; - printf("Common subdirectories: %s and %s\n", - file1, file2); - goto closem; - } + strlcpy(path1 + plen1, dp->d_name, MAXPATHLEN - plen1); + if (stat(path1, &stb1) != 0) { + if (!Nflag || errno != ENOENT) { + warn("%s", path1); + return; } - goto notsame; - } - if (stb1.st_size != stb2.st_size) - goto notsame; - for (;;) { - i = read(f1, buf1, BUFSIZ); - j = read(f2, buf2, BUFSIZ); - if (i < 0 || j < 0 || i != j) - goto notsame; - if (i == 0 && j == 0) - goto same; - for (j = 0; j < i; j++) - if (buf1[j] != buf2[j]) - goto notsame; - } -same: - if (sflag != 0) - printf("Files %s and %s are identical\n", file1, file2); - goto closem; -notsame: - dirstatus |= 1; - if (!ascii(f1) || !ascii(f2)) { - if (opt == D_NORMAL || opt == D_CONTEXT || opt == D_UNIFIED) - printf("Binary files %s and %s differ\n", - file1, file2); - goto closem; + flags |= D_EMPTY1; + memset(&stb1, 0, sizeof(stb1)); } - close(f1); - close(f2); - anychange = 1; - if (opt == D_EDIT) { - printf("ed - %s << '-*-END-*-'\n", dp->d_entry); - calldiff(); - } else { - printf("%s%s %s\n", title, file1, file2); - calldiff(); - } - if (opt == D_EDIT) - printf("w\nq\n-*-END-*-\n"); - return; -closem: - close(f1); - close(f2); -} -static void -calldiff(void) -{ - int lstatus; - pid_t pid; + strlcpy(path2 + plen2, dp->d_name, MAXPATHLEN - plen2); + if (stat(path2, &stb2) != 0) { + if (!Nflag || errno != ENOENT) { + warn("%s", path2); + return; + } + flags |= D_EMPTY2; + memset(&stb2, 0, sizeof(stb2)); + stb2.st_mode = stb1.st_mode; + } + if (stb1.st_mode == 0) + stb1.st_mode = stb2.st_mode; - fflush(stdout); - pid = fork(); - if (pid == -1) - errorx("No more processes"); - if (pid == 0) { - execv(_PATH_DIFF, diffargv); - error("%s", _PATH_DIFF); + if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) { + /* XXX GNU diff always prints this for dirs XXX */ + if (format != D_EDIT) + printf("Common subdirectories: %s and %s\n", + path1, path2); + if (rflag) + diffdir(path1, path2); + return; } - while (wait(&lstatus) != pid) - continue; - /* - if ((lstatus >> 8) >= 2) - done(0); - */ - dirstatus |= lstatus >> 8; + diffreg(path1, path2, flags); } -int -ascii(int f) +/* + * Exclude the given directory entry? + */ +static int +excluded(const char *entry) { - char buf[BUFSIZ], *cp; - int cnt; + struct excludes *excl; - if (aflag) + /* always skip "." and ".." */ + if (entry[0] == '.' && + (entry[1] == '\0' || (entry[1] == '.' && entry[2] == '\0'))) return (1); - lseek(f, (off_t)0, SEEK_SET); - cnt = read(f, buf, BUFSIZ); - cp = buf; - while (--cnt >= 0) - if (*cp++ & 0200) - return (0); - return (1); -} + /* check excludes list */ + for (excl = excludes_list; excl != NULL; excl = excl->next) + if (fnmatch(excl->pattern, entry, FNM_PATHNAME) == 0) + return (1); -/* - * THIS IS CRUDE. - */ -int -useless(char *cp) -{ - if (cp[0] == '.') { - if (cp[1] == '\0') - return (1); /* directory "." */ - if (cp[1] == '.' && cp[2] == '\0') - return (1); /* directory ".." */ - } - if (start && strcmp(start, cp) > 0) - return (1); return (0); } diff --git a/usr.bin/diff/diffreg.c b/usr.bin/diff/diffreg.c index c8dbc50c5d5..3a5852125e5 100644 --- a/usr.bin/diff/diffreg.c +++ b/usr.bin/diff/diffreg.c @@ -1,4 +1,4 @@ -/* $OpenBSD: diffreg.c,v 1.26 2003/07/04 17:37:07 millert Exp $ */ +/* $OpenBSD: diffreg.c,v 1.27 2003/07/06 20:48:59 millert Exp $ */ /* * Copyright (C) Caldera International Inc. 2001-2002. @@ -33,20 +33,56 @@ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)diffreg.c 8.1 (Berkeley) 6/6/93 + */ + +#ifndef lint +static const char rcsid[] = "$OpenBSD: diffreg.c,v 1.27 2003/07/06 20:48:59 millert Exp $"; +#endif /* not lint */ #include <sys/types.h> +#include <sys/stat.h> -#include <stdlib.h> -#include <unistd.h> +#include <ctype.h> +#include <err.h> #include <fcntl.h> +#include <libgen.h> +#include <paths.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> #include <string.h> +#include <unistd.h> #include "diff.h" -#include "pathnames.h" - -#if 0 -static char const sccsid[] = "@(#)diffreg.c 4.21 4/6/90"; -#endif /* * diff - compare two files. @@ -115,10 +151,6 @@ static char const sccsid[] = "@(#)diffreg.c 4.21 4/6/90"; * 6n words for files of length n. */ -#define prints(s) fputs(s,stdout) - -FILE *input[2]; - struct cand { int x; int y; @@ -130,40 +162,42 @@ struct line { int value; } *file[2]; -int len[2]; -struct line *sfile[2]; /* shortened by pruning common prefix and suffix */ -int slen[2]; -int pref, suff; /* length of prefix and suffix */ -int inifdef; /* whether or not we are in a #ifdef block */ -int *class; /* will be overlaid on file[0] */ -int *member; /* will be overlaid on file[1] */ -int *klist; /* will be overlaid on file[0] after class */ -struct cand *clist; /* merely a free storage pot for candidates */ -int clen = 0; -int *J; /* will be overlaid on class */ -long *ixold; /* will be overlaid on klist */ -long *ixnew; /* will be overlaid on file[1] */ -u_char *chrtran; /* translation table for case-folding */ +static int *J; /* will be overlaid on class */ +static int *class; /* will be overlaid on file[0] */ +static int *klist; /* will be overlaid on file[0] after class */ +static int *member; /* will be overlaid on file[1] */ +static int clen; +static int inifdef; /* whether or not we are in a #ifdef block */ +static int len[2]; +static int pref, suff; /* length of prefix and suffix */ +static int slen[2]; +static int anychange; +static long *ixnew; /* will be overlaid on file[1] */ +static long *ixold; /* will be overlaid on klist */ +static struct cand *clist; /* merely a free storage pot for candidates */ +static struct line *sfile[2]; /* shortened by pruning common prefix/suffix */ +static u_char *chrtran; /* translation table for case-folding */ static void fetch(long *, int, int, FILE *, char *, int); -static void output(void); -static void check(void); +static void output(char *, FILE *, char *, FILE *); +static void check(char *, FILE *, char *, FILE *); static void range(int, int, char *); -static void dump_context_vec(void); -static void dump_unified_vec(void); +static void dump_context_vec(FILE *, FILE *); +static void dump_unified_vec(FILE *, FILE *); static void prepare(int, FILE *); static void prune(void); static void equiv(struct line *, int, struct line *, int, int *); static void unravel(int); static void unsort(struct line *, int, int *); -static void change(int, int, int, int); +static void change(char *, FILE *, char *, FILE *, int, int, int, int); static void sort(struct line *, int); -static int newcand(int, int, int); -static int search(int *, int, int); -static int skipline(int); -static int asciifile(FILE *); -static int stone(int *, int, int *, int *); -static int readhash(FILE *); +static int asciifile(FILE *); +static int newcand(int, int, int); +static int search(int *, int, int); +static int skipline(FILE *); +static int stone(int *, int, int *, int *); +static int readhash(FILE *); +static int files_differ(FILE *, FILE *, int); /* * chrtran points to one of 2 translation tables: cup2low if folding upper to @@ -224,70 +258,100 @@ u_char cup2low[256] = { }; void -diffreg(void) +diffreg(char *ofile1, char *ofile2, int flags) { - char buf1[BUFSIZ], buf2[BUFSIZ]; - FILE *f1, *f2; - int i, j; + char *file1 = ofile1; + char *file2 = ofile2; + FILE *f1 = NULL; + FILE *f2 = NULL; + int i; + anychange = 0; chrtran = (iflag ? cup2low : clow2low); if (strcmp(file1, "-") == 0 && strcmp(file2, "-") == 0) - errorx("can't specify - -"); - if (S_ISDIR(stb1.st_mode)) { - file1 = splice(file1, file2); - if (stat(file1, &stb1) < 0) - error("%s", file1); - } else if (strcmp(file1, "-") == 0 || - (!S_ISREG(stb1.st_mode) && strcmp(file1, _PATH_DEVNULL) != 0)) { - file1 = copytemp(file1, 1); - if (stat(file1, &stb1) < 0) - error("%s", file1); + goto notsame; + + /* XXX - only make temp file for stdin if not seekable? (millert) */ + if (flags & D_EMPTY1) + f1 = fopen(_PATH_DEVNULL, "r"); + else { + if (S_ISDIR(stb1.st_mode)) { + file1 = splice(file1, file2); + if (stat(file1, &stb1) < 0) { + warn("%s", file1); + status |= 2; + goto closem; + } + } else if (strcmp(file1, "-") == 0 || !S_ISREG(stb1.st_mode)) { + file1 = copytemp(file1, 1); + if (file1 == NULL || stat(file1, &stb1) < 0) { + warn("%s", file1); + status |= 2; + goto closem; + } + } + f1 = fopen(file1, "r"); } - if (S_ISDIR(stb2.st_mode)) { - file2 = splice(file2, file1); - if (stat(file2, &stb2) < 0) - error("%s", file2); - } else if (strcmp(file2, "-") == 0 || - (!S_ISREG(stb2.st_mode) && strcmp(file2, _PATH_DEVNULL) != 0)) { - file2 = copytemp(file2, 2); - if (stat(file2, &stb2) < 0) - error("%s", file2); + if (f1 == NULL) { + warn("%s", file1); + status |= 2; + goto closem; } - if ((f1 = fopen(file1, "r")) == NULL) - error("%s", file1); - if ((f2 = fopen(file2, "r")) == NULL) - error("%s", file2); - if ((stb1.st_mode & S_IFMT) != (stb2.st_mode & S_IFMT) || - stb1.st_size != stb2.st_size) - goto notsame; - for (;;) { - i = fread(buf1, 1, BUFSIZ, f1); - j = fread(buf2, 1, BUFSIZ, f2); - if (i < 0 || j < 0 || i != j) - goto notsame; - if (i == 0 && j == 0) { - fclose(f1); - fclose(f2); - status = 0; /* files don't differ */ - goto same; + + if (flags & D_EMPTY2) + f2 = fopen(_PATH_DEVNULL, "r"); + else { + if (S_ISDIR(stb2.st_mode)) { + file2 = splice(file2, file1); + if (stat(file2, &stb2) < 0) { + warn("%s", file2); + status |= 2; + goto closem; + } + } else if (strcmp(file2, "-") == 0 || !S_ISREG(stb2.st_mode)) { + file2 = copytemp(file2, 2); + if (file2 == NULL || stat(file2, &stb2) < 0) { + warn("%s", file2); + status |= 2; + goto closem; + } } - for (j = 0; j < i; j++) - if (buf1[j] != buf2[j]) - goto notsame; + f2 = fopen(file2, "r"); + } + if (f2 == NULL) { + warn("%s", file2); + status |= 2; + goto closem; } + + switch (files_differ(f1, f2, flags)) { + case 0: + goto same; + case 1: + break; + default: + /* error */ + status |= 2; + goto closem; + } + notsame: /* * Files certainly differ at this point; set status accordingly */ - status = 1; + status |= 1; + if (flags & D_HEADER) { + if (format == D_EDIT) + printf("ed - %s << '-*-END-*-'\n", basename(file1)); + else + printf("%s %s %s\n", diffargs, file1, file2); + } if (!asciifile(f1) || !asciifile(f2)) { printf("Binary files %s and %s differ\n", file1, file2); - exit(status); + goto closem; } prepare(0, f1); prepare(1, f2); - fclose(f1); - fclose(f2); prune(); sort(sfile[0], slen[0]); sort(sfile[1], slen[1]); @@ -306,23 +370,74 @@ notsame: free(member); free(class); - J = emalloc((len[0] + 2) * sizeof(int)); + J = erealloc(J, (len[0] + 2) * sizeof(int)); unravel(klist[i]); free(clist); free(klist); - ixold = emalloc((len[0] + 2) * sizeof(long)); - ixnew = emalloc((len[1] + 2) * sizeof(long)); - check(); - output(); - status = anychange; + ixold = erealloc(ixold, (len[0] + 2) * sizeof(long)); + ixnew = erealloc(ixnew, (len[1] + 2) * sizeof(long)); + check(file1, f1, file2, f2); + output(file1, f1, file2, f2); + if ((flags & D_HEADER) && format == D_EDIT) + printf("w\nq\n-*-END-*-\n"); same: - if (anychange == 0 && (opt == D_CONTEXT || opt == D_UNIFIED)) - printf("No differences encountered\n"); + if (anychange == 0 && sflag != 0) + printf("Files %s and %s are identical\n", file1, file2); + +closem: + if (f1 != NULL) + fclose(f1); + if (f2 != NULL) + fclose(f2); + if (tempfiles[0] != NULL) { + unlink(tempfiles[0]); + free(tempfiles[0]); + tempfiles[0] = NULL; + } + if (tempfiles[1] != NULL) { + unlink(tempfiles[1]); + free(tempfiles[1]); + tempfiles[1] = NULL; + } + if (file1 != ofile1) + free(file1); + if (file2 != ofile2) + free(file2); +} + +/* + * Check to see if the given files differ. + * Returns 0 if they are the same, 1 if different, and -1 on error. + * XXX - could use code from cmp(1) [faster] + */ +static int +files_differ(FILE *f1, FILE *f2, int flags) +{ + char buf1[BUFSIZ], buf2[BUFSIZ]; + size_t i, j; + + if ((flags & (D_EMPTY1|D_EMPTY2)) || stb1.st_size != stb2.st_size || + (stb1.st_mode & S_IFMT) != (stb2.st_mode & S_IFMT)) + return (1); + for (;;) { + i = fread(buf1, 1, sizeof(buf1), f1); + j = fread(buf2, 1, sizeof(buf2), f2); + if (i != j) + return (1); + if (i == 0 && j == 0) { + if (ferror(f1) || ferror(f2)) + return (1); + return (0); + } + if (memcmp(buf1, buf2, i) != 0) + return (1); + } } char *tempfiles[2]; +/* XXX - pass back a FILE * too (millert) */ char * copytemp(const char *file, int n) { @@ -335,24 +450,24 @@ copytemp(const char *file, int n) if (strcmp(file, "-") == 0) ifd = STDIN_FILENO; else if ((ifd = open(file, O_RDONLY, 0644)) < 0) - error("%s", file); + return (NULL); if ((tempdir = getenv("TMPDIR")) == NULL) tempdir = _PATH_TMP; if (asprintf(&tempfile, "%s/diff%d.XXXXXXXX", tempdir, n) == -1) - error(NULL); + return (NULL); tempfiles[n - 1] = tempfile; - signal(SIGHUP, done); - signal(SIGINT, done); - signal(SIGPIPE, done); - signal(SIGTERM, done); + signal(SIGHUP, quit); + signal(SIGINT, quit); + signal(SIGPIPE, quit); + signal(SIGTERM, quit); ofd = mkstemp(tempfile); if (ofd < 0) - error("%s", tempfile); + return (NULL); while ((i = read(ifd, buf, BUFSIZ)) > 0) { if (write(ofd, buf, i) != i) - error("%s", tempfile); + return (NULL); } close(ifd); close(ofd); @@ -365,8 +480,6 @@ splice(char *dir, char *file) char *tail, *buf; size_t len; - if (!strcmp(file, "-")) - errorx("can't specify - with other arg directory"); tail = strrchr(file, '/'); if (tail == NULL) tail = file; @@ -384,7 +497,7 @@ prepare(int i, FILE *fd) struct line *p; int j, h; - fseek(fd, 0L, SEEK_SET); + rewind(fd); p = emalloc(3 * sizeof(struct line)); for (j = 0; (h = readhash(fd));) { p = erealloc(p, (++j + 3) * sizeof(struct line)); @@ -538,32 +651,30 @@ unravel(int p) * 2. collect random access indexes to the two files */ static void -check(void) +check(char *file1, FILE *f1, char *file2, FILE *f2) { int i, j, jackpot, c, d; long ctold, ctnew; - if ((input[0] = fopen(file1, "r")) == NULL) - error("%s", file1); - if ((input[1] = fopen(file2, "r")) == NULL) - error("%s", file2); + rewind(f1); + rewind(f2); j = 1; ixold[0] = ixnew[0] = 0; jackpot = 0; ctold = ctnew = 0; for (i = 1; i <= len[0]; i++) { if (J[i] == 0) { - ixold[i] = ctold += skipline(0); + ixold[i] = ctold += skipline(f1); continue; } while (j < J[i]) { - ixnew[j] = ctnew += skipline(1); + ixnew[j] = ctnew += skipline(f2); j++; } if (bflag || wflag || iflag) { for (;;) { - c = getc(input[0]); - d = getc(input[1]); + c = getc(f1); + d = getc(f2); ctold++; ctnew++; if (bflag && isspace(c) && isspace(d)) { @@ -571,19 +682,19 @@ check(void) if (c == '\n') break; ctold++; - } while (isspace(c = getc(input[0]))); + } while (isspace(c = getc(f1))); do { if (d == '\n') break; ctnew++; - } while (isspace(d = getc(input[1]))); + } while (isspace(d = getc(f2))); } else if (wflag) { while (isspace(c) && c != '\n') { - c = getc(input[0]); + c = getc(f1); ctold++; } while (isspace(d) && d != '\n') { - d = getc(input[1]); + d = getc(f2); ctnew++; } } @@ -591,9 +702,9 @@ check(void) jackpot++; J[i] = 0; if (c != '\n') - ctold += skipline(0); + ctold += skipline(f1); if (d != '\n') - ctnew += skipline(1); + ctnew += skipline(f2); break; } if (c == '\n') @@ -603,13 +714,13 @@ check(void) for (;;) { ctold++; ctnew++; - if ((c = getc(input[0])) != (d = getc(input[1]))) { + if ((c = getc(f1)) != (d = getc(f2))) { /* jackpot++; */ J[i] = 0; if (c != '\n') - ctold += skipline(0); + ctold += skipline(f1); if (d != '\n') - ctnew += skipline(1); + ctnew += skipline(f2); break; } if (c == '\n') @@ -620,11 +731,8 @@ check(void) ixnew[j] = ctnew; j++; } - for (; j <= len[1]; j++) { - ixnew[j] = ctnew += skipline(1); - } - fclose(input[0]); - fclose(input[1]); + for (; j <= len[1]; j++) + ixnew[j] = ctnew += skipline(f2); /* * if (jackpot) * fprintf(stderr, "jackpot\n"); @@ -678,27 +786,27 @@ unsort(struct line *f, int l, int *b) } static int -skipline(int f) +skipline(FILE *f) { int i, c; - for (i = 1; (c = getc(input[f])) != '\n'; i++) + for (i = 1; (c = getc(f)) != '\n'; i++) if (c < 0) return (i); return (i); } static void -output(void) +output(char *file1, FILE *f1, char *file2, FILE *f2) { int m, i0, i1, j0, j1; - input[0] = fopen(file1, "r"); - input[1] = fopen(file2, "r"); + rewind(f1); + rewind(f2); m = len[0]; J[0] = 0; J[m + 1] = len[1] + 1; - if (opt != D_EDIT) { + if (format != D_EDIT) { for (i0 = 1; i0 <= m; i0 = i1 + 1) { while (i0 <= m && J[i0] == J[i0 - 1] + 1) i0++; @@ -708,7 +816,7 @@ output(void) i1++; j1 = J[i1 + 1] - 1; J[i1] = j1; - change(i0, i1, j0, j1); + change(file1, f1, file2, f2, i0, i1, j0, j1); } } else { for (i0 = m; i0 >= 1; i0 = i1 - 1) { @@ -720,15 +828,15 @@ output(void) i1--; j1 = J[i1 - 1] + 1; J[i1] = j1; - change(i1, i0, j1, j0); + change(file1, f1, file2, f2, i1, i0, j1, j0); } } if (m == 0) - change(1, 0, 1, len[1]); - if (opt == D_IFDEF) { + change(file1, f1, file2, f2, 1, 0, 1, len[1]); + if (format == D_IFDEF) { for (;;) { #define c i0 - c = getc(input[0]); + c = getc(f1); if (c < 0) return; putchar(c); @@ -736,17 +844,17 @@ output(void) #undef c } if (anychange != 0) { - if (opt == D_CONTEXT) - dump_context_vec(); - else if (opt == D_UNIFIED) - dump_unified_vec(); + if (format == D_CONTEXT) + dump_context_vec(f1, f2); + else if (format == D_UNIFIED) + dump_unified_vec(f1, f2); } } /* * The following struct is used to record change information when - * doing a "context" diff. (see routine "change" to understand the - * highly mneumonic field names) + * doing a "context" or "unified" diff. (see routine "change" to + * understand the highly mnemonic field names) */ struct context_vec { int a; /* start line in old file */ @@ -760,35 +868,32 @@ struct context_vec *context_vec_start, *context_vec_end, *context_vec_ptr; #define MAX_CONTEXT 128 /* - * indicate that there is a difference between lines a and b of the from file - * to get to lines c to d of the to file. If a is greater then b then there + * Indicate that there is a difference between lines a and b of the from file + * to get to lines c to d of the to file. If a is greater then b then there * are no lines in the from file involved and this means that there were - * lines appended (beginning at b). If c is greater than d then there are + * lines appended (beginning at b). If c is greater than d then there are * lines missing from the to file. */ static void -change(int a, int b, int c, int d) +change(char *file1, FILE *f1, char *file2, FILE *f2, int a, int b, int c, int d) { - struct stat stbuf; - - if (opt != D_IFDEF && a > b && c > d) + if (format != D_IFDEF && a > b && c > d) return; if (anychange == 0) { anychange = 1; - if (opt == D_CONTEXT || opt == D_UNIFIED) { - stat(file1, &stbuf); - printf("%s %s %s", opt == D_CONTEXT ? "***" : "---", - file1, ctime(&stbuf.st_mtime)); - stat(file2, &stbuf); - printf("%s %s %s", opt == D_CONTEXT ? "---" : "+++", - file2, ctime(&stbuf.st_mtime)); - context_vec_start = emalloc(MAX_CONTEXT * - sizeof(struct context_vec)); + if (format == D_CONTEXT || format == D_UNIFIED) { + printf("%s %s %s", format == D_CONTEXT ? "***" : "---", + file1, ctime(&stb1.st_mtime)); + printf("%s %s %s", format == D_CONTEXT ? "---" : "+++", + file2, ctime(&stb2.st_mtime)); + if (context_vec_start == NULL) + context_vec_start = emalloc(MAX_CONTEXT * + sizeof(struct context_vec)); context_vec_end = context_vec_start + MAX_CONTEXT; context_vec_ptr = context_vec_start - 1; } } - if (opt == D_CONTEXT || opt == D_UNIFIED) { + if (format == D_CONTEXT || format == D_UNIFIED) { /* * If this new change is within 'context' lines of * the previous change, just add it to the change @@ -800,10 +905,10 @@ change(int a, int b, int c, int d) (context_vec_ptr >= context_vec_start && a > (context_vec_ptr->b + 2 * context) && c > (context_vec_ptr->d + 2 * context))) { - if (opt == D_CONTEXT) - dump_context_vec(); + if (format == D_CONTEXT) + dump_context_vec(f1, f2); else - dump_unified_vec(); + dump_unified_vec(f1, f2); } context_vec_ptr++; context_vec_ptr->a = a; @@ -812,13 +917,13 @@ change(int a, int b, int c, int d) context_vec_ptr->d = d; return; } - switch (opt) { + switch (format) { case D_NORMAL: case D_EDIT: range(a, b, ","); putchar(a > b ? 'a' : c > d ? 'd' : 'c'); - if (opt == D_NORMAL) + if (format == D_NORMAL) range(c, d, ","); putchar('\n'); break; @@ -838,14 +943,14 @@ change(int a, int b, int c, int d) } break; } - if (opt == D_NORMAL || opt == D_IFDEF) { - fetch(ixold, a, b, input[0], "< ", 1); - if (a <= b && c <= d && opt == D_NORMAL) - prints("---\n"); + if (format == D_NORMAL || format == D_IFDEF) { + fetch(ixold, a, b, f1, "< ", 1); + if (a <= b && c <= d && format == D_NORMAL) + puts("---"); } - fetch(ixnew, c, d, input[1], opt == D_NORMAL ? "> " : "", 0); - if ((opt == D_EDIT || opt == D_REVERSE) && c <= d) - prints(".\n"); + fetch(ixnew, c, d, f2, format == D_NORMAL ? "> " : "", 0); + if ((format == D_EDIT || format == D_REVERSE) && c <= d) + puts("."); if (inifdef) { fprintf(stdout, "#endif /* %s */\n", ifdefname); inifdef = 0; @@ -869,7 +974,7 @@ fetch(long *f, int a, int b, FILE *lb, char *s, int oldfile) * When doing #ifdef's, copy down to current line * if this is the first file, so that stuff makes it to output. */ - if (opt == D_IFDEF && oldfile) { + if (format == D_IFDEF && oldfile) { long curpos = ftell(lb); /* print through if append (a>b), else to (nb: 0 vs 1 orig) */ nc = f[a > b ? b : a - 1] - curpos; @@ -878,7 +983,7 @@ fetch(long *f, int a, int b, FILE *lb, char *s, int oldfile) } if (a > b) return; - if (opt == D_IFDEF) { + if (format == D_IFDEF) { if (inifdef) { fprintf(stdout, "#else /* %s%s */\n", oldfile == 1 ? "!" : "", ifdefname); @@ -893,8 +998,8 @@ fetch(long *f, int a, int b, FILE *lb, char *s, int oldfile) for (i = a; i <= b; i++) { fseek(lb, f[i - 1], SEEK_SET); nc = f[i] - f[i - 1]; - if (opt != D_IFDEF) - prints(s); + if (format != D_IFDEF) + fputs(s, stdout); col = 0; for (j = 0; j < nc; j++) { c = getc(lb); @@ -984,17 +1089,17 @@ readhash(FILE *f) return ((short) low(sum) + (short) high(sum)); } -static int +int asciifile(FILE *f) { char buf[BUFSIZ], *cp; int cnt; - if (aflag) + if (aflag || f == NULL) return (1); - fseek(f, 0L, SEEK_SET); - cnt = fread(buf, 1, BUFSIZ, f); + rewind(f); + cnt = fread(buf, 1, sizeof(buf), f); cp = buf; while (--cnt >= 0) if (*cp++ & 0200) @@ -1002,9 +1107,19 @@ asciifile(FILE *f) return (1); } +static __inline int min(int a, int b) +{ + return (a < b ? a : b); +} + +static __inline int max(int a, int b) +{ + return (a > b ? a : b); +} + /* dump accumulated "context" diff changes */ static void -dump_context_vec(void) +dump_context_vec(FILE *f1, FILE *f2) { struct context_vec *cvp = context_vec_start; int lowa, upb, lowc, upd, do_output; @@ -1025,7 +1140,7 @@ dump_context_vec(void) printf(" ****\n"); /* - * output changes to the "old" file. The first loop suppresses + * Output changes to the "old" file. The first loop suppresses * output if there were no changes to the "old" file (we'll see * the "old" lines as context in the "new" list). */ @@ -1049,16 +1164,16 @@ dump_context_vec(void) ch = (a <= b) ? 'd' : 'a'; if (ch == 'a') - fetch(ixold, lowa, b, input[0], " ", 0); + fetch(ixold, lowa, b, f1, " ", 0); else { - fetch(ixold, lowa, a - 1, input[0], " ", 0); - fetch(ixold, a, b, input[0], + fetch(ixold, lowa, a - 1, f1, " ", 0); + fetch(ixold, a, b, f1, ch == 'c' ? "! " : "- ", 0); } lowa = b + 1; cvp++; } - fetch(ixold, b + 1, upb, input[0], " ", 0); + fetch(ixold, b + 1, upb, f1, " ", 0); } /* output changes to the "new" file */ printf("--- "); @@ -1085,23 +1200,23 @@ dump_context_vec(void) ch = (a <= b) ? 'd' : 'a'; if (ch == 'd') - fetch(ixnew, lowc, d, input[1], " ", 0); + fetch(ixnew, lowc, d, f2, " ", 0); else { - fetch(ixnew, lowc, c - 1, input[1], " ", 0); - fetch(ixnew, c, d, input[1], + fetch(ixnew, lowc, c - 1, f2, " ", 0); + fetch(ixnew, c, d, f2, ch == 'c' ? "! " : "+ ", 0); } lowc = d + 1; cvp++; } - fetch(ixnew, d + 1, upd, input[1], " ", 0); + fetch(ixnew, d + 1, upd, f2, " ", 0); } context_vec_ptr = context_vec_start - 1; } /* dump accumulated "unified" diff changes */ static void -dump_unified_vec(void) +dump_unified_vec(FILE *f1, FILE *f2) { struct context_vec *cvp = context_vec_start; int lowa, upb, lowc, upd; @@ -1142,23 +1257,23 @@ dump_unified_vec(void) switch (ch) { case 'c': - fetch(ixold, lowa, a - 1, input[0], " ", 0); - fetch(ixold, a, b, input[0], "-", 0); - fetch(ixnew, c, d, input[1], "+", 0); + fetch(ixold, lowa, a - 1, f1, " ", 0); + fetch(ixold, a, b, f1, "-", 0); + fetch(ixnew, c, d, f2, "+", 0); break; case 'd': - fetch(ixold, lowa, a - 1, input[0], " ", 0); - fetch(ixold, a, b, input[0], "-", 0); + fetch(ixold, lowa, a - 1, f1, " ", 0); + fetch(ixold, a, b, f1, "-", 0); break; case 'a': - fetch(ixnew, lowc, c - 1, input[1], " ", 0); - fetch(ixnew, c, d, input[1], "+", 0); + fetch(ixnew, lowc, c - 1, f2, " ", 0); + fetch(ixnew, c, d, f2, "+", 0); break; } lowa = b + 1; lowc = d + 1; } - fetch(ixnew, d + 1, upd, input[1], " ", 0); + fetch(ixnew, d + 1, upd, f2, " ", 0); context_vec_ptr = context_vec_start - 1; } diff --git a/usr.bin/diff/pathnames.h b/usr.bin/diff/pathnames.h deleted file mode 100644 index 83b4235e923..00000000000 --- a/usr.bin/diff/pathnames.h +++ /dev/null @@ -1,34 +0,0 @@ -/*- - * Copyright (c) 1989, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)pathnames.h 8.1 (Berkeley) 6/6/93 - */ - -#include <paths.h> - -#define _PATH_DIFF "/usr/bin/diff" |