1*96e45528Sotto /* $OpenBSD: diff.c,v 1.44 2004/01/07 17:18:32 otto Exp $ */ 2d0c3f575Sderaadt 3d0c3f575Sderaadt /* 44ec4b3d5Smillert * Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com> 5d0c3f575Sderaadt * 64ec4b3d5Smillert * Permission to use, copy, modify, and distribute this software for any 74ec4b3d5Smillert * purpose with or without fee is hereby granted, provided that the above 84ec4b3d5Smillert * copyright notice and this permission notice appear in all copies. 9d0c3f575Sderaadt * 104ec4b3d5Smillert * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 114ec4b3d5Smillert * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 124ec4b3d5Smillert * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 134ec4b3d5Smillert * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 144ec4b3d5Smillert * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 154ec4b3d5Smillert * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 164ec4b3d5Smillert * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 174ec4b3d5Smillert * 184ec4b3d5Smillert * Sponsored in part by the Defense Advanced Research Projects 194ec4b3d5Smillert * Agency (DARPA) and Air Force Research Laboratory, Air Force 204ec4b3d5Smillert * Materiel Command, USAF, under agreement number F39502-99-1-0512. 21d0c3f575Sderaadt */ 22d0c3f575Sderaadt 234ec4b3d5Smillert #ifndef lint 24*96e45528Sotto static const char rcsid[] = "$OpenBSD: diff.c,v 1.44 2004/01/07 17:18:32 otto Exp $"; 254ec4b3d5Smillert #endif /* not lint */ 264ec4b3d5Smillert 274ec4b3d5Smillert #include <sys/param.h> 284ec4b3d5Smillert #include <sys/stat.h> 294ec4b3d5Smillert 30cb9b5491Smillert #include <ctype.h> 314ec4b3d5Smillert #include <err.h> 3266e5764eSmillert #include <errno.h> 334ec4b3d5Smillert #include <getopt.h> 347b6ec9e4Smillert #include <signal.h> 3526da422aStedu #include <stdlib.h> 364ec4b3d5Smillert #include <stdio.h> 3766e5764eSmillert #include <stdarg.h> 38e582024bSdavid #include <string.h> 3926da422aStedu #include <unistd.h> 40ae8d569bSderaadt 41ae8d569bSderaadt #include "diff.h" 42ae8d569bSderaadt 43*96e45528Sotto int aflag, bflag, dflag, iflag, lflag, Nflag, Pflag, pflag, rflag; 44a65ef95eSderaadt int sflag, tflag, Tflag, wflag; 454ec4b3d5Smillert int format, context, status; 461f9aa9e0Smillert char *start, *ifdefname, *diffargs, *label; 47d5d5ac6cStedu struct stat stb1, stb2; 484ec4b3d5Smillert struct excludes *excludes_list; 494ec4b3d5Smillert 50*96e45528Sotto #define OPTIONS "0123456789abC:cdD:efhiL:lnNPpqrS:sTtU:uwX:x:" 514ec4b3d5Smillert static struct option longopts[] = { 524ec4b3d5Smillert { "text", no_argument, 0, 'a' }, 534ec4b3d5Smillert { "ignore-space-change", no_argument, 0, 'b' }, 544ec4b3d5Smillert { "context", optional_argument, 0, 'C' }, 554ec4b3d5Smillert { "ifdef", required_argument, 0, 'D' }, 566e18f850Sotto { "minimal", no_argument, 0, 'd' }, 574ec4b3d5Smillert { "ed", no_argument, 0, 'e' }, 584ec4b3d5Smillert { "forward-ed", no_argument, 0, 'f' }, 594ec4b3d5Smillert { "ignore-case", no_argument, 0, 'i' }, 60b4bca33fSmillert { "paginate", no_argument, 0, 'l' }, 611f9aa9e0Smillert { "label", required_argument, 0, 'L' }, 624ec4b3d5Smillert { "new-file", no_argument, 0, 'N' }, 634ec4b3d5Smillert { "rcs", no_argument, 0, 'n' }, 64aeb82612Smillert { "unidirectional-new-file", no_argument, 0, 'P' }, 65*96e45528Sotto { "show-c-function", no_argument, 0, 'p' }, 66cab5d83cSmillert { "brief", no_argument, 0, 'q' }, 674ec4b3d5Smillert { "recursive", no_argument, 0, 'r' }, 684ec4b3d5Smillert { "report-identical-files", no_argument, 0, 's' }, 694ec4b3d5Smillert { "starting-file", required_argument, 0, 'S' }, 704ec4b3d5Smillert { "expand-tabs", no_argument, 0, 't' }, 71049b39f6Sdavid { "initial-tab", no_argument, 0, 'T' }, 724ec4b3d5Smillert { "unified", optional_argument, 0, 'U' }, 734ec4b3d5Smillert { "ignore-all-space", no_argument, 0, 'w' }, 744ec4b3d5Smillert { "exclude", required_argument, 0, 'x' }, 754ec4b3d5Smillert { "exclude-from", required_argument, 0, 'X' }, 76d6c18fb8Smillert { NULL, 0, 0, '\0'} 774ec4b3d5Smillert }; 78ae8d569bSderaadt 79c42aed39Smillert __dead void usage(void); 804ec4b3d5Smillert void push_excludes(char *); 814ec4b3d5Smillert void read_excludes_file(char *file); 824ec4b3d5Smillert void set_argstr(char **, char **); 83ae8d569bSderaadt 8426da422aStedu int 8526da422aStedu main(int argc, char **argv) 8626da422aStedu { 874ec4b3d5Smillert char *ep, **oargv; 884ec4b3d5Smillert long l; 89cb9b5491Smillert int ch, lastch, gotstdin, prevoptind, newarg; 9026da422aStedu 914ec4b3d5Smillert oargv = argv; 924ec4b3d5Smillert gotstdin = 0; 93c42aed39Smillert 94cb9b5491Smillert lastch = '\0'; 95cb9b5491Smillert prevoptind = 1; 96cb9b5491Smillert newarg = 1; 974ec4b3d5Smillert while ((ch = getopt_long(argc, argv, OPTIONS, longopts, NULL)) != -1) { 98c42aed39Smillert switch (ch) { 9968cd7c43Stedu case '0': case '1': case '2': case '3': case '4': 10068cd7c43Stedu case '5': case '6': case '7': case '8': case '9': 101cb9b5491Smillert if (newarg) 102cb9b5491Smillert usage(); /* disallow -[0-9]+ */ 103cb9b5491Smillert else if (lastch == 'c' || lastch == 'u') 10468cd7c43Stedu context = 0; 105cb9b5491Smillert else if (!isdigit(lastch) || context > INT_MAX / 10) 106cb9b5491Smillert usage(); 107cb9b5491Smillert context = (context * 10) + (ch - '0'); 10868cd7c43Stedu break; 109d5d5ac6cStedu case 'a': 1104ec4b3d5Smillert aflag = 1; 111d5d5ac6cStedu break; 112ae8d569bSderaadt case 'b': 1134ec4b3d5Smillert bflag = 1; 114c42aed39Smillert break; 115c42aed39Smillert case 'C': 116ae8d569bSderaadt case 'c': 1174ec4b3d5Smillert format = D_CONTEXT; 1184ec4b3d5Smillert if (optarg != NULL) { 1194ec4b3d5Smillert l = strtol(optarg, &ep, 10); 1204ec4b3d5Smillert if (*ep != '\0' || l < 0 || l >= INT_MAX) 1214ec4b3d5Smillert usage(); 1224ec4b3d5Smillert context = (int)l; 1234ec4b3d5Smillert } else 124ae8d569bSderaadt context = 3; 125c42aed39Smillert break; 1266e18f850Sotto case 'd': 1276e18f850Sotto dflag = 1; 1286e18f850Sotto break; 129c42aed39Smillert case 'D': 1304ec4b3d5Smillert format = D_IFDEF; 13190f56ad8Smillert ifdefname = optarg; 132c42aed39Smillert break; 133c42aed39Smillert case 'e': 1344ec4b3d5Smillert format = D_EDIT; 135c42aed39Smillert break; 136c42aed39Smillert case 'f': 1374ec4b3d5Smillert format = D_REVERSE; 138c42aed39Smillert break; 139a0daf5ccSmillert case 'h': 140a0daf5ccSmillert /* silently ignore for backwards compatibility */ 141a0daf5ccSmillert break; 142c42aed39Smillert case 'i': 1434ec4b3d5Smillert iflag = 1; 1444ec4b3d5Smillert break; 1451f9aa9e0Smillert case 'L': 1461f9aa9e0Smillert label = optarg; 1471f9aa9e0Smillert break; 148b4bca33fSmillert case 'l': 149b4bca33fSmillert lflag = 1; 1507b6ec9e4Smillert signal(SIGPIPE, SIG_IGN); 151b4bca33fSmillert break; 1524ec4b3d5Smillert case 'N': 1534ec4b3d5Smillert Nflag = 1; 154c42aed39Smillert break; 155c42aed39Smillert case 'n': 1564ec4b3d5Smillert format = D_NREVERSE; 157c42aed39Smillert break; 158*96e45528Sotto case 'p': 159*96e45528Sotto pflag = 1; 160*96e45528Sotto break; 161aeb82612Smillert case 'P': 162aeb82612Smillert Pflag = 1; 163aeb82612Smillert break; 164c42aed39Smillert case 'r': 1654ec4b3d5Smillert rflag = 1; 166c42aed39Smillert break; 167cab5d83cSmillert case 'q': 168cab5d83cSmillert format = D_BRIEF; 169cab5d83cSmillert break; 170c42aed39Smillert case 'S': 171c42aed39Smillert start = optarg; 172c42aed39Smillert break; 173c42aed39Smillert case 's': 1744ec4b3d5Smillert sflag = 1; 175c42aed39Smillert break; 1761f9aa9e0Smillert case 'T': 1771f9aa9e0Smillert Tflag = 1; 1781f9aa9e0Smillert break; 179c42aed39Smillert case 't': 1804ec4b3d5Smillert tflag = 1; 181c42aed39Smillert break; 1829de32c1bSmillert case 'U': 1839de32c1bSmillert case 'u': 1844ec4b3d5Smillert format = D_UNIFIED; 1854ec4b3d5Smillert if (optarg != NULL) { 1864ec4b3d5Smillert l = strtol(optarg, &ep, 10); 1874ec4b3d5Smillert if (*ep != '\0' || l < 0 || l >= INT_MAX) 1884ec4b3d5Smillert usage(); 1894ec4b3d5Smillert context = (int)l; 1904ec4b3d5Smillert } else 1919de32c1bSmillert context = 3; 1929de32c1bSmillert break; 193c42aed39Smillert case 'w': 1944ec4b3d5Smillert wflag = 1; 1954ec4b3d5Smillert break; 1964ec4b3d5Smillert case 'X': 1974ec4b3d5Smillert read_excludes_file(optarg); 1984ec4b3d5Smillert break; 1994ec4b3d5Smillert case 'x': 2004ec4b3d5Smillert push_excludes(optarg); 201c42aed39Smillert break; 202ae8d569bSderaadt default: 203c42aed39Smillert usage(); 204c42aed39Smillert break; 205ae8d569bSderaadt } 20668cd7c43Stedu lastch = ch; 207cb9b5491Smillert newarg = optind != prevoptind; 208cb9b5491Smillert prevoptind = optind; 209ae8d569bSderaadt } 210c42aed39Smillert argc -= optind; 211c42aed39Smillert argv += optind; 212c42aed39Smillert 2134ec4b3d5Smillert /* 2144ec4b3d5Smillert * Do sanity checks, fill in stb1 and stb2 and call the appropriate 2154ec4b3d5Smillert * driver routine. Both drivers use the contents of stb1 and stb2. 2164ec4b3d5Smillert */ 217c42aed39Smillert if (argc != 2) 2184ec4b3d5Smillert usage(); 2194ec4b3d5Smillert if (strcmp(argv[0], "-") == 0) { 220b1a26502Smillert fstat(STDIN_FILENO, &stb1); 2214ec4b3d5Smillert gotstdin = 1; 2224ec4b3d5Smillert } else if (stat(argv[0], &stb1) != 0) 2237b6ec9e4Smillert err(2, "%s", argv[0]); 2244ec4b3d5Smillert if (strcmp(argv[1], "-") == 0) { 225b1a26502Smillert fstat(STDIN_FILENO, &stb2); 2264ec4b3d5Smillert gotstdin = 1; 2274ec4b3d5Smillert } else if (stat(argv[1], &stb2) != 0) 2287b6ec9e4Smillert err(2, "%s", argv[1]); 2294ec4b3d5Smillert if (gotstdin && (S_ISDIR(stb1.st_mode) || S_ISDIR(stb2.st_mode))) 2307b6ec9e4Smillert errx(2, "can't compare - to a directory"); 23177aa65d5Smillert set_argstr(oargv + 1, argv); 2324ec4b3d5Smillert if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) { 2334ec4b3d5Smillert if (format == D_IFDEF) 2347b6ec9e4Smillert errx(2, "-D option not supported with directories"); 2354ec4b3d5Smillert diffdir(argv[0], argv[1]); 236b4bca33fSmillert } else { 2377b6ec9e4Smillert if (S_ISDIR(stb1.st_mode)) { 2387b6ec9e4Smillert argv[0] = splice(argv[0], argv[1]); 2397b6ec9e4Smillert if (stat(argv[0], &stb1) < 0) 2407b6ec9e4Smillert err(2, "%s", argv[0]); 2417b6ec9e4Smillert } 2427b6ec9e4Smillert if (S_ISDIR(stb2.st_mode)) { 2437b6ec9e4Smillert argv[1] = splice(argv[1], argv[0]); 2447b6ec9e4Smillert if (stat(argv[1], &stb2) < 0) 2457b6ec9e4Smillert err(2, "%s", argv[1]); 2467b6ec9e4Smillert } 247b4bca33fSmillert print_status(diffreg(argv[0], argv[1], 0), argv[0], argv[1], 248b4bca33fSmillert NULL); 249b4bca33fSmillert } 2504ec4b3d5Smillert exit(status); 251ae8d569bSderaadt } 252ae8d569bSderaadt 25326da422aStedu void * 25449dffe13Smillert emalloc(size_t n) 255ae8d569bSderaadt { 25626da422aStedu void *p; 257ae8d569bSderaadt 25826da422aStedu if ((p = malloc(n)) == NULL) 2597b6ec9e4Smillert err(2, NULL); 26026da422aStedu return (p); 26126da422aStedu } 26226da422aStedu 26326da422aStedu void * 26449dffe13Smillert erealloc(void *p, size_t n) 26526da422aStedu { 26626da422aStedu void *q; 26726da422aStedu 26826da422aStedu if ((q = realloc(p, n)) == NULL) 2697b6ec9e4Smillert err(2, NULL); 270ae8d569bSderaadt return (q); 271ae8d569bSderaadt } 272ae8d569bSderaadt 273b4bca33fSmillert int 274b4bca33fSmillert easprintf(char **ret, const char *fmt, ...) 275b4bca33fSmillert { 276b4bca33fSmillert int len; 277b4bca33fSmillert va_list ap; 278b4bca33fSmillert 279b4bca33fSmillert va_start(ap, fmt); 280b4bca33fSmillert len = vasprintf(ret, fmt, ap); 281b4bca33fSmillert va_end(ap); 282b4bca33fSmillert 283b4bca33fSmillert if (len == -1) 2847b6ec9e4Smillert err(2, NULL); 285b4bca33fSmillert return (len); 286b4bca33fSmillert } 287b4bca33fSmillert 2884ec4b3d5Smillert void 2894ec4b3d5Smillert set_argstr(char **av, char **ave) 2904ec4b3d5Smillert { 2914ec4b3d5Smillert size_t argsize; 2924ec4b3d5Smillert char **ap; 2934ec4b3d5Smillert 29477aa65d5Smillert argsize = 4 + *ave - *av + 1; 2954ec4b3d5Smillert diffargs = emalloc(argsize); 2964ec4b3d5Smillert strlcpy(diffargs, "diff", argsize); 2974ec4b3d5Smillert for (ap = av + 1; ap < ave; ap++) { 2984ec4b3d5Smillert if (strcmp(*ap, "--") != 0) { 2994ec4b3d5Smillert strlcat(diffargs, " ", argsize); 3004ec4b3d5Smillert strlcat(diffargs, *ap, argsize); 3014ec4b3d5Smillert } 3024ec4b3d5Smillert } 3034ec4b3d5Smillert } 3044ec4b3d5Smillert 3054ec4b3d5Smillert /* 3064ec4b3d5Smillert * Read in an excludes file and push each line. 3074ec4b3d5Smillert */ 3084ec4b3d5Smillert void 3094ec4b3d5Smillert read_excludes_file(char *file) 3104ec4b3d5Smillert { 3114ec4b3d5Smillert FILE *fp; 3124ec4b3d5Smillert char *buf, *pattern; 3134ec4b3d5Smillert size_t len; 3144ec4b3d5Smillert 3154ec4b3d5Smillert if (strcmp(file, "-") == 0) 3164ec4b3d5Smillert fp = stdin; 3174ec4b3d5Smillert else if ((fp = fopen(file, "r")) == NULL) 3187b6ec9e4Smillert err(2, "%s", file); 3194ec4b3d5Smillert while ((buf = fgetln(fp, &len)) != NULL) { 3204ec4b3d5Smillert if (buf[len - 1] == '\n') 3214ec4b3d5Smillert len--; 3224ec4b3d5Smillert pattern = emalloc(len + 1); 3234ec4b3d5Smillert memcpy(pattern, buf, len); 3244ec4b3d5Smillert pattern[len] = '\0'; 3254ec4b3d5Smillert push_excludes(pattern); 3264ec4b3d5Smillert } 3274ec4b3d5Smillert if (strcmp(file, "-") != 0) 3284ec4b3d5Smillert fclose(fp); 3294ec4b3d5Smillert } 3304ec4b3d5Smillert 3314ec4b3d5Smillert /* 3324ec4b3d5Smillert * Push a pattern onto the excludes list. 3334ec4b3d5Smillert */ 3344ec4b3d5Smillert void 3354ec4b3d5Smillert push_excludes(char *pattern) 3364ec4b3d5Smillert { 3374ec4b3d5Smillert struct excludes *entry; 3384ec4b3d5Smillert 3394ec4b3d5Smillert entry = emalloc(sizeof(*entry)); 3404ec4b3d5Smillert entry->pattern = pattern; 3414ec4b3d5Smillert entry->next = excludes_list; 3424ec4b3d5Smillert excludes_list = entry; 3434ec4b3d5Smillert } 3444ec4b3d5Smillert 345b4bca33fSmillert void 3464893e147Smillert print_only(const char *path, size_t dirlen, const char *entry) 3474893e147Smillert { 3484893e147Smillert if (dirlen > 1) 3494893e147Smillert dirlen--; 3504893e147Smillert printf("Only in %.*s: %s\n", (int)dirlen, path, entry); 3514893e147Smillert } 3524893e147Smillert 3534893e147Smillert void 354b4bca33fSmillert print_status(int val, char *path1, char *path2, char *entry) 355b4bca33fSmillert { 356b4bca33fSmillert switch (val) { 357b4bca33fSmillert case D_ONLY: 3584893e147Smillert print_only(path1, strlen(path1), entry); 359b4bca33fSmillert break; 360b4bca33fSmillert case D_COMMON: 361b4bca33fSmillert printf("Common subdirectories: %s%s and %s%s\n", 362b4bca33fSmillert path1, entry ? entry : "", path2, entry ? entry : ""); 363b4bca33fSmillert break; 364b4bca33fSmillert case D_BINARY: 365b4bca33fSmillert printf("Binary files %s%s and %s%s differ\n", 366b4bca33fSmillert path1, entry ? entry : "", path2, entry ? entry : ""); 367b4bca33fSmillert break; 368b4bca33fSmillert case D_DIFFER: 369b4bca33fSmillert if (format == D_BRIEF) 370b4bca33fSmillert printf("Files %s%s and %s%s differ\n", 371b4bca33fSmillert path1, entry ? entry : "", 372b4bca33fSmillert path2, entry ? entry : ""); 373b4bca33fSmillert break; 374b4bca33fSmillert case D_SAME: 375b4bca33fSmillert if (sflag) 376b4bca33fSmillert printf("Files %s%s and %s%s are identical\n", 377b4bca33fSmillert path1, entry ? entry : "", 378b4bca33fSmillert path2, entry ? entry : ""); 379b4bca33fSmillert break; 380fed3a06dSmillert case D_MISMATCH1: 381de414158Smillert printf("File %s%s is a directory while file %s%s is a regular file\n", 382fed3a06dSmillert path1, entry ? entry : "", path2, entry ? entry : ""); 383fed3a06dSmillert break; 384fed3a06dSmillert case D_MISMATCH2: 385de414158Smillert printf("File %s%s is a regular file while file %s%s is a directory\n", 3867b6ec9e4Smillert path1, entry ? entry : "", path2, entry ? entry : ""); 3877b6ec9e4Smillert break; 388b4bca33fSmillert } 389b4bca33fSmillert } 390b4bca33fSmillert 391c42aed39Smillert __dead void 392c42aed39Smillert usage(void) 393c42aed39Smillert { 394c012fe98Sderaadt (void)fprintf(stderr, 395*96e45528Sotto "usage: diff [-abdilpqtTw] [-c | -e | -f | -n | -u] [-L label] file1 file2\n" 396*96e45528Sotto " diff [-abdilpqtTw] [-L label] -C number file1 file2\n" 397f5537e38Sjmc " diff [-abdilqtw] -D string file1 file2\n" 398*96e45528Sotto " diff [-abdilpqtTw] [-L label] -U number file1 file2\n" 399*96e45528Sotto " diff [-abdilNPpqtTw] [-c | -e | -f | -n | -u ] [-L label] [-r] [-s]\n" 400f5537e38Sjmc " [-S name] [-X file] [-x pattern] dir1 dir2\n"); 401c42aed39Smillert 40266e5764eSmillert exit(2); 403c42aed39Smillert } 404