]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/hgfs/hgdb.c
cc: use 7 octal digits for 21 bit runes
[plan9front.git] / sys / src / cmd / hgfs / hgdb.c
1 /* hg debug stuff, will become update/merge program */
2
3 #include <u.h>
4 #include <libc.h>
5 #include <thread.h>
6 #include <bio.h>
7 #include "dat.h"
8 #include "fns.h"
9
10 typedef struct Workdir Workdir;
11 typedef struct Dstate Dstate;
12
13 struct Dstate
14 {
15         Dstate  *next;
16         int     mode;
17         ulong   size;
18         long    mtime;
19         char    status;
20         char    path[];
21 };
22
23 struct Workdir
24 {
25         char    path[MAXPATH];
26         uchar   p1hash[HASHSZ];
27         uchar   p2hash[HASHSZ];
28         Dstate  *ht[256];
29 };
30
31 int clean = 0;
32
33 static Dstate**
34 dslookup(Workdir *wd, char *path)
35 {
36         Dstate **hp, *h;
37
38         hp = &wd->ht[hashstr(path) % nelem(wd->ht)];
39         for(h = *hp; h != nil; h = *hp){
40                 if(strcmp(path, h->path) == 0)
41                         break;
42                 hp = &h->next;
43         }
44         return hp;
45 }
46
47 static void
48 clearworkdir(Workdir *wd)
49 {
50         Dstate *h;
51         int i;
52
53         for(i=0; i<nelem(wd->ht); i++)
54                 while(h = wd->ht[i]){
55                         wd->ht[i] = h->next;
56                         free(h);
57                 }
58         memset(wd, 0, sizeof(*wd));
59 }
60
61 static int
62 loadworkdir(Workdir *wd, char *path)
63 {
64         uchar hdr[1+4+4+4+4];
65         char buf[MAXPATH], *err;
66         Dstate **hp, *h;
67         int fd, n;
68
69         memset(wd, 0, sizeof(*wd));
70         if(getworkdir(wd->path, path) < 0)
71                 return -1;
72         snprint(buf, sizeof(buf), "%s/.hg/dirstate", wd->path);
73         if((fd = open(buf, OREAD)) < 0)
74                 return -1;
75         err = "dirstate truncated";
76         if(read(fd, wd->p1hash, HASHSZ) != HASHSZ)
77                 goto Error;
78         if(read(fd, wd->p2hash, HASHSZ) != HASHSZ)
79                 goto Error;
80         for(;;){
81                 if((n = read(fd, hdr, sizeof(hdr))) == 0)
82                         break;
83                 if(n < 0){
84                         err = "reading dirstate: %r";
85                         goto Error;
86                 }
87                 if(n != sizeof(hdr))
88                         goto Error;
89                 n = hdr[16] | hdr[15]<<8 | hdr[14]<<16 | hdr[13]<<24;
90                 if(n < 0 || n >= sizeof(buf)){
91                         err = "bad path length in dirstate";
92                         goto Error;
93                 }
94                 if(read(fd, buf, n) != n)
95                         goto Error;
96                 buf[n++] = 0;
97                 hp = dslookup(wd, buf);
98                 if(*hp != nil){
99                         err = "duplicate entry in dirstate";
100                         goto Error;
101                 }
102                 h = malloc(sizeof(*h) + n);
103                 if(h == nil){
104                         err = "out of memory";
105                         goto Error;
106                 }
107                 memmove(h->path, buf, n);
108                 h->status = hdr[0];
109                 h->mode = hdr[4] | hdr[3]<<8 | hdr[2]<<16 | hdr[1]<<24;
110                 h->size = hdr[8] | hdr[7]<<8 | hdr[6]<<16 | hdr[5]<<24;
111                 h->mtime = hdr[12] | hdr[11]<<8 | hdr[10]<<16 | hdr[9]<<24;
112                 h->next = *hp;
113                 *hp = h;
114         }
115         close(fd);
116         return 0;
117 Error:
118         clearworkdir(wd);
119         close(fd);
120         werrstr(err);
121         return -1;
122 }
123
124 char*
125 pjoin(char *path, char *name)
126 {
127         if(path[0] == '\0')
128                 path = "/";
129         if(name[0] == '\0')
130                 return strdup(path);
131         if(path[strlen(path)-1] == '/' || name[0] == '/')
132                 return smprint("%s%s", path, name);
133         return smprint("%s/%s", path, name);
134 }
135
136 void
137 changes1(int fd, char *lpath, char *rpath, char *apath,
138         void (*apply)(char *, char *, char *, char *, char *, void *), void *aux)
139 {
140         char *state, *name;
141         Biobuf bin;
142
143         Binit(&bin, fd, OREAD);
144         while((state = Brdstr(&bin, '\n', 1)) != nil){
145                 if((name = strchr(state, '\t')) == nil)
146                         continue;
147                 while(*name == '\t' || *name == ' ')
148                         *name++ = '\0';
149                 if(name[0] == '.' && name[1] == '/')
150                         name += 2;
151                 apply(state, name, lpath, rpath, apath, aux);
152         }
153         Bterm(&bin);
154 }
155
156 int
157 changes(char *opt, char *lp, char *rp, char *ap,
158         void (*apply)(char *, char *, char *, char *, char *, void *), void *aux)
159 {
160         int pfd[2], pid;
161         Waitmsg *w;
162
163         if(pipe(pfd) < 0)
164                 return -1;
165         pid = rfork(RFPROC|RFMEM|RFFDG);
166         switch(pid){
167         case -1:
168                 close(pfd[0]);
169                 close(pfd[1]);
170                 return -1;
171         case 0:
172                 close(pfd[0]);
173                 dup(pfd[1], 1);
174                 close(pfd[1]);
175                 execl("/bin/derp", "derp", opt, "-p", "0111", lp, ap, rp, nil);
176                 exits("exec");
177         }
178         close(pfd[1]);
179         changes1(pfd[0], lp, rp, ap, apply, aux);
180         close(pfd[0]);
181         while(w = wait()){
182                 if(w->pid == pid){
183                         if(w->msg[0] != '\0'){
184                                 werrstr("%s", w->msg);
185                                 free(w);
186                                 return 1;
187                         }
188                         free(w);
189                         return 0;
190                 }
191                 free(w);
192         }
193         return -1;
194 }
195
196 void
197 apply(char *state, char *name, char *lp, char *rp, char *ap, Workdir *)
198 {
199         Dir *rd, *ld;
200
201         ld = rd = nil;
202         // fprint(2, "### %s %s ->\t", state, name);
203         if(strcmp(state, "na") == 0){
204                 rd = dirstat(rp);
205                 if(rd != nil){
206                         if(rd->qid.type & QTDIR)
207                                 print("mkdir %s\n", lp);
208                         else
209                                 print("cp %s %s\n", rp, lp);
210                 }
211         }
212         else if(strcmp(state, "an") == 0){
213         }
214         else if(strcmp(state, "nm") == 0){
215                 print("cp %s %s\n", rp, lp);
216         }
217         else if(strcmp(state, "mn") == 0){
218         }
219         else if(strcmp(state, "nd") == 0){
220                 print("rm %s\n", lp);
221         }
222         else if(strcmp(state, "dn") == 0){
223         }
224         else if(strcmp(state, "aa!") == 0 || strcmp(state, "mm!") == 0){
225                 ld = dirstat(lp);
226                 rd = dirstat(rp);
227                 if(ld != nil && rd != nil){
228                         if(rd->qid.type & QTDIR)
229                                 print("# conflict # mkdir %s\n", lp);
230                         else if(ld->qid.type & QTDIR)
231                                 print("# conflict # rm -r %s\n", lp);
232                         else
233                                 print("# conflict # ape/diff3 %s %s %s >%s\n", lp, ap, rp, lp);
234                 }
235         }
236         else if(strcmp(state, "md!") == 0){
237                 print("# delete conflict # rm %s\n", lp);
238         }
239         else if(strcmp(state, "dm!") == 0){
240                 print("# delete conflict # cp %s %s\n", rp, lp);
241         }
242         else {
243                 print("# unknown status %s %s\n", state, name);
244         }
245         free(rd);
246         free(ld);
247 }
248
249 void
250 apply1(char *state, char *name, char *lp, char *rp, char *ap, void *aux)
251 {
252         Workdir *wd = aux;
253
254         lp = pjoin(lp, name);
255         rp = pjoin(rp, name);
256         ap = pjoin(ap, name);
257         apply(state, lp + strlen(wd->path)+1, lp, rp, ap, wd);
258         free(ap);
259         free(rp);
260         free(lp);
261 }
262
263 void
264 apply0(char *state, char *name, char *lp, char *rp, char *ap, void *aux)
265 {
266         Workdir *wd = aux;
267         Dir *ld;
268
269         if(clean){
270                 /* working dir clean */
271                 apply1(state, name, wd->path, rp, ap, wd);
272                 return;
273         }
274         lp = pjoin(wd->path, name);
275         ld = dirstat(lp);
276         if(clean == 0 && ld != nil && (ld->qid.type & QTDIR) == 0){
277                 /* check for changes in working directory */
278                 rp = pjoin(rp, name);
279                 ap = pjoin(ap, name);
280                 changes("-Lcq", lp, rp, ap, apply1, wd);
281                 free(ap);
282                 free(rp);
283         } else {
284                 /* working dir clean */
285                 apply1(state, name, wd->path, rp, ap, wd);
286         }
287         free(lp);
288         free(ld);
289 }
290
291 void
292 usage(void)
293 {
294         fprint(2, "usage: %s [-m mtpt] [-r rev] [root]\n", argv0);
295         exits("usage");
296 }
297
298 void
299 main(int argc, char *argv[])
300 {
301         char lp[MAXPATH], rp[MAXPATH], ap[MAXPATH];
302         uchar rh[HASHSZ], ah[HASHSZ];
303         char *mtpt, *rev;
304         Workdir wd;
305
306         fmtinstall('H', Hfmt);
307
308         rev = "tip";
309         mtpt = "/mnt/hg";
310
311         ARGBEGIN {
312         case 'm':
313                 mtpt = EARGF(usage());
314                 break;
315         case 'r':
316                 rev = EARGF(usage());
317                 break;
318         case 'c':
319                 clean = 1;
320                 break;
321         } ARGEND;
322
323         memset(&wd, 0, sizeof(wd));
324         if(loadworkdir(&wd, *argv) < 0)
325                 sysfatal("loadworkdir: %r");
326
327         if(memcmp(wd.p2hash, nullid, HASHSZ))
328                 sysfatal("outstanding merge");
329
330         snprint(rp, sizeof(rp), "%s/%s", mtpt, rev);
331         if(readhash(rp, "rev", rh) != 0)
332                 sysfatal("unable to get hash for %s", rev);
333
334         if(memcmp(rh, wd.p1hash, HASHSZ) == 0){
335                 fprint(2, "up to date\n");
336                 exits(0);
337         }
338
339         ancestor(mtpt, wd.p1hash, rh, ah);
340         if(memcmp(ah, nullid, HASHSZ) == 0)
341                 sysfatal("no common ancestor between %H and %H", wd.p1hash, rh);
342
343         if(memcmp(ah, rh, HASHSZ) == 0)
344                 memmove(ah, wd.p1hash, HASHSZ);
345
346         snprint(lp, sizeof(lp), "%s/%H/files", mtpt, wd.p1hash);
347         snprint(rp, sizeof(rp), "%s/%H/files", mtpt, rh);
348         snprint(ap, sizeof(ap), "%s/%H/files", mtpt, ah);
349         
350         changes("-L", lp, rp, ap, apply0, &wd);
351
352         exits(0);
353 }