From 45001b2700f817cc4a3609574031437040de393e Mon Sep 17 00:00:00 2001 From: Ori Bernstein Date: Sun, 18 Feb 2024 04:54:36 +0000 Subject: [PATCH] sync with 9front --- conf.c | 4 +- fs.c | 17 +- get.c | 2 +- git.h | 25 ++- log.c | 7 +- pack.c | 63 ++++-- proto.c | 6 +- query.c | 29 +-- ref.c | 14 +- save.c | 87 +++++--- send.c | 9 +- serve.c | 118 +++------- util.c | 34 ++- walk.c | 657 ++++++++++++++++++++++++++++++++++++++------------------ 14 files changed, 711 insertions(+), 361 deletions(-) diff --git a/conf.c b/conf.c index 63cf475..051a6f6 100644 --- a/conf.c +++ b/conf.c @@ -59,7 +59,7 @@ void main(int argc, char **argv) { char repo[512], *p, *s; - int i, j; + int i, j, nrel; ARGBEGIN{ case 'f': file[nfile++]=EARGF(usage()); break; @@ -69,7 +69,7 @@ main(int argc, char **argv) }ARGEND; if(findroot){ - if(findrepo(repo, sizeof(repo)) == -1) + if(findrepo(repo, sizeof(repo), &nrel) == -1) sysfatal("%r"); print("%s\n", repo); exits(nil); diff --git a/fs.c b/fs.c index 7e6b4bf..9612450 100644 --- a/fs.c +++ b/fs.c @@ -71,11 +71,10 @@ char *qroot[] = { }; #define Eperm "permission denied" -#define Eexist "does not exist" +#define Eexist "file does not exist" #define E2long "path too long" #define Enodir "not a directory" #define Erepo "unable to read repo" -#define Eobject "invalid object" #define Egreg "wat" #define Ebadobj "invalid object" @@ -305,6 +304,9 @@ gcommitgen(int i, Dir *d, void *p) d->qid.path = qpath(c, i, o->id, Qauthor); break; default: + free(d->uid); + free(d->gid); + free(d->muid); return -1; } return 0; @@ -453,6 +455,9 @@ walklink(Gitaux *aux, char *link, int nlink, int ndotdot, int *mode) break; } free(path); + for(i = 0; o != nil && i < aux->ncrumb; i++) + if(crumb(aux, i)->obj == o) + return nil; return o; } @@ -482,7 +487,7 @@ objwalk1(Qid *q, Object *o, Crumb *p, Crumb *c, char *name, vlong qdir, Gitaux * if(!w) return Ebadobj; q->type = (w->type == GTree) ? QTDIR : 0; - q->path = qpath(c, i, w->id, qdir); + q->path = qpath(p, i, w->id, qdir); c->mode = m; c->mode |= (w->type == GTree) ? DMDIR|0755 : 0644; c->obj = w; @@ -618,6 +623,8 @@ gitwalk1(Fid *fid, char *name, Qid *q) q->path = qpath(o, Qbranch, c->obj->id, Qcommit); else e = Eexist; + if(d != nil) + c->mode = d->mode & ~0222; free(d); break; case Qobject: @@ -625,9 +632,9 @@ gitwalk1(Fid *fid, char *name, Qid *q) e = objwalk1(q, o->obj, o, c, name, Qobject, aux); }else{ if(hparse(&h, name) == -1) - return Eobject; + return Ebadobj; if((c->obj = readobject(h)) == nil) - return Eobject; + return Ebadobj; if(c->obj->type == GBlob || c->obj->type == GTag){ c->mode = 0644; q->type = 0; diff --git a/get.c b/get.c index 77547c0..1c0d336 100644 --- a/get.c +++ b/get.c @@ -280,7 +280,7 @@ fetchpack(Conn *c) if(hasheq(&have[i], &Zhash) || oshas(&hadobj, have[i])) continue; if((o = readobject(have[i])) == nil) - sysfatal("missing object we should have: %H", have[i]); + sysfatal("missing exected object: %H", have[i]); if(fmtpkt(c, "have %H", o->hash) == -1) sysfatal("write: %r"); enqueueparent(&haveq, o); diff --git a/git.h b/git.h index c3ba7b7..229fa17 100644 --- a/git.h +++ b/git.h @@ -10,6 +10,7 @@ typedef struct Hash Hash; typedef struct Delta Delta; typedef struct Cinfo Cinfo; typedef struct Tinfo Tinfo; +typedef struct Ginfo Ginfo; typedef struct Object Object; typedef struct Objset Objset; typedef struct Pack Pack; @@ -21,6 +22,7 @@ typedef struct Dtab Dtab; typedef struct Dblock Dblock; typedef struct Objq Objq; typedef struct Qelt Qelt; +typedef struct Idxent Idxent; enum { Pathmax = 512, @@ -128,6 +130,7 @@ struct Object { union { Cinfo *commit; Tinfo *tree; + Ginfo *tag; }; }; @@ -150,6 +153,18 @@ struct Cinfo { vlong mtime; }; +struct Ginfo { + /* Tag */ + Hash object; + char *tagger; + char *type; + char *tag; + char *msg; + int nmsg; + vlong ctime; + vlong mtime; +}; + struct Objset { Object **obj; int nobj; @@ -190,6 +205,13 @@ struct Delta { int len; }; +struct Idxent { + char *path; + Qid qid; + int mode; + int order; + char state; +}; #define GETBE16(b)\ ((((b)[0] & 0xFFul) << 8) | \ @@ -301,9 +323,10 @@ int hparse(Hash *, char *); int hassuffix(char *, char *); int swapsuffix(char *, int, char *, char *, char *); char *strip(char *); -int findrepo(char *, int); +int findrepo(char *, int, int*); int showprogress(int, int); u64int murmurhash2(void*, usize); +Qid parseqid(char*); /* packing */ void dtinit(Dtab *, Object*); diff --git a/log.c b/log.c index 3336503..97d71a3 100644 --- a/log.c +++ b/log.c @@ -207,6 +207,8 @@ showcommits(char *c) sysfatal("resolve %s: %r", c); if((o = readobject(h)) == nil) sysfatal("load %H: %r", h); + if(o->type != GCommit) + sysfatal("%s: not a commit", c); qinit(&objq); osinit(&done); qput(&objq, o, 0); @@ -239,7 +241,7 @@ void main(int argc, char **argv) { char path[1024], repo[1024], *p, *r; - int i, nrepo; + int i, nrel, nrepo; ARGBEGIN{ case 'e': @@ -259,7 +261,7 @@ main(int argc, char **argv) break; }ARGEND; - if(findrepo(repo, sizeof(repo)) == -1) + if(findrepo(repo, sizeof(repo), &nrel) == -1) sysfatal("find root: %r"); nrepo = strlen(repo); if(argc != 0){ @@ -291,5 +293,6 @@ main(int argc, char **argv) showquery(queryexpr); else showcommits(commitid); + Bterm(out); exits(nil); } diff --git a/pack.c b/pack.c index d361161..e557897 100644 --- a/pack.c +++ b/pack.c @@ -66,7 +66,7 @@ Objset objcache; Object *lruhead; Object *lrutail; vlong ncache; -vlong cachemax = 512*MiB; +vlong cachemax = 128*MiB; Packf *packf; int npackf; int openpacks; @@ -160,7 +160,7 @@ cache(Object *o) ref(o); ncache += o->size; } - while(ncache > cachemax && lrutail != nil){ + while(ncache > cachemax && lrutail != lruhead){ p = lrutail; lrutail = p->prev; if(lrutail != nil) @@ -825,6 +825,7 @@ parseauthor(char **str, int *nstr, char **name, vlong *time) { char buf[128]; Resub m[4]; + vlong tz; char *p; int n, nm; @@ -845,10 +846,16 @@ parseauthor(char **str, int *nstr, char **name, vlong *time) memcpy(*name, m[1].sp, nm); buf[nm] = 0; + nm = m[3].ep - m[3].sp; + memcpy(buf, m[3].sp, nm); + buf[nm] = 0; + tz = atoll(buf); + nm = m[2].ep - m[2].sp; memcpy(buf, m[2].sp, nm); buf[nm] = 0; - *time = atoll(buf); + *time = atoll(buf) + 3600*(tz/100) + 60*(tz%100); + return 0; } @@ -900,6 +907,46 @@ parsecommit(Object *o) o->commit->nmsg = np; } +static void +parsetag(Object *o) +{ + char *p, buf[128]; + int np; + + p = o->data; + np = o->size; + o->tag = emalloc(sizeof(Ginfo)); + while(1){ + if(scanword(&p, &np, buf, sizeof(buf)) == -1) + break; + if(strcmp(buf, "object") == 0){ + if(scanword(&p, &np, buf, sizeof(buf)) == -1) + sysfatal("invalid commit: tree missing"); + if(hparse(&o->tag->object, buf) == -1) + sysfatal("invalid commit: garbled tree"); + }else if(strcmp(buf, "tagger") == 0){ + parseauthor(&p, &np, &o->commit->author, &o->tag->mtime); + }else if(strcmp(buf, "type") == 0){ + if(scanword(&p, &np, buf, sizeof(buf)) == -1) + sysfatal("bad tag type"); + if((o->tag->type = strdup(buf)) == nil) + sysfatal("strdup: %r"); + }else if(strcmp(buf, "tag") == 0){ + if(scanword(&p, &np, buf, sizeof(buf)) == -1) + sysfatal("bad tag type"); + if((o->tag->type = strdup(buf)) == nil) + sysfatal("strdup: %r"); + } + nextline(&p, &np); + } + while (np && isspace(*p)) { + p++; + np--; + } + o->commit->msg = p; + o->commit->nmsg = np; +} + static void parsetree(Object *o) { @@ -954,12 +1001,6 @@ parsetree(Object *o) o->tree->ent = ent; o->tree->nent = nent; } - -static void -parsetag(Object *) -{ -} - void parseobject(Object *o) { @@ -1210,10 +1251,8 @@ indexpack(char *pack, char *idx, Hash ph) if(objectcrc(f, o) == -1) return -1; } - if(n == nvalid){ + if(n == nvalid) sysfatal("fix point reached too early: %d/%d: %r", nvalid, nobj); - goto error; - } nvalid = n; } if(interactive) diff --git a/proto.c b/proto.c index 9af09ec..3f5292a 100644 --- a/proto.c +++ b/proto.c @@ -58,8 +58,10 @@ readpkt(Conn *c, char *buf, int nbuf) char *e; int n; - if(readn(c->rfd, len, 4) != 4) - sysfatal("pktline: short read from transport"); + if(readn(c->rfd, len, 4) != 4){ + werrstr("pktline: short read from transport"); + return -1; + } len[4] = 0; n = strtol(len, &e, 16); if(n == 0){ diff --git a/query.c b/query.c index 40020ea..d028831 100644 --- a/query.c +++ b/query.c @@ -152,10 +152,10 @@ usage(void) void main(int argc, char **argv) { - int i, j, n; + char *query, repo[512]; + char *p, *e, *objpfx; + int i, j, n, nrel; Hash *h; - char *p, *e, *s, *objpfx; - char query[2048], repo[512]; ARGBEGIN{ case 'd': chattygit++; break; @@ -170,25 +170,26 @@ main(int argc, char **argv) if(argc == 0) usage(); - if(findrepo(repo, sizeof(repo)) == -1) + if(findrepo(repo, sizeof(repo), &nrel) == -1) sysfatal("find root: %r"); if(chdir(repo) == -1) sysfatal("chdir: %r"); if((objpfx = smprint("%s/.git/fs/object/", repo)) == nil) sysfatal("smprint: %r"); - s = ""; + for(i = 0, n = 0; i < argc; i++) + n += strlen(argv[i]) + 1; + query = emalloc(n+1); p = query; - e = query + nelem(query); - for(i = 0; i < argc; i++){ - p = seprint(p, e, "%s%s", s, argv[i]); - s = " "; - } - if((n = resolverefs(&h, query)) == -1) + e = query + n; + for(i = 0; i < argc; i++) + p = seprint(p, e, "%s ", argv[i]); + n = resolverefs(&h, query); + free(query); + if(n == -1) sysfatal("resolve: %r"); if(changes){ - if(n != 2) - sysfatal("diff: need 2 commits, got %d", n); - diffcommits(h[0], h[1]); + for(i = 1; i < n; i++) + diffcommits(h[0], h[i]); }else{ p = (fullpath ? objpfx : ""); for(j = 0; j < n; j++) diff --git a/ref.c b/ref.c index bf798e2..0cb5e5a 100644 --- a/ref.c +++ b/ref.c @@ -125,8 +125,10 @@ paint(Hash *head, int nhead, Hash *tail, int ntail, Object ***res, int *nres, in nskip = 0; for(i = 0; i < nhead; i++){ + if(hasheq(&head[i], &Zhash)) + continue; if((o = readobject(head[i])) == nil){ - fprint(2, "warning: %H does not point at commit\n", o->hash); + fprint(2, "warning: %H does not point at commit\n", head[i]); werrstr("read head %H: %r", head[i]); return -1; } @@ -140,6 +142,8 @@ paint(Hash *head, int nhead, Hash *tail, int ntail, Object ***res, int *nres, in unref(o); } for(i = 0; i < ntail; i++){ + if(hasheq(&tail[i], &Zhash)) + continue; if((o = readobject(tail[i])) == nil){ werrstr("read tail %H: %r", tail[i]); return -1; @@ -184,6 +188,10 @@ paint(Hash *head, int nhead, Hash *tail, int ntail, Object ***res, int *nres, in break; } o = readobject(e.o->hash); + if(o->type != GCommit){ + werrstr("not a commit: %H", o->hash); + goto error; + } for(i = 0; i < o->commit->nparent; i++){ if((c = readobject(e.o->commit->parent[i])) == nil) goto error; @@ -301,6 +309,10 @@ parent(Eval *ev) Object *o, *p; o = pop(ev); + if(o->type != GCommit){ + werrstr("not a commit: %H", o->hash); + return -1; + } /* Special case: first commit has no parent. */ if(o->commit->nparent == 0) p = emptydir(); diff --git a/save.c b/save.c index 08a1a50..b41a528 100644 --- a/save.c +++ b/save.c @@ -10,6 +10,7 @@ struct Objbuf { char *dat; int ndat; }; + enum { Maxparents = 16, }; @@ -21,7 +22,9 @@ char *committeremail; char *commitmsg; Hash parents[Maxparents]; int nparents; - +Idxent *idx; +int idxsz; +int nidx; int gitmode(Dirent *e) { @@ -31,12 +34,26 @@ gitmode(Dirent *e) return 0160000; else if(e->mode & DMDIR) return 0040000; - else if(e->mode & 0111) + else if(e->mode & 0100) return 0100755; else return 0100644; } +int +idxcmp(void *pa, void *pb) +{ + Idxent *a, *b; + int c; + + a = (Idxent*)pa; + b = (Idxent*)pb; + if((c = strcmp(a->path, b->path)) != 0) + return c; + assert(a->order != b->order); + return a-> order < b->order ? -1 : 1; +} + int entcmp(void *pa, void *pb) { @@ -184,27 +201,21 @@ blobify(Dir *d, char *path, int *mode, Hash *bh) int tracked(char *path) { - char ipath[256]; - Dir *d; - - /* Explicitly removed. */ - snprint(ipath, sizeof(ipath), ".git/index9/removed/%s", path); - if(strstr(cleanname(ipath), ".git/index9/removed") != ipath) - sysfatal("path %s leaves index", ipath); - d = dirstat(ipath); - if(d != nil && d->qid.type != QTDIR){ - free(d); - return 0; + int r, lo, hi, mid; + + lo = 0; + hi = nidx-1; + while(lo <= hi){ + mid = (hi + lo) / 2; + r = strcmp(path, idx[mid].path); + if(r < 0) + hi = mid-1; + else if(r > 0) + lo = mid+1; + else + return idx[mid].state != 'R'; } - - /* Explicitly added. */ - snprint(ipath, sizeof(ipath), ".git/index9/tracked/%s", path); - if(strstr(cleanname(ipath), ".git/index9/tracked") != ipath) - sysfatal("path %s leaves index", ipath); - if(access(ipath, AEXIST) == 0) - return 1; - - return 0; + return 0; } int @@ -354,10 +365,11 @@ usage(void) void main(int argc, char **argv) { + char *ln, *dstr, *parts[4], cwd[1024]; + int i, r, line, ncwd; Hash th, ch; - char *dstr, cwd[1024]; - int i, r, ncwd; vlong date; + Biobuf *f; Object *t; gitinit(); @@ -425,6 +437,33 @@ main(int argc, char **argv) } t = findroot(); + nidx = 0; + idxsz = 32; + idx = emalloc(idxsz*sizeof(Idxent)); + if((f = Bopen(".git/INDEX9", OREAD)) == nil) + sysfatal("open index: %r"); + line = 0; + while((ln = Brdstr(f, '\n', 1)) != nil){ + line++; + if(ln[0] == 0 || ln[0] == '\n') + continue; + if(getfields(ln, parts, nelem(parts), 0, " \t") != nelem(parts)) + sysfatal(".git/INDEX9:%d: corrupt index", line); + if(nidx == idxsz){ + idxsz += idxsz/2; + idx = realloc(idx, idxsz*sizeof(Idxent)); + } + cleanname(parts[3]); + idx[nidx].state = *parts[0]; + idx[nidx].qid = parseqid(parts[1]); + idx[nidx].mode = strtol(parts[2], nil, 8); + idx[nidx].path = strdup(parts[3]); + idx[nidx].order = nidx; + nidx++; + free(ln); + } + Bterm(f); + qsort(idx, nidx, sizeof(Idxent), idxcmp); r = treeify(t, argv, argv + argc, 0, &th); if(r == -1) sysfatal("could not commit: %r\n"); diff --git a/send.c b/send.c index 1188a71..e83d686 100644 --- a/send.c +++ b/send.c @@ -75,9 +75,11 @@ readours(Hash **tailp, char ***refp) sysfatal("smprint: %r"); if((idx = findref(ref, nu, r)) == -1) idx = nu++; + else + free(ref[idx]); assert(idx < nremoved + nbranch); memcpy(&tail[idx], &Zhash, sizeof(Hash)); - free(r); + ref[idx] = r; } dprint(1, "nu: %d\n", nu); for(i = 0; i < nu; i++) @@ -184,7 +186,10 @@ sendpack(Conn *c) p = nil; if(a != nil && b != nil) p = ancestor(a, b); - if(!force && !hasheq(&m->theirs, &Zhash) && (a == nil || p != a)){ + if(!force + && !hasheq(&m->theirs, &Zhash) + && !hasheq(&m->ours, &Zhash) + && (a == nil || p != a)){ fprint(2, "remote has diverged\n"); werrstr("remote diverged"); flushpkt(c); diff --git a/serve.c b/serve.c index 420a2ad..da7cd56 100644 --- a/serve.c +++ b/serve.c @@ -7,28 +7,12 @@ char *pathpfx = nil; int allowwrite; -int report; - -int -parsecaps(char *caps) -{ - char *p, *n; - - for(p = caps; p != nil; p = n){ - if((n = strchr(p, ' ')) != nil) - *n++ = 0; - if(strcmp(p, "report-status") == 0) - report = 1; - } - return 0; -} int showrefs(Conn *c) { int i, ret, nrefs; Hash head, *refs; - char *p, *e, pkt[Pktmax]; char **names; ret = -1; @@ -44,14 +28,7 @@ showrefs(Conn *c) for(i = 0; i < nrefs; i++){ if(strncmp(names[i], "heads/", strlen("heads/")) != 0) continue; - p = pkt; - e = pkt+sizeof(pkt); - p = seprint(p, e, "%H refs/%s\n", refs[i], names[i]); - if(i == 0){ - *p++ = 0; - p = seprint(p, e, "report-status"); - } - if(writepkt(c, pkt, p-pkt) == -1) + if(fmtpkt(c, "%H refs/%s\n", refs[i], names[i]) == -1) goto error; } if(flushpkt(c) == -1) @@ -68,8 +45,8 @@ showrefs(Conn *c) int servnegotiate(Conn *c, Hash **head, int *nhead, Hash **tail, int *ntail) { - char *sp[3], pkt[Pktmax]; - int n, nsp, acked; + char pkt[Pktmax]; + int n, acked; Object *o; Hash h; @@ -85,22 +62,14 @@ servnegotiate(Conn *c, Hash **head, int *nhead, Hash **tail, int *ntail) goto error; if(n == 0) break; - if((nsp = getfields(pkt, sp, nelem(sp), 1, " \t")) < 2){ - werrstr("protocol garble %s", pkt); - goto error; - } - if(strcmp(sp[0], "want") != 0){ + if(strncmp(pkt, "want ", 5) != 0){ werrstr(" protocol garble %s", pkt); goto error; } - if(hparse(&h, sp[1]) == -1){ + if(hparse(&h, &pkt[5]) == -1){ werrstr(" garbled want"); goto error; } - if(nsp > 2 && parsecaps(sp[2]) == -1){ - werrstr("garbled caps %s", sp[2]); - goto error; - } if((o = readobject(h)) == nil){ werrstr("requested nonexistent object"); goto error; @@ -182,7 +151,7 @@ recvnegotiate(Conn *c, Hash **cur, Hash **upd, char ***ref, int *nupd) { char pkt[Pktmax], *sp[4]; Hash old, new; - int l, n, i; + int n, i; if(showrefs(c) == -1) return -1; @@ -195,11 +164,6 @@ recvnegotiate(Conn *c, Hash **cur, Hash **upd, char ***ref, int *nupd) goto error; if(n == 0) break; - l = strlen(pkt); - if(n > l+1 && parsecaps(pkt+l+1) == -1){ - fmtpkt(c, "ERR protocol garble %s\n", pkt); - goto error; - } if(getfields(pkt, sp, nelem(sp), 1, " \t\n\r") != 3){ fmtpkt(c, "ERR protocol garble %s\n", pkt); goto error; @@ -350,26 +314,21 @@ updatepack(Conn *c) packsz += n; } if(checkhash(pfd, packsz, &h) == -1){ - werrstr("hash mismatch\n"); + dprint(1, "hash mismatch\n"); goto error1; } if(indexpack(packtmp, idxtmp, h) == -1){ - werrstr("indexing failed: %r\n"); + dprint(1, "indexing failed: %r\n"); goto error1; } if(rename(packtmp, idxtmp, h) == -1){ - werrstr("rename failed: %r\n"); + dprint(1, "rename failed: %r\n"); goto error2; } - if(report) - fmtpkt(c, "unpack ok"); return 0; error2: remove(idxtmp); error1: remove(packtmp); - dprint(1, "update pack: %r"); - if(report) - fmtpkt(c, "unpack %r"); return -1; } @@ -389,13 +348,12 @@ lockrepo(void) int updaterefs(Conn *c, Hash *cur, Hash *upd, char **ref, int nupd) { - char refpath[512]; - int i, j, newidx, hadref, fd, ret, lockfd; + char refpath[512], buf[128]; + int i, newidx, hadref, fd, ret, lockfd; vlong newtm; Object *o; Hash h; - i = 0; ret = -1; hadref = 0; newidx = -1; @@ -406,32 +364,32 @@ updaterefs(Conn *c, Hash *cur, Hash *upd, char **ref, int nupd) */ newtm = -23811206400; if((lockfd = lockrepo()) == -1){ - werrstr("repo locked\n"); - goto out; + snprint(buf, sizeof(buf), "repo locked\n"); + return -1; } for(i = 0; i < nupd; i++){ if(resolveref(&h, ref[i]) == 0){ hadref = 1; if(!hasheq(&h, &cur[i])){ - werrstr("old ref changed: %s", ref[i]); - goto out; + snprint(buf, sizeof(buf), "old ref changed: %s", ref[i]); + goto error; } } if(snprint(refpath, sizeof(refpath), ".git/%s", ref[i]) == sizeof(refpath)){ - werrstr("ref path too long: %s", ref[i]); - goto out; + snprint(buf, sizeof(buf), "ref path too long: %s", ref[i]); + goto error; } if(hasheq(&upd[i], &Zhash)){ remove(refpath); continue; } if((o = readobject(upd[i])) == nil){ - werrstr("update to nonexistent hash %H", upd[i]); - goto out; + snprint(buf, sizeof(buf), "update to nonexistent hash %H", upd[i]); + goto error; } if(o->type != GCommit){ - werrstr("not commit: %H", upd[i]); - goto out; + snprint(buf, sizeof(buf), "not commit: %H", upd[i]); + goto error; } if(o->commit->mtime > newtm){ newtm = o->commit->mtime; @@ -439,13 +397,13 @@ updaterefs(Conn *c, Hash *cur, Hash *upd, char **ref, int nupd) } unref(o); if((fd = create(refpath, OWRITE|OTRUNC, 0644)) == -1){ - werrstr("open ref: %r"); - goto out; + snprint(buf, sizeof(buf), "open ref: %r"); + goto error; } if(fprint(fd, "%H", upd[i]) == -1){ - werrstr("upate ref: %r"); + snprint(buf, sizeof(buf), "upate ref: %r"); close(fd); - goto out; + goto error; } close(fd); } @@ -462,30 +420,22 @@ updaterefs(Conn *c, Hash *cur, Hash *upd, char **ref, int nupd) * use. This should make us pick a useful default in * those cases, instead of silently failing. */ - i = 0; if(resolveref(&h, "HEAD") == -1 && hadref == 0 && newidx != -1){ if((fd = create(".git/HEAD", OWRITE|OTRUNC, 0644)) == -1){ - werrstr("open HEAD: %r"); - goto out; + snprint(buf, sizeof(buf), "open HEAD: %r"); + goto error; } - if(fprint(fd, "ref: %s", ref[i]) == -1){ - werrstr("write HEAD ref: %r"); - goto out; + if(fprint(fd, "ref: %s", ref[0]) == -1){ + snprint(buf, sizeof(buf), "write HEAD ref: %r"); + goto error; } close(fd); } ret = 0; -out: - if(report){ - for(j = 0; j < nupd; j++){ - if(i != j || ret == 0) - fmtpkt(c, "ok %s", ref[i]); - else - fmtpkt(c, "ng %s %r\n", ref[i]); - } - } - if(lockfd != -1) - close(lockfd); +error: + fmtpkt(c, "ERR %s", buf); + close(lockfd); + werrstr(buf); return ret; } diff --git a/util.c b/util.c index 99b1f8e..d53b0b7 100644 --- a/util.c +++ b/util.c @@ -192,8 +192,10 @@ Qfmt(Fmt *fmt) Qid q; q = va_arg(fmt->args, Qid); - return fmtprint(fmt, "Qid{path=0x%llx(dir:%d,obj:%lld), vers=%ld, type=%d}", - q.path, QDIR(&q), (q.path >> 8), q.vers, q.type); + if(q.path == ~0ULL && q.vers == ~0UL && q.type == 0xff) + return fmtprint(fmt, "NOQID"); + else + return fmtprint(fmt, "%llux.%lud.%hhx", q.path, q.vers, q.type); } void @@ -205,7 +207,7 @@ gitinit(void) fmtinstall('Q', Qfmt); inflateinit(); deflateinit(); - authorpat = regcomp("[\t ]*(.*)[\t ]+([0-9]+)[\t ]+([\\-+]?[0-9]+)"); + authorpat = regcomp("[\t ]*(.*)[\t ]+([0-9]+)[\t ]*([\\-+]?[0-9]+)?"); osinit(&objcache); } @@ -294,7 +296,7 @@ _dprint(char *fmt, ...) /* Finds the directory containing the git repo. */ int -findrepo(char *buf, int nbuf) +findrepo(char *buf, int nbuf, int *nrel) { char *p, *suff; @@ -302,12 +304,14 @@ findrepo(char *buf, int nbuf) if(getwd(buf, nbuf - strlen(suff) - 1) == nil) return -1; + *nrel = 0; for(p = buf + strlen(buf); p != nil; p = strrchr(buf, '/')){ strcpy(p, suff); if(access(buf, AEXIST) == 0){ p[p == buf] = '\0'; return 0; } + *nrel += 1; *p = '\0'; } werrstr("not a git repository"); @@ -347,6 +351,7 @@ qput(Objq *q, Object *o, int color) Qelt t; int i; + assert(o->type == GCommit); if(q->nheap == q->heapsz){ q->heapsz *= 2; q->heap = earealloc(q->heap, q->heapsz, sizeof(Qelt)); @@ -442,3 +447,24 @@ murmurhash2(void *pp, usize n) return h; } + +Qid +parseqid(char *s) +{ + char *e; + Qid q; + + if(strcmp(s, "NOQID") == 0) + return (Qid){-1, -1, -1}; + e = s; + q.path = strtoull(e, &e, 16); + if(*e != '.') + sysfatal("corrupt qid: %s (%s)\n", s, e); + q.vers = strtoul(e+1, &e, 10); + if(*e != '.') + sysfatal("corrupt qid: %s (%s)\n", s, e); + q.type = strtoul(e+1, &e, 16); + if(*e != '\0') + sysfatal("corrupt qid: %s (%x)\n", s, *e); + return q; +} diff --git a/walk.c b/walk.c index b6a2d63..03d0f38 100644 --- a/walk.c +++ b/walk.c @@ -2,52 +2,72 @@ #include #include "git.h" +typedef struct Seen Seen; +typedef struct Idxed Idxed; +typedef struct Idxent Idxent; + #define NCACHE 4096 -#define TDIR ".git/index9/tracked" -#define RDIR ".git/index9/removed" -#define HDIR ".git/fs/HEAD/tree" -typedef struct Cache Cache; -typedef struct Wres Wres; -struct Cache { + +enum { + Rflg = 1 << 0, + Mflg = 1 << 1, + Aflg = 1 << 2, + Uflg = 1 << 3, + /* everything after this is not an error */ + Tflg = 1 << 4, +}; + +struct Seen { Dir* cache; int n; int max; }; -struct Wres { - char **path; - int npath; - int pathsz; +struct Idxed { + char** cache; + int n; + int max; }; -enum { - Rflg = 1 << 0, - Mflg = 1 << 1, - Aflg = 1 << 2, - Tflg = 1 << 3, -}; +Seen seentab[NCACHE]; +Idxed idxtab[NCACHE]; +char repopath[1024]; +char wdirpath[1024]; +char *rstr = "R "; +char *mstr = "M "; +char *astr = "A "; +char *ustr = "U "; +char *tstr = "T "; +char *bdir = ".git/fs/HEAD/tree"; +int useidx = 1; +int nrel; +int quiet; +int dirty; +int printflg; -Cache seencache[NCACHE]; -int quiet; -int printflg; -char *rstr = "R "; -char *tstr = "T "; -char *mstr = "M "; -char *astr = "A "; +Idxent *idx; +int idxsz; +int nidx; +int staleidx; +Idxent *wdir; +int wdirsz; +int nwdir; + +int loadwdir(char*); int seen(Dir *dir) { + Seen *c; Dir *dp; int i; - Cache *c; - c = &seencache[dir->qid.path&(NCACHE-1)]; + c = &seentab[dir->qid.path&(NCACHE-1)]; dp = c->cache; for(i=0; in; i++, dp++) - if(dir->qid.path == dp->qid.path && - dir->type == dp->type && - dir->dev == dp->dev) + if(dir->qid.path == dp->qid.path + && dir->qid.type == dp->qid.type + && dir->dev == dp->dev) return 1; if(c->n == c->max){ if (c->max == 0) @@ -62,129 +82,115 @@ seen(Dir *dir) return 0; } -void -grow(Wres *r) -{ - if(r->npath == r->pathsz){ - r->pathsz = 2*r->pathsz + 1; - r->path = erealloc(r->path, r->pathsz * sizeof(char*)); - } -} - int -readpaths(Wres *r, char *pfx, char *dir) +checkedin(Idxent *e, int change) { - char *f, *sub, *full, *sep; - Dir *d; - int fd, ret, i, n; + char *p; + int r; - d = nil; - ret = -1; - sep = ""; - if(dir[0] != 0) - sep = "/"; - if((full = smprint("%s/%s", pfx, dir)) == nil) - sysfatal("smprint: %r"); - if((fd = open(full, OREAD)) < 0) - goto error; - while((n = dirread(fd, &d)) > 0){ - for(i = 0; i < n; i++){ - if(seen(&d[i])) - continue; - if(d[i].qid.type & QTDIR){ - if((sub = smprint("%s%s%s", dir, sep, d[i].name)) == nil) - sysfatal("smprint: %r"); - if(readpaths(r, pfx, sub) == -1){ - free(sub); - goto error; - } - free(sub); - }else{ - grow(r); - if((f = smprint("%s%s%s", dir, sep, d[i].name)) == nil) - sysfatal("smprint: %r"); - r->path[r->npath++] = f; - } - } - free(d); + p = smprint("%s/%s", bdir, e->path); + r = access(p, AEXIST); + if(r == 0 && change){ + if(e->state != 'R') + e->state = 'T'; + staleidx = 1; } - ret = r->npath; -error: - close(fd); - free(full); - return ret; + free(p); + return r == 0; } int -cmp(void *pa, void *pb) +indexed(char *path, int isdir) { - return strcmp(*(char **)pa, *(char **)pb); -} + int lo, hi, mid, n, r; + char *s; -void -dedup(Wres *r) -{ - int i, o; - - if(r->npath <= 1) - return; - o = 0; - qsort(r->path, r->npath, sizeof(r->path[0]), cmp); - for(i = 1; i < r->npath; i++) - if(strcmp(r->path[o], r->path[i]) != 0) - r->path[++o] = r->path[i]; - r->npath = o + 1; + if(!useidx){ + s = smprint("%s/%s", bdir, path); + r = access(s, AEXIST); + free(s); + return r == 0; + } + s = path; + if(isdir) + s = smprint("%s/", path); + r = -1; + lo = 0; + hi = nidx-1; + n = strlen(s); + while(lo <= hi){ + mid = (hi + lo) / 2; + if(isdir) + r = strncmp(s, idx[mid].path, n); + else + r = strcmp(s, idx[mid].path); + if(r < 0) + hi = mid-1; + else if(r > 0) + lo = mid+1; + else + break; + } + if(isdir) + free(s); + return r == 0; } int -sameqid(Dir *d, char *qf) +idxcmp(void *pa, void *pb) { - char indexqid[64], fileqid[64], *p; - int fd, n; - - if(!d) - return 0; - if((fd = open(qf, OREAD)) == -1) - return 0; - if((n = readn(fd, indexqid, sizeof(indexqid) - 1)) == -1) - return 0; - indexqid[n] = 0; - close(fd); - if((p = strpbrk(indexqid, " \t\n\r")) != nil) - *p = 0; - - snprint(fileqid, sizeof(fileqid), "%ullx.%uld.%.2uhhx", - d->qid.path, d->qid.vers, d->qid.type); + Idxent *a, *b; + int c; - if(strcmp(indexqid, fileqid) == 0) - return 1; - return 0; -} - -void -writeqid(Dir *d, char *qf) -{ - int fd; - - if((fd = create(qf, OWRITE, 0666)) == -1) - return; - fprint(fd, "%ullx.%uld.%.2uhhx\n", - d->qid.path, d->qid.vers, d->qid.type); - close(fd); + a = (Idxent*)pa; + b = (Idxent*)pb; + if((c = strcmp(a->path, b->path)) != 0) + return c; + /* order is unique */ + return a-> order < b->order ? -1 : 1; } +/* + * compares whether the indexed entry 'a' + * has the same contents and mode as + * the entry on disk 'b'; if the indexed + * entry is nil, does a deep comparison + * of the checked out file and the file + * checked in. + */ int -samedata(char *pa, char *pb) +samedata(Idxent *a, Idxent *b) { - char ba[32*1024], bb[32*1024]; + char *gitpath, ba[IOUNIT], bb[IOUNIT]; int fa, fb, na, nb, same; + Dir *da, *db; + + if(a != nil){ + if(a->qid.path == b->qid.path + && a->qid.vers == b->qid.vers + && a->qid.type == b->qid.type + && a->mode == b->mode + && a->mode != 0) + return 1; + } same = 0; - fa = open(pa, OREAD); - fb = open(pb, OREAD); - if(fa == -1 || fb == -1){ + da = nil; + db = nil; + if((gitpath = smprint("%s/%s", bdir, b->path)) == nil) + sysfatal("smprint: %r"); + fa = open(gitpath, OREAD); + fb = open(b->path, OREAD); + if(fa == -1 || fb == -1) + goto mismatch; + da = dirfstat(fa); + db = dirfstat(fb); + if(da == nil || db == nil) + goto mismatch; + if((da->mode&0100) != (db->mode&0100)) + goto mismatch; + if(da->length != db->length) goto mismatch; - } while(1){ if((na = readn(fa, ba, sizeof(ba))) == -1) goto mismatch; @@ -197,8 +203,16 @@ samedata(char *pa, char *pb) if(memcmp(ba, bb, na) != 0) goto mismatch; } + if(a != nil){ + a->qid = db->qid; + a->mode = db->mode; + staleidx = 1; + } same = 1; + mismatch: + free(da); + free(db); if(fa != -1) close(fa); if(fb != -1) @@ -206,21 +220,160 @@ samedata(char *pa, char *pb) return same; } +int +loadent(char *dir, Dir *d, int fullpath) +{ + char *path; + int ret, isdir; + Idxent *e; + + if(fullpath) + path = strdup(dir); + else + path = smprint("%s/%s", dir, d->name); + if(path == nil) + sysfatal("smprint: %r"); + + cleanname(path); + if(strncmp(path, ".git/", 5) == 0){ + free(path); + return 0; + } + ret = 0; + isdir = d->qid.type & QTDIR; + if((printflg & Uflg) == 0 && !indexed(path, isdir)){ + free(path); + return 0; + } + if(isdir){ + ret = loadwdir(path); + free(path); + }else{ + if(nwdir == wdirsz){ + wdirsz += wdirsz/2; + wdir = erealloc(wdir, wdirsz*sizeof(Idxent)); + } + e = wdir + nwdir; + e->path = path; + e->qid = d->qid; + e->mode = d->mode; + e->order = nwdir; + e->state = 'T'; + nwdir++; + } + return ret; +} + +int +loadwdir(char *path) +{ + int fd, ret, i, n; + Dir *d, *e; + + d = nil; + e = nil; + ret = -1; + cleanname(path); + if(strncmp(path, ".git/", 5) == 0) + return 0; + if((fd = open(path, OREAD)) < 0) + goto error; + if((e = dirfstat(fd)) == nil) + sysfatal("fstat: %r"); + if(e->qid.type & QTDIR) + while((n = dirread(fd, &d)) > 0){ + for(i = 0; i < n; i++) + if(loadent(path, &d[i], 0) == -1) + goto error; + free(d); + } + else{ + if(loadent(path, e, 1) == -1) + goto error; + } + ret = 0; +error: + free(e); + if(fd != -1) + close(fd); + return ret; +} + +int +pfxmatch(char *p, char **pfx, int *pfxlen, int npfx) +{ + int i; + + if(p == nil) + return 0; + if(npfx == 0) + return 1; + for(i = 0; i < npfx; i++){ + if(strncmp(p, pfx[i], pfxlen[i]) != 0) + continue; + if(p[pfxlen[i]] == '/' || p[pfxlen[i]] == 0) + return 1; + if(strcmp(pfx[i], ".") == 0 || *pfx[i] == 0) + return 1; + } + return 0; +} + + +char* +reporel(char *s) +{ + char *p; + int n; + + if(*s == '/') + s = strdup(s); + else + s = smprint("%s/%s", wdirpath, s); + p = cleanname(s); + n = strlen(repopath); + if(strncmp(s, repopath, n) != 0) + sysfatal("path outside repo: %s", s); + p += n; + if(*p == '/') + p++; + memmove(s, p, strlen(p)+1); + return s; +} + +void +show(Biobuf *o, int flg, char *str, char *path) +{ + dirty |= flg; + if(!quiet && (printflg & flg)) + Bprint(o, "%s%s\n", str, path); +} + void usage(void) { - fprint(2, "usage: %s [-qbc] [-f filt] [paths...]\n", argv0); + fprint(2, "usage: %s [-qbc] [-f filt] [-b base] [paths...]\n", argv0); exits("usage"); } void main(int argc, char **argv) { - char *rpath, *tpath, *bpath, buf[8], repo[512]; - char *p, *e; - int i, dirty; - Wres r; - Dir *d; + char *p, *e, *ln, *base, **argrel, *parts[4], xbuf[8]; + int i, j, c, line, wfd, *argn; + Biobuf *f, *o, *w; + Hash h, hd; + Dir rn; + + gitinit(); + if(access(".git/fs/ctl", AEXIST) != 0) + sysfatal("no running git/fs"); + if(getwd(wdirpath, sizeof(wdirpath)) == nil) + sysfatal("getwd: %r"); + if(findrepo(repopath, sizeof(repopath), &nrel) == -1) + sysfatal("find root: %r"); + if(chdir(repopath) == -1) + sysfatal("chdir: %r"); ARGBEGIN{ case 'q': @@ -231,6 +384,7 @@ main(int argc, char **argv) tstr = ""; mstr = ""; astr = ""; + ustr = ""; break; case 'f': for(p = EARGF(usage()); *p; p++) @@ -239,95 +393,184 @@ main(int argc, char **argv) case 'A': printflg |= Aflg; break; case 'M': printflg |= Mflg; break; case 'R': printflg |= Rflg; break; + case 'U': printflg |= Uflg; break; default: usage(); break; } break; + case 'b': + useidx = 0; + base = EARGF(usage()); + if(resolveref(&h, base) == -1) + sysfatal("no such ref '%s'", base); + /* optimization: we're a lot faster when using the index */ + if(resolveref(&hd, "HEAD") == 0 && hasheq(&h, &hd)) + useidx = 1; + bdir = smprint(".git/fs/object/%H/tree", h); + break; default: usage(); - }ARGEND + }ARGEND; - if(findrepo(repo, sizeof(repo)) == -1) - sysfatal("find root: %r"); - if(chdir(repo) == -1) - sysfatal("chdir: %r"); - if(access(".git/fs/ctl", AEXIST) != 0) - sysfatal("no running git/fs"); - dirty = 0; - memset(&r, 0, sizeof(r)); if(printflg == 0) printflg = Tflg | Aflg | Mflg | Rflg; - if(argc == 0){ - if(access(TDIR, AEXIST) == 0 && readpaths(&r, TDIR, "") == -1) - sysfatal("read tracked: %r"); - if(access(RDIR, AEXIST) == 0 && readpaths(&r, RDIR, "") == -1) - sysfatal("read removed: %r"); - }else{ - for(i = 0; i < argc; i++){ - tpath = smprint(TDIR"/%s", argv[i]); - rpath = smprint(RDIR"/%s", argv[i]); - if((d = dirstat(tpath)) == nil && (d = dirstat(rpath)) == nil) - goto nextarg; - if(d->mode & DMDIR){ - readpaths(&r, TDIR, argv[i]); - readpaths(&r, RDIR, argv[i]); - }else{ - grow(&r); - r.path[r.npath++] = estrdup(argv[i]); + + nidx = 0; + idxsz = 32; + idx = emalloc(idxsz*sizeof(Idxent)); + nwdir = 0; + wdirsz = 32; + wdir = emalloc(wdirsz*sizeof(Idxent)); + argrel = emalloc(argc*sizeof(char*)); + argn = emalloc(argc*sizeof(int)); + for(i = 0; i < argc; i++){ + argrel[i] = reporel(argv[i]); + argn[i] = strlen(argrel[i]); + } + if((o = Bfdopen(1, OWRITE)) == nil) + sysfatal("open out: %r"); + if(useidx){ + if((f = Bopen(".git/INDEX9", OREAD)) == nil){ + fprint(2, "open index: %r\n"); + if(access(".git/index9", AEXIST) == 0){ + fprint(2, "index format conversion needed:\n"); + fprint(2, "\tcd %s && git/fs\n", repopath); + fprint(2, "\t@{cd .git/index9/removed >[2]/dev/null && walk -f | sed 's/^/R NOQID 0 /'} >> .git/INDEX9\n"); + fprint(2, "\t@{cd .git/fs/HEAD/tree && walk -f | sed 's/^/T NOQID 0 /'} >> .git/INDEX9\n"); } -nextarg: - free(tpath); - free(rpath); - free(d); + exits("noindex"); } + line = 0; + while((ln = Brdstr(f, '\n', 1)) != nil){ + line++; + /* allow blank lines */ + if(ln[0] == 0 || ln[0] == '\n') + continue; + if(getfields(ln, parts, nelem(parts), 0, " \t") != nelem(parts)) + sysfatal(".git/INDEX9:%d: corrupt index", line); + if(nidx == idxsz){ + idxsz += idxsz/2; + idx = realloc(idx, idxsz*sizeof(Idxent)); + } + cleanname(parts[3]); + if(strncmp(parts[3], ".git/", 5) == 0){ + staleidx = 1; + free(ln); + continue; + } + idx[nidx].state = *parts[0]; + idx[nidx].qid = parseqid(parts[1]); + idx[nidx].mode = strtol(parts[2], nil, 8); + idx[nidx].path = strdup(parts[3]); + idx[nidx].order = nidx; + nidx++; + free(ln); + } + qsort(idx, nidx, sizeof(Idxent), idxcmp); } - dedup(&r); - - for(i = 0; i < r.npath; i++){ - p = r.path[i]; - d = dirstat(p); - if(d && d->mode & DMDIR) - goto next; - rpath = smprint(RDIR"/%s", p); - tpath = smprint(TDIR"/%s", p); - bpath = smprint(HDIR"/%s", p); - /* Fast path: we don't want to force access to the rpath. */ - if(d && sameqid(d, tpath)) { - if(!quiet && (printflg & Tflg)) - print("%s%s\n", tstr, p); - }else{ - if(d == nil || access(rpath, AEXIST) == 0 ){ - if(access(bpath, AEXIST) == 0){ - dirty |= Rflg; - if(!quiet && (printflg & Rflg)) - print("%s%s\n", rstr, p); + + for(i = 0; i < argc; i++){ + argrel[i] = reporel(argv[i]); + argn[i] = strlen(argrel[i]); + } + if(argc == 0) + loadwdir("."); + else for(i = 0; i < argc; i++) + loadwdir(argrel[i]); + qsort(wdir, nwdir, sizeof(Idxent), idxcmp); + for(i = 0; i < argc; i++){ + argrel[i] = reporel(argv[i]); + argn[i] = strlen(argrel[i]); + } + i = 0; + j = 0; + while(i < nidx || j < nwdir){ + /* find the last entry we tracked for a path */ + while(i+1 < nidx && strcmp(idx[i].path, idx[i+1].path) == 0){ + staleidx = 1; + i++; + } + while(j+1 < nwdir && strcmp(wdir[j].path, wdir[j+1].path) == 0) + j++; + if(i < nidx && !pfxmatch(idx[i].path, argrel, argn, argc)){ + i++; + continue; + } + if(i >= nidx) + c = 1; + else if(j >= nwdir) + c = -1; + else + c = strcmp(idx[i].path, wdir[j].path); + /* exists in both index and on disk */ + if(c == 0){ + if(idx[i].state == 'R'){ + if(checkedin(&idx[i], 0)) + show(o, Rflg, rstr, idx[i].path); + else{ + idx[i].state = 'U'; + staleidx = 1; } - }else if(access(bpath, AEXIST) == -1) { - dirty |= Aflg; - if(!quiet && (printflg & Aflg)) - print("%s%s\n", astr, p); - }else if(samedata(p, bpath)){ - if(!quiet && (printflg & Tflg)) - print("%s%s\n", tstr, p); - writeqid(d, tpath); - }else{ - dirty |= Mflg; - if(!quiet && (printflg & Mflg)) - print("%s%s\n", mstr, p); - } + }else if(idx[i].state == 'A' && !checkedin(&idx[i], 1)) + show(o, Aflg, astr, idx[i].path); + else if(!samedata(&idx[i], &wdir[j])) + show(o, Mflg, mstr, idx[i].path); + else + show(o, Tflg, tstr, idx[i].path); + i++; + j++; + /* only exists in index */ + }else if(c < 0){ + if(checkedin(&idx[i], 0)) + show(o, Rflg, rstr, idx[i].path); + i++; + /* only exists on disk */ + }else{ + if(!useidx && checkedin(&wdir[j], 0)){ + if(samedata(nil, &wdir[j])) + show(o, Tflg, tstr, wdir[j].path); + else + show(o, Mflg, mstr, wdir[j].path); + }else if(printflg & Uflg && pfxmatch(idx[i].path, argrel, argn, argc)) + show(o, Uflg, ustr, wdir[j].path); + j++; } - free(rpath); - free(tpath); - free(bpath); -next: - free(d); } + Bterm(o); + + if(useidx && staleidx) + if((wfd = create(".git/INDEX9.new", OWRITE, 0644)) != -1){ + if((w = Bfdopen(wfd, OWRITE)) == nil){ + close(wfd); + goto Nope; + } + for(i = 0; i < nidx; i++){ + while(i+1 < nidx && strcmp(idx[i].path, idx[i+1].path) == 0) + i++; + if(idx[i].state == 'U') + continue; + Bprint(w, "%c %Q %o %s\n", + idx[i].state, + idx[i].qid, + idx[i].mode, + idx[i].path); + } + Bterm(w); + nulldir(&rn); + rn.name = "INDEX9"; + if(remove(".git/INDEX9") == -1) + goto Nope; + if(dirwstat(".git/INDEX9.new", &rn) == -1) + sysfatal("rename: %r"); + } + +Nope: if(!dirty) exits(nil); - p = buf; - e = buf + sizeof(buf); + p = xbuf; + e = p + sizeof(xbuf); for(i = 0; (1 << i) != Tflg; i++) if(dirty & (1 << i)) - p = seprint(p, e, "%c", "DMAT"[i]); - exits(buf); + p = seprint(p, e, "%c", "RMAUT"[i]); + exits(xbuf); }