26 static char *filelist[] = {
39 static int needhist[Nfile] = {
46 * The qids are <8-bit type><16-bit page number><16-bit page version><8-bit file index>.
48 enum { /* <8-bit type> */
59 mkqid(int type, int num, int vers, int file)
61 return ((uvlong)type<<40) | ((uvlong)num<<24) | (vers<<8) | file;
67 return (path>>40)&0xFF;
73 return (path>>24)&0xFFFF;
79 return (path>>8)&0xFFFF;
88 typedef struct Aux Aux;
103 if(r->ifcall.aname && r->ifcall.aname[0]){
104 respond(r, "invalid attach specifier");
108 a = emalloc(sizeof(Aux));
110 a->name = s_copy(r->ifcall.uname);
112 r->ofcall.qid = (Qid){mkqid(Droot, 0, 0, 0), 0, QTDIR};
113 r->fid->qid = r->ofcall.qid;
123 if((b = wBopen(".httplogin", OREAD)) == nil)
126 while(s_read(b, s, Bsize) > 0)
135 fswalk1(Fid *fid, char *name, Qid *qid)
138 int i, isdotdot, n, t;
144 isdotdot = strcmp(name, "..")==0;
145 n = strtoul(name, &q, 10);
146 path = fid->qid.path;
149 switch(qidtype(path)){
151 return "wikifs: bad path in server (bug)";
158 if(strcmp(name, "new")==0){
159 *qid = (Qid){mkqid(Fnew, 0, 0, 0), 0, 0};
162 if(strcmp(name, "map")==0){
163 *qid = (Qid){mkqid(Fmap, 0, 0, 0), 0, 0};
166 if((*q!='\0' || (wh=getcurrent(n))==nil)
167 && (wh=getcurrentbyname(name))==nil)
168 return "file does not exist";
169 *qid = (Qid){mkqid(D1st, wh->n, 0, 0), wh->doc->time, QTDIR};
175 *qid = (Qid){mkqid(Droot, 0, 0, 0), 0, QTDIR};
179 /* handle history directories */
181 if((wh = gethistory(qidnum(path))) == nil)
182 return "file does not exist";
183 for(i=0; i<wh->ndoc; i++)
184 if(wh->doc[i].time == n)
188 return "file does not exist";
193 *qid = (Qid){mkqid(D2nd, qidnum(path), i, 0), wh->doc[i].time, QTDIR};
197 /* handle files other than index */
198 for(i=0; i<nelem(filelist); i++){
199 if(strcmp(name, filelist[i])==0){
201 if((wh = gethistory(qidnum(path))) == nil)
202 return "file does not exist";
206 *qid = (Qid){mkqid(F1st, qidnum(path), 0, i), a->w->doc->time, 0};
210 return "file does not exist";
215 * Can't use a->w[a->ndoc-1] because that
216 * might be a failed write rather than the real one.
218 *qid = (Qid){mkqid(D1st, qidnum(path), 0, 0), 0, QTDIR};
219 if((wh = getcurrent(qidnum(path))) == nil)
220 return "file does not exist";
226 for(i=0; i<=Qraw; i++){
227 if(strcmp(name, filelist[i])==0){
228 *qid = (Qid){mkqid(F2nd, qidnum(path), qidvers(path), i), a->w->doc->time, 0};
232 return "file does not exist";
235 return "bad programming";
240 t = qidtype(qid->path);
241 switch(qidfile(qid->path)){
243 s = tohtml(a->w, a->w->doc+a->n,
244 t==F1st? Tpage : Toldpage);
247 s = totext(a->w, a->w->doc+a->n,
248 t==F1st? Tpage : Toldpage);
251 s = s_copy(a->w->title);
252 s = s_append(s, "\n");
253 s = doctext(s, &a->w->doc[a->n]);
256 s = tohtml(a->w, a->w->doc+a->n, Thistory);
259 s = totext(a->w, a->w->doc+a->n, Thistory);
262 s = tohtml(a->w, a->w->doc+a->n, Tdiff);
265 s = tohtml(a->w, a->w->doc+a->n, Tedit);
268 s = tohtml(a->w, a->w->doc+a->n, Twerror);
271 s = totext(a->w, a->w->doc+a->n, Twerror);
277 return "internal error";
293 path = fid->qid.path;
294 t = qidtype(fid->qid.path);
295 if((r->ifcall.mode != OREAD && t != Fnew && t != Fmap)
296 || (r->ifcall.mode&ORCLOSE)){
297 respond(r, "permission denied");
313 if((wh = gethistory(qidnum(path))) == nil){
314 respond(r, "file does not exist");
320 r->ofcall.qid.vers = wh->doc[a->n].time;
321 r->fid->qid = r->ofcall.qid;
341 respond(r, "programmer error");
347 fsclone(Fid *old, Fid *new)
351 a = emalloc(sizeof(*a));
352 *a = *(Aux*)old->aux;
368 fsdestroyfid(Fid *fid)
389 fillstat(Dir *d, uvlong path, ulong tm, ulong length)
394 memset(d, 0, sizeof(Dir));
395 d->uid = estrdup9p("wiki");
396 d->gid = estrdup9p("wiki");
398 switch(qidtype(path)){
408 d->qid = (Qid){path, tm, type};
410 d->atime = d->mtime = tm;
412 if(qidfile(path) == Qedithtml)
413 d->atime = d->mtime = time(0);
415 switch(qidtype(path)){
417 d->name = estrdup("/");
418 d->mode = DMDIR|0555;
422 d->name = numtoname(qidnum(path));
424 d->name = estrdup("<dead>");
425 for(p=d->name; *p; p++)
428 d->mode = DMDIR|0555;
432 snprint(tmp, sizeof tmp, "%lud", tm);
433 d->name = estrdup(tmp);
434 d->mode = DMDIR|0555;
438 d->name = estrdup("map");
443 d->name = estrdup("new");
448 d->name = estrdup(filelist[qidfile(path)]);
453 d->name = estrdup(filelist[qidfile(path)]);
458 print("bad qid path 0x%.8llux\n", path);
472 if((a = fid->aux) && a->w)
473 t = a->w->doc[a->n].time;
475 fillstat(&r->d, fid->qid.path, t, a->s ? s_len(a->s) : 0);
479 typedef struct Bogus Bogus;
486 rootgen(int i, Dir *d, void *aux)
495 fillstat(d, mkqid(Fnew, 0, 0, 0), a->map->t, 0);
498 fillstat(d, mkqid(Fmap, 0, 0, 0), a->map->t, 0);
500 default: /* first-level directory */
504 fillstat(d, mkqid(D1st, a->map->el[i].n, 0, 0), a->map->t, 0);
510 firstgen(int i, Dir *d, void *aux)
518 num = qidnum(b->path);
520 t = a->w->doc[a->n].time;
522 if(i < Nfile){ /* file in first-level directory */
523 fillstat(d, mkqid(F1st, num, 0, i), t, 0);
528 if(i < a->w->ndoc){ /* second-level (history) directory */
529 fillstat(d, mkqid(D2nd, num, i, 0), a->w->doc[i].time, 0);
538 secondgen(int i, Dir *d, void *aux)
548 if(i <= Qraw){ /* index.html, index.txt, raw */
549 fillstat(d, mkqid(F2nd, qidnum(path), qidvers(path), i), a->w->doc[a->n].time, 0);
566 path = r->fid->qid.path;
569 switch(qidtype(path)){
571 respond(r, "cannot happen (bad qid)");
575 if(a == nil || a->map == nil){
576 respond(r, "cannot happen (no map)");
579 dirread9p(r, rootgen, &b);
584 if(a == nil || a->w == nil){
585 respond(r, "cannot happen (no wh)");
588 dirread9p(r, firstgen, &b);
593 dirread9p(r, secondgen, &b);
599 respond(r, "protocol botch");
606 respond(r, "unknown name");
619 if(a == nil || a->s == nil){
620 respond(r, "cannot happen (no s)");
623 readbuf(r, s_to_c(a->s), s_len(a->s));
629 typedef struct Sread Sread;
635 Srdline(void *v, int c)
643 else if(p = strchr(s->rp, c)){
655 responderrstr(Req *r)
659 rerrstr(buf, sizeof buf);
661 strcpy(buf, "unknown error");
668 char *author, *comment, *net, *err, *p, *title, tmp[40];
679 switch(qidtype(fid->qid.path)){
681 stmp = s_nappend(s_reset(nil), r->ifcall.data, r->ifcall.count);
682 a->n = nametonum(s_to_c(stmp));
685 respond(r, "name not found");
692 respond(r, "cannot happen");
697 respond(r, "protocol botch");
700 if(r->ifcall.count==0){ /* do final processing */
704 if((title = Srdline(&s, '\n')) == nil){
714 w = emalloc(sizeof(*w));
716 w->title = estrdup(title);
719 author = estrdup(s_to_c(a->name));
722 while(s.rp && *s.rp && *s.rp != '\n'){
723 p = Srdline(&s, '\n');
728 author = estrdup(p+1);
731 t = strtoul(p+1, &p, 10);
737 comment = estrdup(p+1);
742 w->doc = emalloc(sizeof(w->doc[0]));
743 w->doc->time = time(0);
744 w->doc->comment = comment;
746 if(net = r->pool->srv->aux){
747 p = emalloc(strlen(author)+10+strlen(net));
755 w->doc->author = author;
757 if((w->doc->wtxt = Brdpage(Srdline, &s)) == nil){
758 err = "empty document";
763 if((n = allocnum(w->title, 0)) < 0)
765 sprint(tmp, "D%lud\n", w->doc->time);
766 a->s = s_reset(a->s);
767 a->s = doctext(a->s, w->doc);
768 rv = writepage(n, t, a->s, w->title);
780 if(s_len(a->s)+r->ifcall.count > Maxfile){
781 respond(r, "file too large");
786 a->s = s_nappend(a->s, r->ifcall.data, r->ifcall.count);
787 r->ofcall.count = r->ifcall.count;
793 .destroyfid= fsdestroyfid,
805 fprint(2, "usage: wikifs [-D] [-a addr]... [-m mtpt] [-p perm] [-s service] dir\n");
810 main(int argc, char **argv)
815 char *service, *mtpt;
831 addr = erealloc(addr, (naddr+8)*sizeof(addr[0]));
832 addr[naddr++] = EARGF(usage());
835 mtpt = EARGF(usage());
841 perm = strtoul(EARGF(usage()), nil, 8);
844 service = EARGF(usage());
854 if((dp = dirstat(argv[0])) == nil)
855 sysfatal("dirstat %s: %r", argv[0]);
856 if((dp->mode&DMDIR) == 0)
857 sysfatal("%s: not a directory", argv[0]);
863 for(i=0; i<naddr; i++)
864 listensrv(&wikisrv, addr[i]);
866 s = emalloc(sizeof *s);
868 postmountsrv(s, service, mtpt, MREPL|MCREATE);
870 buf = emalloc9p(5+strlen(service)+1);
871 strcpy(buf, "/srv/");
872 strcat(buf, service);
875 if(dirwstat(buf, &d) < 0)
876 fprint(2, "wstat: %r\n");