| #include <u.h> |
| #include <libc.h> |
| #include <bio.h> |
| #include "diff.h" |
| |
| #define DIRECTORY(s) ((s)->qid.type&QTDIR) |
| #define REGULAR_FILE(s) ((s)->type == 'M' && !DIRECTORY(s)) |
| |
| Biobuf stdout; |
| |
| static char *tmp[] = {"/tmp/diff1XXXXXXXXXXX", "/tmp/diff2XXXXXXXXXXX"}; |
| static int whichtmp; |
| static char *progname; |
| static char usage[] = "diff [ -acefmnbwr ] file1 ... file2\n"; |
| |
| static void |
| rmtmpfiles(void) |
| { |
| while (whichtmp > 0) { |
| whichtmp--; |
| remove(tmp[whichtmp]); |
| } |
| } |
| |
| void |
| done(int status) |
| { |
| rmtmpfiles(); |
| switch(status) |
| { |
| case 0: |
| exits(""); |
| case 1: |
| exits("some"); |
| default: |
| exits("error"); |
| } |
| /*NOTREACHED*/ |
| } |
| |
| void |
| panic(int status, char *fmt, ...) |
| { |
| va_list arg; |
| |
| Bflush(&stdout); |
| |
| fprint(2, "%s: ", progname); |
| va_start(arg, fmt); |
| vfprint(2, fmt, arg); |
| va_end(arg); |
| if (status) |
| done(status); |
| /*NOTREACHED*/ |
| } |
| |
| static int |
| catch(void *a, char *msg) |
| { |
| USED(a); |
| panic(2, msg); |
| return 1; |
| } |
| |
| int |
| mkpathname(char *pathname, char *path, char *name) |
| { |
| if (strlen(path) + strlen(name) > MAXPATHLEN) { |
| panic(0, "pathname %s/%s too long\n", path, name); |
| return 1; |
| } |
| sprint(pathname, "%s/%s", path, name); |
| return 0; |
| } |
| |
| static char * |
| mktmpfile(int input, Dir **sb) |
| { |
| int fd, i; |
| char *p; |
| char buf[8192]; |
| |
| atnotify(catch, 1); |
| /* |
| p = mktemp(tmp[whichtmp++]); |
| fd = create(p, OWRITE, 0600); |
| */ |
| fd = mkstemp(p=tmp[whichtmp++]); |
| if (fd < 0) { |
| panic(mflag ? 0: 2, "cannot create %s: %r\n", p); |
| return 0; |
| } |
| while ((i = read(input, buf, sizeof(buf))) > 0) { |
| if ((i = write(fd, buf, i)) < 0) |
| break; |
| } |
| *sb = dirfstat(fd); |
| close(fd); |
| if (i < 0) { |
| panic(mflag ? 0: 2, "cannot read/write %s: %r\n", p); |
| return 0; |
| } |
| return p; |
| } |
| |
| static char * |
| statfile(char *file, Dir **sb) |
| { |
| Dir *dir; |
| int input; |
| |
| dir = dirstat(file); |
| if(dir == nil) { |
| if (strcmp(file, "-") || (dir = dirfstat(0)) == nil) { |
| panic(mflag ? 0: 2, "cannot stat %s: %r\n", file); |
| return 0; |
| } |
| free(dir); |
| return mktmpfile(0, sb); |
| } |
| else if (!REGULAR_FILE(dir) && !DIRECTORY(dir)) { |
| free(dir); |
| if ((input = open(file, OREAD)) == -1) { |
| panic(mflag ? 0: 2, "cannot open %s: %r\n", file); |
| return 0; |
| } |
| file = mktmpfile(input, sb); |
| close(input); |
| } |
| else |
| *sb = dir; |
| return file; |
| } |
| |
| void |
| diff(char *f, char *t, int level) |
| { |
| char *fp, *tp, *p, fb[MAXPATHLEN+1], tb[MAXPATHLEN+1]; |
| Dir *fsb, *tsb; |
| |
| if ((fp = statfile(f, &fsb)) == 0) |
| goto Return; |
| if ((tp = statfile(t, &tsb)) == 0){ |
| free(fsb); |
| goto Return; |
| } |
| if (DIRECTORY(fsb) && DIRECTORY(tsb)) { |
| if (rflag || level == 0) |
| diffdir(fp, tp, level); |
| else |
| Bprint(&stdout, "Common subdirectories: %s and %s\n", |
| fp, tp); |
| } |
| else if (REGULAR_FILE(fsb) && REGULAR_FILE(tsb)) |
| diffreg(fp, tp); |
| else { |
| if (REGULAR_FILE(fsb)) { |
| if ((p = utfrrune(f, '/')) == 0) |
| p = f; |
| else |
| p++; |
| if (mkpathname(tb, tp, p) == 0) |
| diffreg(fp, tb); |
| } |
| else { |
| if ((p = utfrrune(t, '/')) == 0) |
| p = t; |
| else |
| p++; |
| if (mkpathname(fb, fp, p) == 0) |
| diffreg(fb, tp); |
| } |
| } |
| free(fsb); |
| free(tsb); |
| Return: |
| rmtmpfiles(); |
| } |
| |
| void |
| main(int argc, char *argv[]) |
| { |
| char *p; |
| int i; |
| Dir *fsb, *tsb; |
| extern int _p9usepwlibrary; |
| |
| _p9usepwlibrary = 0; |
| Binit(&stdout, 1, OWRITE); |
| progname = *argv; |
| while (--argc && (*++argv)[0] == '-' && (*argv)[1]) { |
| for (p = *argv+1; *p; p++) { |
| switch (*p) { |
| |
| case 'e': |
| case 'f': |
| case 'n': |
| case 'c': |
| case 'a': |
| mode = *p; |
| break; |
| |
| case 'w': |
| bflag = 2; |
| break; |
| |
| case 'b': |
| bflag = 1; |
| break; |
| |
| case 'r': |
| rflag = 1; |
| mflag = 1; |
| break; |
| |
| case 'm': |
| mflag = 1; |
| break; |
| |
| case 'h': |
| default: |
| progname = "Usage"; |
| panic(2, usage); |
| } |
| } |
| } |
| if (argc < 2) |
| panic(2, usage, progname); |
| if ((tsb = dirstat(argv[argc-1])) == nil) |
| panic(2, "can't stat %s\n", argv[argc-1]); |
| if (argc > 2) { |
| if (!DIRECTORY(tsb)) |
| panic(2, usage, progname); |
| mflag = 1; |
| } |
| else { |
| if ((fsb = dirstat(argv[0])) == nil) |
| panic(2, "can't stat %s\n", argv[0]); |
| if (DIRECTORY(fsb) && DIRECTORY(tsb)) |
| mflag = 1; |
| free(fsb); |
| } |
| free(tsb); |
| for (i = 0; i < argc-1; i++) |
| diff(argv[i], argv[argc-1], 0); |
| done(anychange); |
| /*NOTREACHED*/ |
| } |
| |
| static char noroom[] = "out of memory - try diff -h\n"; |
| |
| void * |
| emalloc(unsigned n) |
| { |
| register void *p; |
| |
| if ((p = malloc(n)) == 0) |
| panic(2, noroom); |
| return p; |
| } |
| |
| void * |
| erealloc(void *p, unsigned n) |
| { |
| register void *rp; |
| |
| if ((rp = realloc(p, n)) == 0) |
| panic(2, noroom); |
| return rp; |
| } |