1 /* $OpenBSD: diff.c,v 1.23 2003/07/06 20:48:59 millert Exp $ */ 2 3 /* 4 * Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 * 18 * Sponsored in part by the Defense Advanced Research Projects 19 * Agency (DARPA) and Air Force Research Laboratory, Air Force 20 * Materiel Command, USAF, under agreement number F39502-99-1-0512. 21 */ 22 23 #ifndef lint 24 static const char rcsid[] = "$OpenBSD: diff.c,v 1.23 2003/07/06 20:48:59 millert Exp $"; 25 #endif /* not lint */ 26 27 #include <sys/param.h> 28 #include <sys/stat.h> 29 30 #include <err.h> 31 #include <errno.h> 32 #include <getopt.h> 33 #include <stdlib.h> 34 #include <stdio.h> 35 #include <stdarg.h> 36 #include <string.h> 37 #include <unistd.h> 38 39 #include "diff.h" 40 41 int aflag, bflag, iflag, Nflag, rflag, sflag, tflag, wflag; 42 int format, context, status; 43 char *start, *ifdefname, *diffargs; 44 struct stat stb1, stb2; 45 struct excludes *excludes_list; 46 47 #define OPTIONS "abC:cD:efhinNrS:stU:uwX:x:" 48 static struct option longopts[] = { 49 { "text", no_argument, 0, 'a' }, 50 { "ignore-space-change", no_argument, 0, 'b' }, 51 { "context", optional_argument, 0, 'C' }, 52 { "ifdef", required_argument, 0, 'D' }, 53 { "ed", no_argument, 0, 'e' }, 54 { "forward-ed", no_argument, 0, 'f' }, 55 { "ignore-case", no_argument, 0, 'i' }, 56 { "new-file", no_argument, 0, 'N' }, 57 { "rcs", no_argument, 0, 'n' }, 58 { "recursive", no_argument, 0, 'r' }, 59 { "report-identical-files", no_argument, 0, 's' }, 60 { "starting-file", required_argument, 0, 'S' }, 61 { "expand-tabs", no_argument, 0, 't' }, 62 { "unified", optional_argument, 0, 'U' }, 63 { "ignore-all-space", no_argument, 0, 'w' }, 64 { "exclude", required_argument, 0, 'x' }, 65 { "exclude-from", required_argument, 0, 'X' }, 66 }; 67 68 __dead void usage(void); 69 void push_excludes(char *); 70 void read_excludes_file(char *file); 71 void set_argstr(char **, char **); 72 73 int 74 main(int argc, char **argv) 75 { 76 char *ep, **oargv; 77 long l; 78 int ch, gotstdin; 79 80 oargv = argv; 81 gotstdin = 0; 82 83 while ((ch = getopt_long(argc, argv, OPTIONS, longopts, NULL)) != -1) { 84 switch (ch) { 85 case 'a': 86 aflag = 1; 87 break; 88 case 'b': 89 bflag = 1; 90 break; 91 case 'C': 92 case 'c': 93 format = D_CONTEXT; 94 if (optarg != NULL) { 95 l = strtol(optarg, &ep, 10); 96 if (*ep != '\0' || l < 0 || l >= INT_MAX) 97 usage(); 98 context = (int)l; 99 } else 100 context = 3; 101 break; 102 case 'D': 103 format = D_IFDEF; 104 ifdefname = optarg; 105 break; 106 case 'e': 107 format = D_EDIT; 108 break; 109 case 'f': 110 format = D_REVERSE; 111 break; 112 case 'h': 113 /* silently ignore for backwards compatibility */ 114 break; 115 case 'i': 116 iflag = 1; 117 break; 118 case 'N': 119 Nflag = 1; 120 break; 121 case 'n': 122 format = D_NREVERSE; 123 break; 124 case 'r': 125 rflag = 1; 126 break; 127 case 'S': 128 start = optarg; 129 break; 130 case 's': 131 sflag = 1; 132 break; 133 case 't': 134 tflag = 1; 135 break; 136 case 'U': 137 case 'u': 138 format = D_UNIFIED; 139 if (optarg != NULL) { 140 l = strtol(optarg, &ep, 10); 141 if (*ep != '\0' || l < 0 || l >= INT_MAX) 142 usage(); 143 context = (int)l; 144 } else 145 context = 3; 146 break; 147 case 'w': 148 wflag = 1; 149 break; 150 case 'X': 151 read_excludes_file(optarg); 152 break; 153 case 'x': 154 push_excludes(optarg); 155 break; 156 default: 157 usage(); 158 break; 159 } 160 } 161 argc -= optind; 162 argv += optind; 163 164 /* 165 * Do sanity checks, fill in stb1 and stb2 and call the appropriate 166 * driver routine. Both drivers use the contents of stb1 and stb2. 167 */ 168 if (argc != 2) 169 usage(); 170 if (strcmp(argv[0], "-") == 0) { 171 stb1.st_mode = S_IFREG; 172 gotstdin = 1; 173 } else if (stat(argv[0], &stb1) != 0) 174 error("%s", argv[0]); 175 if (strcmp(argv[1], "-") == 0) { 176 stb2.st_mode = S_IFREG; 177 gotstdin = 1; 178 } else if (stat(argv[1], &stb2) != 0) 179 error("%s", argv[1]); 180 if (gotstdin && (S_ISDIR(stb1.st_mode) || S_ISDIR(stb2.st_mode))) 181 errorx("can't compare - to a directory"); 182 if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) { 183 if (format == D_IFDEF) 184 errorx("-D option not supported with directories"); 185 set_argstr(oargv, argv); 186 diffdir(argv[0], argv[1]); 187 } else 188 diffreg(argv[0], argv[1], 0); 189 exit(status); 190 } 191 192 void 193 quit(int signo) 194 { 195 if (tempfiles[0] != NULL) 196 unlink(tempfiles[0]); 197 if (tempfiles[1] != NULL) 198 unlink(tempfiles[1]); 199 _exit(status); 200 } 201 202 void * 203 emalloc(size_t n) 204 { 205 void *p; 206 207 if ((p = malloc(n)) == NULL) 208 error(NULL); 209 return (p); 210 } 211 212 void * 213 erealloc(void *p, size_t n) 214 { 215 void *q; 216 217 if ((q = realloc(p, n)) == NULL) 218 error(NULL); 219 return (q); 220 } 221 222 __dead void 223 error(const char *fmt, ...) 224 { 225 va_list ap; 226 int sverrno = errno; 227 228 if (tempfiles[0] != NULL) 229 unlink(tempfiles[0]); 230 if (tempfiles[1] != NULL) 231 unlink(tempfiles[1]); 232 errno = sverrno; 233 va_start(ap, fmt); 234 verr(2, fmt, ap); 235 va_end(ap); 236 } 237 238 __dead void 239 errorx(const char *fmt, ...) 240 { 241 va_list ap; 242 243 if (tempfiles[0] != NULL) 244 unlink(tempfiles[0]); 245 if (tempfiles[1] != NULL) 246 unlink(tempfiles[1]); 247 va_start(ap, fmt); 248 verrx(2, fmt, ap); 249 va_end(ap); 250 } 251 252 void 253 set_argstr(char **av, char **ave) 254 { 255 size_t argsize; 256 char **ap; 257 258 argsize = 4 + (char *)ave - (char *)av + 1; 259 diffargs = emalloc(argsize); 260 strlcpy(diffargs, "diff", argsize); 261 for (ap = av + 1; ap < ave; ap++) { 262 if (strcmp(*ap, "--") != 0) { 263 strlcat(diffargs, " ", argsize); 264 strlcat(diffargs, *ap, argsize); 265 } 266 } 267 } 268 269 /* 270 * Read in an excludes file and push each line. 271 */ 272 void 273 read_excludes_file(char *file) 274 { 275 FILE *fp; 276 char *buf, *pattern; 277 size_t len; 278 279 if (strcmp(file, "-") == 0) 280 fp = stdin; 281 else if ((fp = fopen(file, "r")) == NULL) 282 error("%s", file); 283 while ((buf = fgetln(fp, &len)) != NULL) { 284 if (buf[len - 1] == '\n') 285 len--; 286 pattern = emalloc(len + 1); 287 memcpy(pattern, buf, len); 288 pattern[len] = '\0'; 289 push_excludes(pattern); 290 } 291 if (strcmp(file, "-") != 0) 292 fclose(fp); 293 } 294 295 /* 296 * Push a pattern onto the excludes list. 297 */ 298 void 299 push_excludes(char *pattern) 300 { 301 struct excludes *entry; 302 303 entry = emalloc(sizeof(*entry)); 304 entry->pattern = pattern; 305 entry->next = excludes_list; 306 excludes_list = entry; 307 } 308 309 __dead void 310 usage(void) 311 { 312 (void)fprintf(stderr, 313 "usage: diff [-bitw] [-c | -e | -f | -n | -u ] file1 file2\n" 314 " diff [-bitw] -C number file1 file2\n" 315 " diff [-bitw] -D string file1 file2\n" 316 " diff [-bitw] -U number file1 file2\n" 317 " diff [-biNwt] [-c | -e | -f | -n | -u ] [-r] [-s] [-S name]" 318 " [-X file]\n [-x pattern] dir1 dir2\n"); 319 320 exit(2); 321 } 322