Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#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 [-abcefmnrw] 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);
        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;

        Binit(&stdout, 1, OWRITE);
        progname = argv0 = *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;
                                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;
}