1 /* $OpenBSD: diff.c,v 1.24 2003/07/06 22:02:36 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.24 2003/07/06 22:02:36 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, Pflag, 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:efhinNPrS: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 { "unidirectional-new-file", no_argument, 0, 'P' }, 59 { "recursive", no_argument, 0, 'r' }, 60 { "report-identical-files", no_argument, 0, 's' }, 61 { "starting-file", required_argument, 0, 'S' }, 62 { "expand-tabs", no_argument, 0, 't' }, 63 { "unified", optional_argument, 0, 'U' }, 64 { "ignore-all-space", no_argument, 0, 'w' }, 65 { "exclude", required_argument, 0, 'x' }, 66 { "exclude-from", required_argument, 0, 'X' }, 67 }; 68 69 __dead void usage(void); 70 void push_excludes(char *); 71 void read_excludes_file(char *file); 72 void set_argstr(char **, char **); 73 74 int 75 main(int argc, char **argv) 76 { 77 char *ep, **oargv; 78 long l; 79 int ch, gotstdin; 80 81 oargv = argv; 82 gotstdin = 0; 83 84 while ((ch = getopt_long(argc, argv, OPTIONS, longopts, NULL)) != -1) { 85 switch (ch) { 86 case 'a': 87 aflag = 1; 88 break; 89 case 'b': 90 bflag = 1; 91 break; 92 case 'C': 93 case 'c': 94 format = D_CONTEXT; 95 if (optarg != NULL) { 96 l = strtol(optarg, &ep, 10); 97 if (*ep != '\0' || l < 0 || l >= INT_MAX) 98 usage(); 99 context = (int)l; 100 } else 101 context = 3; 102 break; 103 case 'D': 104 format = D_IFDEF; 105 ifdefname = optarg; 106 break; 107 case 'e': 108 format = D_EDIT; 109 break; 110 case 'f': 111 format = D_REVERSE; 112 break; 113 case 'h': 114 /* silently ignore for backwards compatibility */ 115 break; 116 case 'i': 117 iflag = 1; 118 break; 119 case 'N': 120 Nflag = 1; 121 break; 122 case 'n': 123 format = D_NREVERSE; 124 break; 125 case 'P': 126 Pflag = 1; 127 break; 128 case 'r': 129 rflag = 1; 130 break; 131 case 'S': 132 start = optarg; 133 break; 134 case 's': 135 sflag = 1; 136 break; 137 case 't': 138 tflag = 1; 139 break; 140 case 'U': 141 case 'u': 142 format = D_UNIFIED; 143 if (optarg != NULL) { 144 l = strtol(optarg, &ep, 10); 145 if (*ep != '\0' || l < 0 || l >= INT_MAX) 146 usage(); 147 context = (int)l; 148 } else 149 context = 3; 150 break; 151 case 'w': 152 wflag = 1; 153 break; 154 case 'X': 155 read_excludes_file(optarg); 156 break; 157 case 'x': 158 push_excludes(optarg); 159 break; 160 default: 161 usage(); 162 break; 163 } 164 } 165 argc -= optind; 166 argv += optind; 167 168 /* 169 * Do sanity checks, fill in stb1 and stb2 and call the appropriate 170 * driver routine. Both drivers use the contents of stb1 and stb2. 171 */ 172 if (argc != 2) 173 usage(); 174 if (strcmp(argv[0], "-") == 0) { 175 stb1.st_mode = S_IFREG; 176 gotstdin = 1; 177 } else if (stat(argv[0], &stb1) != 0) 178 error("%s", argv[0]); 179 if (strcmp(argv[1], "-") == 0) { 180 stb2.st_mode = S_IFREG; 181 gotstdin = 1; 182 } else if (stat(argv[1], &stb2) != 0) 183 error("%s", argv[1]); 184 if (gotstdin && (S_ISDIR(stb1.st_mode) || S_ISDIR(stb2.st_mode))) 185 errorx("can't compare - to a directory"); 186 if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) { 187 if (format == D_IFDEF) 188 errorx("-D option not supported with directories"); 189 set_argstr(oargv, argv); 190 diffdir(argv[0], argv[1]); 191 } else 192 diffreg(argv[0], argv[1], 0); 193 exit(status); 194 } 195 196 void 197 quit(int signo) 198 { 199 if (tempfiles[0] != NULL) 200 unlink(tempfiles[0]); 201 if (tempfiles[1] != NULL) 202 unlink(tempfiles[1]); 203 _exit(status); 204 } 205 206 void * 207 emalloc(size_t n) 208 { 209 void *p; 210 211 if ((p = malloc(n)) == NULL) 212 error(NULL); 213 return (p); 214 } 215 216 void * 217 erealloc(void *p, size_t n) 218 { 219 void *q; 220 221 if ((q = realloc(p, n)) == NULL) 222 error(NULL); 223 return (q); 224 } 225 226 __dead void 227 error(const char *fmt, ...) 228 { 229 va_list ap; 230 int sverrno = errno; 231 232 if (tempfiles[0] != NULL) 233 unlink(tempfiles[0]); 234 if (tempfiles[1] != NULL) 235 unlink(tempfiles[1]); 236 errno = sverrno; 237 va_start(ap, fmt); 238 verr(2, fmt, ap); 239 va_end(ap); 240 } 241 242 __dead void 243 errorx(const char *fmt, ...) 244 { 245 va_list ap; 246 247 if (tempfiles[0] != NULL) 248 unlink(tempfiles[0]); 249 if (tempfiles[1] != NULL) 250 unlink(tempfiles[1]); 251 va_start(ap, fmt); 252 verrx(2, fmt, ap); 253 va_end(ap); 254 } 255 256 void 257 set_argstr(char **av, char **ave) 258 { 259 size_t argsize; 260 char **ap; 261 262 argsize = 4 + (char *)ave - (char *)av + 1; 263 diffargs = emalloc(argsize); 264 strlcpy(diffargs, "diff", argsize); 265 for (ap = av + 1; ap < ave; ap++) { 266 if (strcmp(*ap, "--") != 0) { 267 strlcat(diffargs, " ", argsize); 268 strlcat(diffargs, *ap, argsize); 269 } 270 } 271 } 272 273 /* 274 * Read in an excludes file and push each line. 275 */ 276 void 277 read_excludes_file(char *file) 278 { 279 FILE *fp; 280 char *buf, *pattern; 281 size_t len; 282 283 if (strcmp(file, "-") == 0) 284 fp = stdin; 285 else if ((fp = fopen(file, "r")) == NULL) 286 error("%s", file); 287 while ((buf = fgetln(fp, &len)) != NULL) { 288 if (buf[len - 1] == '\n') 289 len--; 290 pattern = emalloc(len + 1); 291 memcpy(pattern, buf, len); 292 pattern[len] = '\0'; 293 push_excludes(pattern); 294 } 295 if (strcmp(file, "-") != 0) 296 fclose(fp); 297 } 298 299 /* 300 * Push a pattern onto the excludes list. 301 */ 302 void 303 push_excludes(char *pattern) 304 { 305 struct excludes *entry; 306 307 entry = emalloc(sizeof(*entry)); 308 entry->pattern = pattern; 309 entry->next = excludes_list; 310 excludes_list = entry; 311 } 312 313 __dead void 314 usage(void) 315 { 316 (void)fprintf(stderr, 317 "usage: diff [-bitw] [-c | -e | -f | -n | -u ] file1 file2\n" 318 " diff [-bitw] -C number file1 file2\n" 319 " diff [-bitw] -D string file1 file2\n" 320 " diff [-bitw] -U number file1 file2\n" 321 " diff [-biNPwt] [-c | -e | -f | -n | -u ] [-r] [-s] [-S name]" 322 " [-X file]\n [-x pattern] dir1 dir2\n"); 323 324 exit(2); 325 } 326