1*7b6ec9e4Smillert /* $OpenBSD: diff.c,v 1.28 2003/07/09 00:39:25 millert 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*7b6ec9e4Smillert static const char rcsid[] = "$OpenBSD: diff.c,v 1.28 2003/07/09 00:39:25 millert Exp $"; 254ec4b3d5Smillert #endif /* not lint */ 264ec4b3d5Smillert 274ec4b3d5Smillert #include <sys/param.h> 284ec4b3d5Smillert #include <sys/stat.h> 294ec4b3d5Smillert 304ec4b3d5Smillert #include <err.h> 3166e5764eSmillert #include <errno.h> 324ec4b3d5Smillert #include <getopt.h> 33*7b6ec9e4Smillert #include <signal.h> 3426da422aStedu #include <stdlib.h> 354ec4b3d5Smillert #include <stdio.h> 3666e5764eSmillert #include <stdarg.h> 37e582024bSdavid #include <string.h> 3826da422aStedu #include <unistd.h> 39ae8d569bSderaadt 40ae8d569bSderaadt #include "diff.h" 41ae8d569bSderaadt 42b4bca33fSmillert int aflag, bflag, iflag, lflag, Nflag, Pflag, rflag, sflag, tflag, wflag; 434ec4b3d5Smillert int format, context, status; 444ec4b3d5Smillert char *start, *ifdefname, *diffargs; 45d5d5ac6cStedu struct stat stb1, stb2; 464ec4b3d5Smillert struct excludes *excludes_list; 474ec4b3d5Smillert 48b4bca33fSmillert #define OPTIONS "abC:cD:efhilnNPqrS:stU:uwX:x:" 494ec4b3d5Smillert static struct option longopts[] = { 504ec4b3d5Smillert { "text", no_argument, 0, 'a' }, 514ec4b3d5Smillert { "ignore-space-change", no_argument, 0, 'b' }, 524ec4b3d5Smillert { "context", optional_argument, 0, 'C' }, 534ec4b3d5Smillert { "ifdef", required_argument, 0, 'D' }, 544ec4b3d5Smillert { "ed", no_argument, 0, 'e' }, 554ec4b3d5Smillert { "forward-ed", no_argument, 0, 'f' }, 564ec4b3d5Smillert { "ignore-case", no_argument, 0, 'i' }, 57b4bca33fSmillert { "paginate", no_argument, 0, 'l' }, 584ec4b3d5Smillert { "new-file", no_argument, 0, 'N' }, 594ec4b3d5Smillert { "rcs", no_argument, 0, 'n' }, 60aeb82612Smillert { "unidirectional-new-file", no_argument, 0, 'P' }, 61cab5d83cSmillert { "brief", no_argument, 0, 'q' }, 624ec4b3d5Smillert { "recursive", no_argument, 0, 'r' }, 634ec4b3d5Smillert { "report-identical-files", no_argument, 0, 's' }, 644ec4b3d5Smillert { "starting-file", required_argument, 0, 'S' }, 654ec4b3d5Smillert { "expand-tabs", no_argument, 0, 't' }, 664ec4b3d5Smillert { "unified", optional_argument, 0, 'U' }, 674ec4b3d5Smillert { "ignore-all-space", no_argument, 0, 'w' }, 684ec4b3d5Smillert { "exclude", required_argument, 0, 'x' }, 694ec4b3d5Smillert { "exclude-from", required_argument, 0, 'X' }, 704ec4b3d5Smillert }; 71ae8d569bSderaadt 72c42aed39Smillert __dead void usage(void); 734ec4b3d5Smillert void push_excludes(char *); 744ec4b3d5Smillert void read_excludes_file(char *file); 754ec4b3d5Smillert void set_argstr(char **, char **); 76ae8d569bSderaadt 7726da422aStedu int 7826da422aStedu main(int argc, char **argv) 7926da422aStedu { 804ec4b3d5Smillert char *ep, **oargv; 814ec4b3d5Smillert long l; 824ec4b3d5Smillert int ch, gotstdin; 8326da422aStedu 844ec4b3d5Smillert oargv = argv; 854ec4b3d5Smillert gotstdin = 0; 86c42aed39Smillert 874ec4b3d5Smillert while ((ch = getopt_long(argc, argv, OPTIONS, longopts, NULL)) != -1) { 88c42aed39Smillert switch (ch) { 89d5d5ac6cStedu case 'a': 904ec4b3d5Smillert aflag = 1; 91d5d5ac6cStedu break; 92ae8d569bSderaadt case 'b': 934ec4b3d5Smillert bflag = 1; 94c42aed39Smillert break; 95c42aed39Smillert case 'C': 96ae8d569bSderaadt case 'c': 974ec4b3d5Smillert format = D_CONTEXT; 984ec4b3d5Smillert if (optarg != NULL) { 994ec4b3d5Smillert l = strtol(optarg, &ep, 10); 1004ec4b3d5Smillert if (*ep != '\0' || l < 0 || l >= INT_MAX) 1014ec4b3d5Smillert usage(); 1024ec4b3d5Smillert context = (int)l; 1034ec4b3d5Smillert } else 104ae8d569bSderaadt context = 3; 105c42aed39Smillert break; 106c42aed39Smillert case 'D': 1074ec4b3d5Smillert format = D_IFDEF; 10890f56ad8Smillert ifdefname = optarg; 109c42aed39Smillert break; 110c42aed39Smillert case 'e': 1114ec4b3d5Smillert format = D_EDIT; 112c42aed39Smillert break; 113c42aed39Smillert case 'f': 1144ec4b3d5Smillert format = D_REVERSE; 115c42aed39Smillert break; 116a0daf5ccSmillert case 'h': 117a0daf5ccSmillert /* silently ignore for backwards compatibility */ 118a0daf5ccSmillert break; 119c42aed39Smillert case 'i': 1204ec4b3d5Smillert iflag = 1; 1214ec4b3d5Smillert break; 122b4bca33fSmillert case 'l': 123b4bca33fSmillert lflag = 1; 124*7b6ec9e4Smillert signal(SIGPIPE, SIG_IGN); 125b4bca33fSmillert break; 1264ec4b3d5Smillert case 'N': 1274ec4b3d5Smillert Nflag = 1; 128c42aed39Smillert break; 129c42aed39Smillert case 'n': 1304ec4b3d5Smillert format = D_NREVERSE; 131c42aed39Smillert break; 132aeb82612Smillert case 'P': 133aeb82612Smillert Pflag = 1; 134aeb82612Smillert break; 135c42aed39Smillert case 'r': 1364ec4b3d5Smillert rflag = 1; 137c42aed39Smillert break; 138cab5d83cSmillert case 'q': 139cab5d83cSmillert format = D_BRIEF; 140cab5d83cSmillert break; 141c42aed39Smillert case 'S': 142c42aed39Smillert start = optarg; 143c42aed39Smillert break; 144c42aed39Smillert case 's': 1454ec4b3d5Smillert sflag = 1; 146c42aed39Smillert break; 147c42aed39Smillert case 't': 1484ec4b3d5Smillert tflag = 1; 149c42aed39Smillert break; 1509de32c1bSmillert case 'U': 1519de32c1bSmillert case 'u': 1524ec4b3d5Smillert format = D_UNIFIED; 1534ec4b3d5Smillert if (optarg != NULL) { 1544ec4b3d5Smillert l = strtol(optarg, &ep, 10); 1554ec4b3d5Smillert if (*ep != '\0' || l < 0 || l >= INT_MAX) 1564ec4b3d5Smillert usage(); 1574ec4b3d5Smillert context = (int)l; 1584ec4b3d5Smillert } else 1599de32c1bSmillert context = 3; 1609de32c1bSmillert break; 161c42aed39Smillert case 'w': 1624ec4b3d5Smillert wflag = 1; 1634ec4b3d5Smillert break; 1644ec4b3d5Smillert case 'X': 1654ec4b3d5Smillert read_excludes_file(optarg); 1664ec4b3d5Smillert break; 1674ec4b3d5Smillert case 'x': 1684ec4b3d5Smillert push_excludes(optarg); 169c42aed39Smillert break; 170ae8d569bSderaadt default: 171c42aed39Smillert usage(); 172c42aed39Smillert break; 173ae8d569bSderaadt } 174ae8d569bSderaadt } 175c42aed39Smillert argc -= optind; 176c42aed39Smillert argv += optind; 177c42aed39Smillert 1784ec4b3d5Smillert /* 1794ec4b3d5Smillert * Do sanity checks, fill in stb1 and stb2 and call the appropriate 1804ec4b3d5Smillert * driver routine. Both drivers use the contents of stb1 and stb2. 1814ec4b3d5Smillert */ 182c42aed39Smillert if (argc != 2) 1834ec4b3d5Smillert usage(); 1844ec4b3d5Smillert if (strcmp(argv[0], "-") == 0) { 185b1a26502Smillert fstat(STDIN_FILENO, &stb1); 1864ec4b3d5Smillert gotstdin = 1; 1874ec4b3d5Smillert } else if (stat(argv[0], &stb1) != 0) 188*7b6ec9e4Smillert err(2, "%s", argv[0]); 1894ec4b3d5Smillert if (strcmp(argv[1], "-") == 0) { 190b1a26502Smillert fstat(STDIN_FILENO, &stb2); 1914ec4b3d5Smillert gotstdin = 1; 1924ec4b3d5Smillert } else if (stat(argv[1], &stb2) != 0) 193*7b6ec9e4Smillert err(2, "%s", argv[1]); 1944ec4b3d5Smillert if (gotstdin && (S_ISDIR(stb1.st_mode) || S_ISDIR(stb2.st_mode))) 195*7b6ec9e4Smillert errx(2, "can't compare - to a directory"); 196b4bca33fSmillert set_argstr(oargv, argv); 1974ec4b3d5Smillert if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) { 1984ec4b3d5Smillert if (format == D_IFDEF) 199*7b6ec9e4Smillert errx(2, "-D option not supported with directories"); 2004ec4b3d5Smillert diffdir(argv[0], argv[1]); 201b4bca33fSmillert } else { 202*7b6ec9e4Smillert if (S_ISDIR(stb1.st_mode)) { 203*7b6ec9e4Smillert argv[0] = splice(argv[0], argv[1]); 204*7b6ec9e4Smillert if (stat(argv[0], &stb1) < 0) 205*7b6ec9e4Smillert err(2, "%s", argv[0]); 206*7b6ec9e4Smillert } 207*7b6ec9e4Smillert if (S_ISDIR(stb2.st_mode)) { 208*7b6ec9e4Smillert argv[1] = splice(argv[1], argv[0]); 209*7b6ec9e4Smillert if (stat(argv[1], &stb2) < 0) 210*7b6ec9e4Smillert err(2, "%s", argv[1]); 211*7b6ec9e4Smillert } 212b4bca33fSmillert print_status(diffreg(argv[0], argv[1], 0), argv[0], argv[1], 213b4bca33fSmillert NULL); 214b4bca33fSmillert } 2154ec4b3d5Smillert exit(status); 216ae8d569bSderaadt } 217ae8d569bSderaadt 21826da422aStedu void * 21949dffe13Smillert emalloc(size_t n) 220ae8d569bSderaadt { 22126da422aStedu void *p; 222ae8d569bSderaadt 22326da422aStedu if ((p = malloc(n)) == NULL) 224*7b6ec9e4Smillert err(2, NULL); 22526da422aStedu return (p); 22626da422aStedu } 22726da422aStedu 22826da422aStedu void * 22949dffe13Smillert erealloc(void *p, size_t n) 23026da422aStedu { 23126da422aStedu void *q; 23226da422aStedu 23326da422aStedu if ((q = realloc(p, n)) == NULL) 234*7b6ec9e4Smillert err(2, NULL); 235ae8d569bSderaadt return (q); 236ae8d569bSderaadt } 237ae8d569bSderaadt 238b4bca33fSmillert int 239b4bca33fSmillert easprintf(char **ret, const char *fmt, ...) 240b4bca33fSmillert { 241b4bca33fSmillert int len; 242b4bca33fSmillert va_list ap; 243b4bca33fSmillert 244b4bca33fSmillert va_start(ap, fmt); 245b4bca33fSmillert len = vasprintf(ret, fmt, ap); 246b4bca33fSmillert va_end(ap); 247b4bca33fSmillert 248b4bca33fSmillert if (len == -1) 249*7b6ec9e4Smillert err(2, NULL); 250b4bca33fSmillert return(len); 251b4bca33fSmillert } 252b4bca33fSmillert 2534ec4b3d5Smillert void 2544ec4b3d5Smillert set_argstr(char **av, char **ave) 2554ec4b3d5Smillert { 2564ec4b3d5Smillert size_t argsize; 2574ec4b3d5Smillert char **ap; 2584ec4b3d5Smillert 2594ec4b3d5Smillert argsize = 4 + (char *)ave - (char *)av + 1; 2604ec4b3d5Smillert diffargs = emalloc(argsize); 2614ec4b3d5Smillert strlcpy(diffargs, "diff", argsize); 2624ec4b3d5Smillert for (ap = av + 1; ap < ave; ap++) { 2634ec4b3d5Smillert if (strcmp(*ap, "--") != 0) { 2644ec4b3d5Smillert strlcat(diffargs, " ", argsize); 2654ec4b3d5Smillert strlcat(diffargs, *ap, argsize); 2664ec4b3d5Smillert } 2674ec4b3d5Smillert } 2684ec4b3d5Smillert } 2694ec4b3d5Smillert 2704ec4b3d5Smillert /* 2714ec4b3d5Smillert * Read in an excludes file and push each line. 2724ec4b3d5Smillert */ 2734ec4b3d5Smillert void 2744ec4b3d5Smillert read_excludes_file(char *file) 2754ec4b3d5Smillert { 2764ec4b3d5Smillert FILE *fp; 2774ec4b3d5Smillert char *buf, *pattern; 2784ec4b3d5Smillert size_t len; 2794ec4b3d5Smillert 2804ec4b3d5Smillert if (strcmp(file, "-") == 0) 2814ec4b3d5Smillert fp = stdin; 2824ec4b3d5Smillert else if ((fp = fopen(file, "r")) == NULL) 283*7b6ec9e4Smillert err(2, "%s", file); 2844ec4b3d5Smillert while ((buf = fgetln(fp, &len)) != NULL) { 2854ec4b3d5Smillert if (buf[len - 1] == '\n') 2864ec4b3d5Smillert len--; 2874ec4b3d5Smillert pattern = emalloc(len + 1); 2884ec4b3d5Smillert memcpy(pattern, buf, len); 2894ec4b3d5Smillert pattern[len] = '\0'; 2904ec4b3d5Smillert push_excludes(pattern); 2914ec4b3d5Smillert } 2924ec4b3d5Smillert if (strcmp(file, "-") != 0) 2934ec4b3d5Smillert fclose(fp); 2944ec4b3d5Smillert } 2954ec4b3d5Smillert 2964ec4b3d5Smillert /* 2974ec4b3d5Smillert * Push a pattern onto the excludes list. 2984ec4b3d5Smillert */ 2994ec4b3d5Smillert void 3004ec4b3d5Smillert push_excludes(char *pattern) 3014ec4b3d5Smillert { 3024ec4b3d5Smillert struct excludes *entry; 3034ec4b3d5Smillert 3044ec4b3d5Smillert entry = emalloc(sizeof(*entry)); 3054ec4b3d5Smillert entry->pattern = pattern; 3064ec4b3d5Smillert entry->next = excludes_list; 3074ec4b3d5Smillert excludes_list = entry; 3084ec4b3d5Smillert } 3094ec4b3d5Smillert 310b4bca33fSmillert void 311b4bca33fSmillert print_status(int val, char *path1, char *path2, char *entry) 312b4bca33fSmillert { 313b4bca33fSmillert switch (val) { 314b4bca33fSmillert case D_ONLY: 315b4bca33fSmillert printf("Only in %s: %s\n", path1, entry); 316b4bca33fSmillert break; 317b4bca33fSmillert case D_COMMON: 318b4bca33fSmillert printf("Common subdirectories: %s%s and %s%s\n", 319b4bca33fSmillert path1, entry ? entry : "", path2, entry ? entry : ""); 320b4bca33fSmillert break; 321b4bca33fSmillert case D_BINARY: 322b4bca33fSmillert printf("Binary files %s%s and %s%s differ\n", 323b4bca33fSmillert path1, entry ? entry : "", path2, entry ? entry : ""); 324b4bca33fSmillert break; 325b4bca33fSmillert case D_DIFFER: 326b4bca33fSmillert if (format == D_BRIEF) 327b4bca33fSmillert printf("Files %s%s and %s%s differ\n", 328b4bca33fSmillert path1, entry ? entry : "", 329b4bca33fSmillert path2, entry ? entry : ""); 330b4bca33fSmillert break; 331b4bca33fSmillert case D_SAME: 332b4bca33fSmillert if (sflag) 333b4bca33fSmillert printf("Files %s%s and %s%s are identical\n", 334b4bca33fSmillert path1, entry ? entry : "", 335b4bca33fSmillert path2, entry ? entry : ""); 336b4bca33fSmillert break; 337*7b6ec9e4Smillert case D_MISMATCH: 338*7b6ec9e4Smillert printf("File %s/%s is a directory but file %s/%s is not\n", 339*7b6ec9e4Smillert path1, entry ? entry : "", path2, entry ? entry : ""); 340*7b6ec9e4Smillert break; 341b4bca33fSmillert } 342b4bca33fSmillert } 343b4bca33fSmillert 344c42aed39Smillert __dead void 345c42aed39Smillert usage(void) 346c42aed39Smillert { 347c012fe98Sderaadt (void)fprintf(stderr, 348cab5d83cSmillert "usage: diff [-biqtw] [-c | -e | -f | -n | -u ] file1 file2\n" 349cab5d83cSmillert " diff [-biqtw] -C number file1 file2\n" 350cab5d83cSmillert " diff [-biqtw] -D string file1 file2\n" 351cab5d83cSmillert " diff [-biqtw] -U number file1 file2\n" 352cab5d83cSmillert " diff [-biNPqwt] [-c | -e | -f | -n | -u ] [-r] [-s] [-S name]" 3534ec4b3d5Smillert " [-X file]\n [-x pattern] dir1 dir2\n"); 354c42aed39Smillert 35566e5764eSmillert exit(2); 356c42aed39Smillert } 357