Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include <u.h>
#include <libc.h>
#include <bio.h>

enum{
        LEN     = 8*1024,
        NFLDS   = 6,            /* filename, modes, uid, gid, mtime, bytes */
};

int     selected(char*, int, char*[]);
void    mkdirs(char*, char*);
void    mkdir(char*, ulong, ulong, char*, char*);
void    extract(char*, ulong, ulong, char*, char*, uvlong);
void    seekpast(uvlong);
void    error(char*, ...);
void    warn(char*, ...);
void    usage(void);
#pragma varargck argpos warn 1
#pragma varargck argpos error 1

Biobufhdr bin;
uchar   binbuf[2*LEN];
char    linebuf[LEN];
int     uflag;
int     hflag;
int     vflag;
int     Tflag;

void
main(int argc, char **argv)
{
        Biobuf bout;
        char *fields[NFLDS], name[2*LEN], *p, *namep;
        char *uid, *gid;
        ulong mode, mtime;
        uvlong bytes;

        quotefmtinstall();
        namep = name;
        ARGBEGIN{
        case 'd':
                p = ARGF();
                if(strlen(p) >= LEN)
                        error("destination fs name too long\n");
                strcpy(name, p);
                namep = name + strlen(name);
                break;
        case 'h':
                hflag = 1;
                Binit(&bout, 1, OWRITE);
                break;
        case 'u':
                uflag = 1;
                Tflag = 1;
                break;
        case 'T':
                Tflag = 1;
                break;
        case 'v':
                vflag = 1;
                break;
        default:
                usage();
        }ARGEND
        
        Binits(&bin, 0, OREAD, binbuf, sizeof binbuf);
        while(p = Brdline(&bin, '\n')){
                p[Blinelen(&bin)-1] = '\0';
                strcpy(linebuf, p);
                p = linebuf;
                if(strcmp(p, "end of archive") == 0){
                        Bterm(&bout);
                        fprint(2, "done\n");
                        exits(0);
                }
                if (gettokens(p, fields, NFLDS, " \t") != NFLDS){
                        warn("too few fields in file header");
                        continue;
                }
                p = unquotestrdup(fields[0]);
                strcpy(namep, p);
                free(p);
                mode = strtoul(fields[1], 0, 8);
                uid = fields[2];
                gid = fields[3];
                mtime = strtoul(fields[4], 0, 10);
                bytes = strtoull(fields[5], 0, 10);
                if(argc){
                        if(!selected(namep, argc, argv)){
                                if(bytes)
                                        seekpast(bytes);
                                continue;
                        }
                        mkdirs(name, namep);
                }
                if(hflag){
                        Bprint(&bout, "%q %luo %q %q %lud %llud\n",
                                name, mode, uid, gid, mtime, bytes);
                        if(bytes)
                                seekpast(bytes);
                        continue;
                }
                if(mode & DMDIR)
                        mkdir(name, mode, mtime, uid, gid);
                else
                        extract(name, mode, mtime, uid, gid, bytes);
        }
        fprint(2, "premature end of archive\n");
        exits("premature end of archive");
}

int
fileprefix(char *prefix, char *s)
{
        while(*prefix)
                if(*prefix++ != *s++)
                        return 0;
        if(*s && *s != '/')
                return 0;
        return 1;
}

int
selected(char *s, int argc, char *argv[])
{
        int i;

        for(i=0; i<argc; i++)
                if(fileprefix(argv[i], s))
                        return 1;
        return 0;
}

void
mkdirs(char *name, char *namep)
{
        char buf[2*LEN], *p;
        int fd;

        strcpy(buf, name);
        for(p = &buf[namep - name]; p = utfrune(p, '/'); p++){
                if(p[1] == '\0')
                        return;
                *p = 0;
                fd = create(buf, OREAD, 0775|DMDIR);
                close(fd);
                *p = '/';
        }
}

void
mkdir(char *name, ulong mode, ulong mtime, char *uid, char *gid)
{
        Dir *d, xd;
        int fd;
        char *p;
        char olderr[256];

        fd = create(name, OREAD, mode);
        if(fd < 0){
                rerrstr(olderr, sizeof(olderr));
                if((d = dirstat(name)) == nil || !(d->mode & DMDIR)){
                        free(d);
                        warn("can't make directory %q, mode %luo: %s", name, mode, olderr);
                        return;
                }
                free(d);
        }
        close(fd);

        d = &xd;
        nulldir(d);
        p = utfrrune(name, L'/');
        if(p)
                p++;
        else
                p = name;
        d->name = p;
        if(uflag){
                d->uid = uid;
                d->gid = gid;
        }
        if(Tflag)
                d->mtime = mtime;
        d->mode = mode;
        if(dirwstat(name, d) < 0)
                warn("can't set modes for %q: %r", name);

        if(uflag||Tflag){
                if((d = dirstat(name)) == nil){
                        warn("can't reread modes for %q: %r", name);
                        return;
                }
                if(Tflag && d->mtime != mtime)
                        warn("%q: time mismatch %lud %lud\n", name, mtime, d->mtime);
                if(uflag && strcmp(uid, d->uid))
                        warn("%q: uid mismatch %q %q", name, uid, d->uid);
                if(uflag && strcmp(gid, d->gid))
                        warn("%q: gid mismatch %q %q", name, gid, d->gid);
        }
}

void
extract(char *name, ulong mode, ulong mtime, char *uid, char *gid, uvlong bytes)
{
        Dir d, *nd;
        Biobuf *b;
        char buf[LEN];
        char *p;
        ulong n;
        uvlong tot;

        if(vflag)
                print("x %q %llud bytes\n", name, bytes);

        b = Bopen(name, OWRITE);
        if(!b){
                warn("can't make file %q: %r", name);
                seekpast(bytes);
                return;
        }
        for(tot = 0; tot < bytes; tot += n){
                n = sizeof buf;
                if(tot + n > bytes)
                        n = bytes - tot;
                n = Bread(&bin, buf, n);
                if(n <= 0)
                        error("premature eof reading %q", name);
                if(Bwrite(b, buf, n) != n)
                        warn("error writing %q: %r", name);
        }

        nulldir(&d);
        p = utfrrune(name, '/');
        if(p)
                p++;
        else
                p = name;
        d.name = p;
        if(uflag){
                d.uid = uid;
                d.gid = gid;
        }
        if(Tflag)
                d.mtime = mtime;
        d.mode = mode;
        Bflush(b);
        if(dirfwstat(Bfildes(b), &d) < 0)
                warn("can't set modes for %q: %r", name);
        if(uflag||Tflag){
                if((nd = dirfstat(Bfildes(b))) == nil)
                        warn("can't reread modes for %q: %r", name);
                else{
                        if(Tflag && nd->mtime != mtime)
                                warn("%q: time mismatch %lud %lud\n",
                                        name, mtime, nd->mtime);
                        if(uflag && strcmp(uid, nd->uid))
                                warn("%q: uid mismatch %q %q",
                                        name, uid, nd->uid);
                        if(uflag && strcmp(gid, nd->gid))
                                warn("%q: gid mismatch %q %q",
                                        name, gid, nd->gid);
                        free(nd);
                }
        }
        Bterm(b);
}

void
seekpast(uvlong bytes)
{
        char buf[LEN];
        long n;
        uvlong tot;

        for(tot = 0; tot < bytes; tot += n){
                n = sizeof buf;
                if(tot + n > bytes)
                        n = bytes - tot;
                n = Bread(&bin, buf, n);
                if(n < 0)
                        error("premature eof");
        }
}

void
error(char *fmt, ...)
{
        char buf[1024];
        va_list arg;

        sprint(buf, "%q: ", argv0);
        va_start(arg, fmt);
        vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);
        va_end(arg);
        fprint(2, "%s\n", buf);
        exits(0);
}

void
warn(char *fmt, ...)
{
        char buf[1024];
        va_list arg;

        sprint(buf, "%q: ", argv0);
        va_start(arg, fmt);
        vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);
        va_end(arg);
        fprint(2, "%s\n", buf);
}

void
usage(void)
{
        fprint(2, "usage: mkext [-h] [-u] [-v] [-d dest-fs] [file ...]\n");
        exits("usage");
}