--- /acme/mail/src/mesg.c Tue Sep 17 23:37:33 2013 +++ /acme/mail/src/mesg.c Mon Dec 2 23:26:07 2013 @@ -43,6 +43,7 @@ "message/rfc822", "text/richtext", "text/tab-separated-values", + "text/calendar", "application/octet-stream", nil, }; @@ -989,6 +990,48 @@ free(s); } +/* + * find the best alternative part. + * + * turkeys have started emitting empty text/plain parts, + * with the actual content in a text/html part, which complicates the choice. + * + * bigger turkeys emit a tiny base64-encoded text/plain part, + * a small base64-encoded text/html part, and the real content is in + * a text/calendar part. + * + * the magic lengths are empirically derived. + * as turkeys devolve and mutate, this will only get worse. + */ +static Message* +bestalt(Message *m, char *dir) +{ + int len; + char *subdir; + Message *nm; + Message *realplain, *realhtml, *realcal; + + realplain = realhtml = realcal = nil; + for(nm = m->head; nm != nil; nm = nm->next){ + subdir = estrstrdup(dir, nm->name); + len = 0; + free(readbody(nm->type, subdir, &len)); + free(subdir); + if(strcmp(nm->type, "text/plain") == 0 && len >= 8) + realplain = nm; + else if(strcmp(nm->type, "text/html") == 0 && len >= 600) + realhtml = nm; + else if(strcmp(nm->type, "text/calendar") == 0) + realcal = nm; + } + if(realplain == nil && realhtml == nil && realcal) + return realcal; /* super-turkey */ + else if(realplain == nil && realhtml) + return realhtml; /* regular turkey */ + else + return realplain; +} + void mesgload(Message *m, char *rootdir, char *file, Window *w) { @@ -1029,12 +1072,15 @@ /* multi-part message, either multipart/* or message/rfc822 */ thisone = nil; if(strcmp(m->type, "multipart/alternative") == 0){ - thisone = m->head; /* in case we can't find a good one */ - for(mp=m->head; mp!=nil; mp=mp->next) - if(isprintable(mp->type)){ - thisone = mp; - break; - } + thisone = bestalt(m, dir); + if(thisone == nil){ + thisone = m->head; /* in case we can't find a good one */ + for(mp=m->head; mp!=nil; mp=mp->next) + if(isprintable(mp->type)){ + thisone = mp; + break; + } + } } for(mp=m->head; mp!=nil; mp=mp->next){ if(thisone!=nil && mp!=thisone) @@ -1043,8 +1089,11 @@ name = estrstrdup(file, mp->name); /* skip first element in name because it's already in window name */ if(mp != m->head) - Bprint(w->body, "\n===> %s (%s) [%s]\n", strchr(name, '/')+1, mp->type, mp->disposition); - if(strcmp(mp->type, "text")==0 || strncmp(mp->type, "text/", 5)==0){ + Bprint(w->body, "\n===> %s (%s) [%s]\n", + strchr(name, '/')+1, mp->type, + mp->disposition); + if(strcmp(mp->type, "text")==0 || + strncmp(mp->type, "text/", 5)==0){ mimedisplay(mp, name, rootdir, w, 1); printheader(subdir, w->body, okheaders); printheader(subdir, w->body, extraheaders); @@ -1053,7 +1102,8 @@ winwritebody(w, s, n); free(s); }else{ - if(strncmp(mp->type, "multipart/", 10)==0 || strcmp(mp->type, "message/rfc822")==0){ + if(strncmp(mp->type, "multipart/", 10)==0 || + strcmp(mp->type, "message/rfc822")==0){ mp->w = w; mesgload(mp, rootdir, name, w); mp->w = nil; --- /sys/lib/backup/backup Tue Nov 12 02:49:57 2013 +++ /sys/lib/backup/backup Mon Dec 2 23:24:21 2013 @@ -89,8 +89,7 @@ if (! ~ $debug yes && ~ $set set1 && ~ $print yes) { 9fs log # don't hang in lp - tail -50 /n/$fs/sys/log/fs.archive | - pr -h 'recent fossil dump scores' | lp & + tail -50 /sys/log/fs.archive | pr -h 'recent fossil dump scores' | lp & echo fossil dump scores just printed. } --- /sys/lib/dist/mkfile Wed Nov 13 22:19:23 2013 +++ /sys/lib/dist/mkfile Mon Dec 2 23:01:26 2013 @@ -13,7 +13,8 @@ $scr/usbdisk.bz2:D: $scr/plan9.iso.bz2 cd pc; mk $target -usb-dist:V: $dist/web.protect/usbdisk.bz2 +usb-dist:V: $scr/usbdisk.bz2 + mk $dist/web.protect/usbdisk.bz2 #contrib-cd:V: $scr/contrib.iso.bz2 # mk $dist/web.protect/contrib.iso.bz2 --- /sys/lib/dist/pc/inst/xxx Wed Apr 10 22:15:19 2002 +++ /sys/lib/dist/pc/inst/xxx Mon Dec 2 23:03:07 2013 @@ -1,9 +1,9 @@ #!/bin/rc ip/ipconfig -echo ' auth=204.178.31.3 +echo ' auth=p9auth.cs.bell-labs.com authdom=cs.bell-labs.com' >>/net/ndb ndb/cs auth/factotum bind -a /bin/auth / -cpu -e clear -h tcp!204.178.31.2 +cpu -e clear -h tcp!plan9.bell-labs.com --- /sys/lib/dist/pc/mkfile Wed Nov 13 22:20:25 2013 +++ /sys/lib/dist/pc/mkfile Mon Dec 2 23:01:40 2013 @@ -1,4 +1,6 @@ # /sys/lib/dist/pc/mkfile +<../defs + out=outside # outside web server s=/sys/lib/dist/pc x=`{bind -b /sys/lib/dist/bin/$cputype /bin} @@ -80,7 +82,7 @@ /sys/src/9/pc/^(9pcflop 9pccd)^.gz subst/plan9.ini /lib/vgadb ls -l $target -$scr/usbdisk:D: /n/sources/plan9 +$scr/usbdisk:D: $scr/plan9.iso.bz2 /n/sources/plan9 @ { rfork n if (~ $#scr 0 || ~ $scr '') { @@ -96,8 +98,7 @@ } $scr/usbdisk.bz2:D: $scr/usbdisk @ { - rm -f $target - bzip2 $prereq && rm -f $prereq + bzip2 <$prereq >$target && rm -f $prereq ls -l $target targsz = `{{ls -s $target; echo 0} | awk '{print $1; exit}'} if (test $targsz -lt 80000) { --- /sys/src/9/boot/boot.c Fri Jul 22 05:38:40 2011 +++ /sys/src/9/boot/boot.c Mon Dec 2 23:38:51 2013 @@ -27,25 +27,13 @@ static Method *rootserver(char*); static void kbmap(void); -void -boot(int argc, char *argv[]) +/* + * we should inherit the standard fds all referring to /dev/cons, + * but we're being paranoid. + */ +static void +opencons(void) { - int fd, afd; - Method *mp; - char *cmd, cmdbuf[64], *iargv[16]; - char rootbuf[64]; - int islocal, ishybrid; - char *rp, *rsp, *rdparts; - int iargc, n; - char buf[32]; - AuthInfo *ai; - - fmtinstall('r', errfmt); - - /* - * we should inherit the standard fds all referring to /dev/cons, - * but we're being paranoid. - */ close(0); close(1); close(2); @@ -53,13 +41,25 @@ open("/dev/cons", OREAD); open("/dev/cons", OWRITE); open("/dev/cons", OWRITE); - /* - * init will reinitialize its namespace. - * #ec gets us plan9.ini settings (*var variables). - */ +} + +/* + * init will reinitialize its namespace. + * #ec gets us plan9.ini settings (*var variables). + */ +static void +bindenvsrv(void) +{ bind("#ec", "/env", MREPL); bind("#e", "/env", MBEFORE|MCREATE); bind("#s", "/srv/", MREPL|MCREATE); +} + +static void +debuginit(int argc, char **argv) +{ + int fd; + if(getenv("debugboot")) debugboot = 1; #ifdef DEBUG @@ -68,77 +68,62 @@ print("%#p %s ", argv[fd], argv[fd]); print("\n"); #endif /* DEBUG */ + SET(fd, argc, argv); USED(fd, argc, argv); +} - ARGBEGIN{ - case 'k': - kflag = 1; - break; - case 'm': - mflag = 1; - break; - case 'f': - fflag = 1; - break; - }ARGEND +/* + * read disk partition tables here so that readnvram via factotum + * can see them. ideally we would have this information in + * environment variables before attaching #S, which would then + * parse them and create partitions. + */ +static void +partinit(void) +{ + char *rdparts; - readfile("#e/cputype", cputype, sizeof(cputype)); + rdparts = getenv("readparts"); + if(rdparts) + readparts(); + free(rdparts); +} - /* - * set up usb keyboard & mouse, if any. - * starts usbd, which mounts itself on /dev. - * starts partfs on first disk, if any, to permit nvram on usb. - */ - usbinit(Dontpost); +/* + * pick a method and initialize it + */ +static Method * +pickmethod(int argc, char **argv) +{ + Method *mp; - /* - * pick a method and initialize it - */ if(method[0].name == nil) fatal("no boot methods"); mp = rootserver(argc ? *argv : 0); (*mp->config)(mp); - islocal = strcmp(mp->name, "local") == 0; - ishybrid = strcmp(mp->name, "hybrid") == 0; + return mp; +} - /* - * load keymap if it's there. - */ - kbmap(); - - /* don't trigger aoe until the network has been configured */ - bind("#æ", "/dev", MAFTER); /* nvram could be here */ - bind("#S", "/dev", MAFTER); /* nvram could be here */ - - /* - * read disk partition tables here so that readnvram via factotum - * can see them. ideally we would have this information in - * environment variables before attaching #S, which would then - * parse them and create partitions. - */ - rdparts = getenv("readparts"); - if(rdparts) - readparts(); - free(rdparts); - - /* - * authentication agent - * sets hostowner, creating an auth discontinuity - */ +/* + * authentication agent + * sets hostowner, creating an auth discontinuity + */ +static void +doauth(int cpuflag) +{ if(debugboot) fprint(2, "auth..."); authentication(cpuflag); +} - /* leave existing subprocesses in their own namespace */ - rfork(RFNAMEG); - - /* - * restart partfs under the new hostowner id - */ - usbinit(Post); +/* + * connect to the root file system + */ +static int +connectroot(Method *mp, int islocal, int ishybrid) +{ + int fd, n; + char buf[32]; - /* - * connect to the root file system - */ fd = (*mp->connect)(); if(fd < 0) fatal("can't connect to file server"); @@ -154,10 +139,20 @@ if(n < 0) fatal("can't init 9P"); srvcreate("boot", fd); + return fd; +} + +/* + * create the name space, mount the root fs + */ +static int +nsinit(int fd, char **rspp) +{ + int afd; + char *rp, *rsp; + AuthInfo *ai; + static char rootbuf[64]; - /* - * create the name space, mount the root fs - */ if(bind("/", "/", MREPL) < 0) fatal("bind /"); rp = getenv("rootspec"); @@ -185,23 +180,28 @@ rp = rootbuf; if(bind(rp, "/", MAFTER|MCREATE) < 0){ fprint(2, "boot: couldn't bind $rootdir=%s to root: %r\n", rp); - if(strcmp(rootbuf, "/root//plan9") == 0){ - fprint(2, "**** warning: remove rootdir=/plan9 entry from plan9.ini\n"); - rp = "/root"; - if(bind(rp, "/", MAFTER|MCREATE) < 0) - fatal("second bind /"); - }else + if(strcmp(rootbuf, "/root//plan9") != 0) + fatal("second bind /"); + /* undo installer's work */ + fprint(2, "**** warning: remove rootdir=/plan9 " + "entry from plan9.ini\n"); + rp = "/root"; + if(bind(rp, "/", MAFTER|MCREATE) < 0) fatal("second bind /"); } } - close(fd); setenv("rootdir", rp); + *rspp = rsp; + return afd; +} - settime(islocal, afd, rsp); - if(afd > 0) - close(afd); - swapproc(); +static void +execinit(void) +{ + int iargc; + char *cmd, cmdbuf[64], *iargv[16]; + /* exec init */ cmd = getenv("init"); if(cmd == nil){ sprint(cmdbuf, "/%s/init -%s%s", cputype, @@ -222,6 +222,64 @@ chmod("/srv/" PARTSRV, 0600); exec(cmd, iargv); fatal(cmd); +} + +void +boot(int argc, char *argv[]) +{ + int fd, afd, islocal, ishybrid; + char *rsp; + Method *mp; + + fmtinstall('r', errfmt); + opencons(); + bindenvsrv(); + debuginit(argc, argv); + + ARGBEGIN{ + case 'k': + kflag = 1; + break; + case 'm': + mflag = 1; + break; + case 'f': + fflag = 1; + break; + }ARGEND + + readfile("#e/cputype", cputype, sizeof(cputype)); + + /* + * set up usb keyboard & mouse, if any. + * starts partfs on first disk, if any, to permit nvram on usb. + */ + usbinit(Dontpost); + + mp = pickmethod(argc, argv); + islocal = strcmp(mp->name, "local") == 0; + ishybrid = strcmp(mp->name, "hybrid") == 0; + + kbmap(); /* load keymap if it's there. */ + + /* don't trigger aoe until the network has been configured */ + bind("#æ", "/dev", MAFTER); /* nvram could be here */ + bind("#S", "/dev", MAFTER); /* nvram could be here */ + partinit(); + + doauth(cpuflag); /* authentication usually changes hostowner */ + rfork(RFNAMEG); /* leave existing subprocs in own namespace */ + usbinit(Post); /* restart partfs under the new hostowner id */ + fd = connectroot(mp, islocal, ishybrid); + afd = nsinit(fd, &rsp); + close(fd); + + settime(islocal, afd, rsp); + if(afd > 0) + close(afd); + swapproc(); + execinit(); + exits("failed to exec init"); } static Method* --- /sys/src/9/ip/ipv6.c Thu Feb 7 15:55:44 2013 +++ /sys/src/9/ip/ipv6.c Mon Dec 2 23:42:04 2013 @@ -259,7 +259,7 @@ tentative = iptentative(f, v6dst); if(tentative && h->proto != ICMPv6) { - print("tentative addr, drop\n"); + print("ipv6 tentative addr %I, drop\n", v6dst); freeblist(bp); return; } --- /sys/src/9/pc/ether82563.c Sat Jun 29 05:24:23 2013 +++ /sys/src/9/pc/ether82563.c Mon Dec 2 23:40:06 2013 @@ -1370,7 +1370,7 @@ } static int -i82563detach(Ctlr* ctlr) +i82563detach0(Ctlr* ctlr) { int r, timeo; @@ -1442,6 +1442,18 @@ return 0; } +static int +i82563detach(Ctlr* ctlr) +{ + int r; + static Lock detlck; + + ilock(&detlck); + r = i82563detach0(ctlr); + iunlock(&detlck); + return r; +} + static void i82563shutdown(Ether* ether) { @@ -1451,9 +1463,13 @@ static ushort eeread(Ctlr *ctlr, int adr) { + ulong n; + csr32w(ctlr, Eerd, EEstart | adr << 2); - while ((csr32r(ctlr, Eerd) & EEdone) == 0) + for (n = 1000000; (csr32r(ctlr, Eerd) & EEdone) == 0 && n-- > 0; ) ; + if (n == 0) + panic("i82563: eeread stuck"); return csr32r(ctlr, Eerd) >> 16; } @@ -1494,6 +1510,7 @@ fread(Ctlr *c, Flash *f, int ladr) { ushort s; + ulong n; delay(1); if(fcycle(c, f) == -1) @@ -1508,8 +1525,10 @@ s &= ~(2*Flcycle); /* read */ f->reg[Fctl] = s | Fgo; - while((f->reg[Fsts] & Fdone) == 0) + for (n = 1000000; (f->reg[Fsts] & Fdone) == 0 && n-- > 0; ) ; + if (n == 0) + panic("i82563: fread stuck"); if(f->reg[Fsts] & (Fcerr|Ael)) return -1; return f->reg32[Fdata] & 0xffff; --- /sys/src/9/pc/ether82598.c Wed Apr 10 21:34:50 2013 +++ /sys/src/9/pc/ether82598.c Mon Dec 2 23:40:32 2013 @@ -384,7 +384,8 @@ readstats(c); for(i = 0; i < nelem(stattab); i++) if(c->stats[i] > 0) - p = seprint(p, q, "%.10s %uld\n", stattab[i].name, c->stats[i]); + p = seprint(p, q, "%.10s %uld\n", stattab[i].name, + c->stats[i]); t = c->speeds; p = seprint(p, q, "speeds: 0:%d 1000:%d 10000:%d\n", t[0], t[1], t[2]); p = seprint(p, q, "mtu: min:%d max:%d\n", e->minmtu, e->maxmtu); --- /sys/src/9/port/proc.c Wed Sep 4 20:19:44 2013 +++ /sys/src/9/port/proc.c Mon Dec 2 23:42:53 2013 @@ -581,6 +581,24 @@ return ok; } +void +noprocpanic(char *msg) +{ + /* + * setting exiting will make hzclock() on each processor call exit(0). + * clearing our bit in machs avoids calling exit(0) from hzclock() + * on this processor. + */ + lock(&active); + active.machs &= ~(1<machno); + active.exiting = 1; + unlock(&active); + + procdump(); + delay(1000); + panic(msg); +} + Proc* newproc(void) { @@ -588,13 +606,19 @@ Proc *p; lock(&procalloc); - for(;;) { - if(p = procalloc.free) - break; + while((p = procalloc.free) == nil) { + unlock(&procalloc); snprint(msg, sizeof msg, "no procs; %s forking", up? up->text: "kernel"); - unlock(&procalloc); + /* + * the situation is unlikely to heal itself. + * dump the proc table and restart by default. + * *noprocspersist in plan9.ini will yield the old + * behaviour of trying forever. + */ + if(getconf("*noprocspersist") == nil) + noprocpanic(msg); resrcwait(msg); lock(&procalloc); } --- /sys/src/cmd/upas/ned/nedmail.c Thu Feb 14 23:21:41 2013 +++ /sys/src/cmd/upas/ned/nedmail.c Mon Dec 2 23:27:20 2013 @@ -59,6 +59,7 @@ { "text/tab-separated-values", "tsv", 1, 0 }, { "text/richtext", "rtx", 1, 0 }, { "text/rtf", "rtf", 1, 0 }, + { "text/calendar", "ics", 1, 0 }, { "text", "txt", 1, 0 }, { "message/rfc822", "msg", 0, 0 }, { "image/bmp", "bmp", 0, "image" }, @@ -1321,6 +1322,22 @@ return m->next; } +/* turn crlfs into newlines */ +int +decrlf(char *buf, int n) +{ + char *nl; + int left; + + for (nl = buf, left = n; left >= 2 && + (nl = memchr(nl, '\r', left)) != nil; left = n - (nl - buf)) + if (nl[1] == '\n'){ + memmove(nl, nl+1, left-1); /* delete the cr */ + --n; + } + return n; +} + int printpart(String *s, char *part) { @@ -1339,6 +1356,7 @@ while((n = read(fd, buf, sizeof(buf))) > 0){ if(interrupted) break; + n = decrlf(buf, n); if(Bwrite(&out, buf, n) <= 0) break; tot += n; @@ -1388,6 +1406,66 @@ *np = 0; } +/* + * find the best alternative part. + * + * turkeys have started emitting empty text/plain parts, + * with the actual content in a text/html part, which complicates the choice. + * + * bigger turkeys emit a tiny base64-encoded text/plain part, + * a small base64-encoded text/html part, and the real content is in + * a text/calendar part. + * + * the magic lengths are empirically derived. + * as turkeys devolve and mutate, this will only get worse. + */ +static Message* +bestalt(Message *m) +{ + Message *nm; + Message *realplain, *realhtml, *realcal; + Ctype *cp; + + realplain = realhtml = realcal = nil; + for(nm = m->child; nm != nil; nm = nm->next){ + cp = findctype(nm); + if(cp->ext != nil) + if(strncmp(cp->ext, "txt", 3) == 0 && nm->len >= 88) + realplain = nm; + else if(strncmp(cp->ext, "html", 3) == 0 && + nm->len >= 670) + realhtml = nm; + else if(strncmp(cp->ext, "ics", 3) == 0) + realcal = nm; + } + if(realplain == nil && realhtml == nil && realcal) + return realcal; /* super-turkey */ + else if(realplain == nil && realhtml) + return realhtml; /* regular turkey */ + else + return realplain; +} + +static void +pralt(Message *m) +{ + Message *nm; + Ctype *cp; + + nm = bestalt(m); + if(nm == nil) + /* no winner. print the first displayable part. */ + for(nm = m->child; nm != nil; nm = nm->next){ + cp = findctype(nm); + if(cp->display) + break; + } + if(nm != nil) + pcmd(nil, nm); + else + hcmd(nil, m); +} + Message* pcmd(Cmd*, Message *m) { @@ -1408,23 +1486,9 @@ printhtml(m); else printpart(m->path, "body"); - } else if(strcmp(m->type, "multipart/alternative") == 0){ - for(nm = m->child; nm != nil; nm = nm->next){ - cp = findctype(nm); - if(cp->ext != nil && strncmp(cp->ext, "txt", 3) == 0) - break; - } - if(nm == nil) - for(nm = m->child; nm != nil; nm = nm->next){ - cp = findctype(nm); - if(cp->display) - break; - } - if(nm != nil) - pcmd(nil, nm); - else - hcmd(nil, m); - } else if(strncmp(m->type, "multipart/", 10) == 0){ + } else if(strcmp(m->type, "multipart/alternative") == 0) + pralt(m); + else if(strncmp(m->type, "multipart/", 10) == 0){ nm = m->child; if(nm != nil){ // always print first part --- /sys/src/libc/9sys/dial.c Sun Sep 9 04:19:33 2012 +++ /sys/src/libc/9sys/dial.c Mon Dec 2 23:48:35 2013 @@ -253,7 +253,7 @@ if (conn - dp->conn != dp->winner && conn->dead && conn->err[0]) { strncpy(besterr, conn->err, ERRMAX-1); - conn->err[ERRMAX-1] = '\0'; + besterr[ERRMAX-1] = '\0'; break; } return dp->winner; @@ -265,8 +265,15 @@ char *dest, *p; dest = strchr(dp->nextaddr, ' '); - if(dest == nil) + if(dest == nil) { + p = strchr(dp->nextaddr, '\n'); + if(p) + *p = '\0'; + werrstr("malformed clone cmd from cs `%s'", dp->nextaddr); + if(p) + *p = '\n'; return -1; + } *dest++ = '\0'; p = strchr(dest, '\n'); if(p == nil) @@ -303,7 +310,7 @@ { int rv, kid, kidme; char *clone, *dest; - char err[ERRMAX], besterr[ERRMAX]; + char besterr[ERRMAX]; dp->winner = -1; dp->nkid = 0; @@ -314,6 +321,8 @@ if (kid < 0) --dp->nkid; else if (kid == 0) { + char err[ERRMAX]; + /* only in kid, to avoid atnotify callbacks in parent */ atnotify(catcher, 1); @@ -324,11 +333,10 @@ _exits(besterr); /* avoid atexit callbacks */ } } + *besterr = '\0'; rv = connectwait(dp, besterr); - if(rv < 0 && *besterr) - werrstr("%s", besterr); - else - werrstr("%s", err); + if(rv < 0) + werrstr("%s", (*besterr? besterr: "unknown error")); return rv; } @@ -341,6 +349,7 @@ char buf[Maxstring], clone[Maxpath], err[ERRMAX], besterr[ERRMAX]; Dest *dp; + werrstr(""); dp = mallocz(sizeof *dp, 1); if(dp == nil) return -1; @@ -367,6 +376,7 @@ /* * ask connection server to translate + * e.g., net!cs.bell-labs.com!smtp */ snprint(buf, sizeof(buf), "%s!%s", ds->proto, ds->rem); if(write(fd, buf, strlen(buf)) < 0){ @@ -376,7 +386,11 @@ } /* - * read all addresses from the connection server. + * read all addresses from the connection server: + * /net/tcp/clone 135.104.9.78!25 + * /net/tcp/clone 2620:0:dc0:1805::29!25 + * + * assumes that we'll get one record per read. */ seek(fd, 0, 0); addrs = 0; @@ -389,6 +403,8 @@ addrp += n; bleft -= n; } + *addrp = '\0'; + /* * if we haven't read all of cs's output, assume the last line might * have been truncated and ignore it. we really don't expect this @@ -522,7 +538,8 @@ ds->netdir = 0; ds->proto = ds->buf; } else { - for(p2 = p; *p2 != '/'; p2--) + /* expecting /net.alt/tcp!foo or #I1/tcp!foo */ + for(p2 = p; p2 > ds->buf && *p2 != '/'; p2--) ; *p2++ = 0; ds->netdir = ds->buf; --- /sys/src/libmach/sym.c Thu Jul 18 01:43:02 2013 +++ /sys/src/libmach/sym.c Mon Dec 2 23:46:53 2013 @@ -1109,11 +1109,15 @@ { int i, j; char *c, *bp, *end; + Sym *sym; bp = buf; end = buf+n-1; for(i = 1; j = (cp[i]<<8)|cp[i+1]; i+=2){ - c = fp[j]->name; + sym = fp[j]; + if (sym == nil) + break; + c = sym->name; if(bp != buf && bp[-1] != '/' && bp < end) *bp++ = '/'; while(bp < end && *c)