| #include <u.h> |
| #include <libc.h> |
| #include <auth.h> |
| #include <fcall.h> |
| #include "dat.h" |
| #include "fns.h" |
| #include "iso9660.h" |
| |
| static void ireset(void); |
| static int iattach(Xfile*); |
| static void iclone(Xfile*, Xfile*); |
| static void iwalkup(Xfile*); |
| static void iwalk(Xfile*, char*); |
| static void iopen(Xfile*, int); |
| static void icreate(Xfile*, char*, long, int); |
| static long ireaddir(Xfile*, uchar*, long, long); |
| static long iread(Xfile*, char*, vlong, long); |
| static long iwrite(Xfile*, char*, vlong, long); |
| static void iclunk(Xfile*); |
| static void iremove(Xfile*); |
| static void istat(Xfile*, Dir*); |
| static void iwstat(Xfile*, Dir*); |
| |
| static char* nstr(uchar*, int); |
| static char* rdate(uchar*, int); |
| static int getcontin(Xdata*, uchar*, uchar**); |
| static int getdrec(Xfile*, void*); |
| static void ungetdrec(Xfile*); |
| static int opendotdot(Xfile*, Xfile*); |
| static int showdrec(int, int, void*); |
| static long gtime(uchar*); |
| static long l16(void*); |
| static long l32(void*); |
| static void newdrec(Xfile*, Drec*); |
| static int rzdir(Xfs*, Dir*, int, Drec*); |
| |
| Xfsub isosub = |
| { |
| ireset, iattach, iclone, iwalkup, iwalk, iopen, icreate, |
| ireaddir, iread, iwrite, iclunk, iremove, istat, iwstat |
| }; |
| |
| static void |
| ireset(void) |
| {} |
| |
| static int |
| iattach(Xfile *root) |
| { |
| Xfs *cd = root->xf; |
| Iobuf *p; Voldesc *v; Isofile *fp; Drec *dp; |
| int fmt, blksize, i, n, l, haveplan9; |
| Iobuf *dirp; |
| uchar dbuf[256]; |
| Drec *rd = (Drec *)dbuf; |
| uchar *q, *s; |
| |
| dirp = nil; |
| blksize = 0; |
| fmt = 0; |
| dp = nil; |
| haveplan9 = 0; |
| for(i=VOLDESC;i<VOLDESC+100; i++){ /* +100 for sanity */ |
| p = getbuf(cd->d, i); |
| v = (Voldesc*)(p->iobuf); |
| if(memcmp(v->byte, "\01CD001\01", 7) == 0){ /* iso */ |
| if(dirp) |
| putbuf(dirp); |
| dirp = p; |
| fmt = 'z'; |
| dp = (Drec*)v->z.desc.rootdir; |
| blksize = l16(v->z.desc.blksize); |
| chat("iso, blksize=%d...", blksize); |
| |
| v = (Voldesc*)(dirp->iobuf); |
| haveplan9 = (strncmp((char*)v->z.boot.sysid, "PLAN 9", 6)==0); |
| if(haveplan9){ |
| if(noplan9) { |
| chat("ignoring plan9"); |
| haveplan9 = 0; |
| } else { |
| fmt = '9'; |
| chat("plan9 iso..."); |
| } |
| } |
| continue; |
| } |
| |
| if(memcmp(&v->byte[8], "\01CDROM\01", 7) == 0){ /* high sierra */ |
| if(dirp) |
| putbuf(dirp); |
| dirp = p; |
| fmt = 'r'; |
| dp = (Drec*)v->r.desc.rootdir; |
| blksize = l16(v->r.desc.blksize); |
| chat("high sierra, blksize=%d...", blksize); |
| continue; |
| } |
| |
| if(haveplan9==0 && !nojoliet |
| && memcmp(v->byte, "\02CD001\01", 7) == 0){ |
| chat("%d %d\n", haveplan9, nojoliet); |
| /* |
| * The right thing to do is walk the escape sequences looking |
| * for one of 25 2F 4[035], but Microsoft seems to not honor |
| * the format, which makes it hard to walk over. |
| */ |
| q = v->z.desc.escapes; |
| if(q[0] == 0x25 && q[1] == 0x2F && (q[2] == 0x40 || q[2] == 0x43 || q[2] == 0x45)){ /* Joliet, it appears */ |
| if(dirp) |
| putbuf(dirp); |
| dirp = p; |
| fmt = 'J'; |
| dp = (Drec*)v->z.desc.rootdir; |
| if(blksize != l16(v->z.desc.blksize)) |
| fprint(2, "warning: suspicious Joliet blocksize\n"); |
| chat("joliet..."); |
| continue; |
| } |
| } |
| putbuf(p); |
| if(v->byte[0] == 0xFF) |
| break; |
| } |
| |
| if(fmt == 0){ |
| if(dirp) |
| putbuf(dirp); |
| return -1; |
| } |
| assert(dirp != nil); |
| |
| if(chatty) |
| showdrec(2, fmt, dp); |
| if(blksize > Sectorsize){ |
| chat("blksize too big..."); |
| putbuf(dirp); |
| return -1; |
| } |
| if(waserror()){ |
| putbuf(dirp); |
| nexterror(); |
| } |
| root->len = sizeof(Isofile) - sizeof(Drec) + dp->z.reclen; |
| root->ptr = fp = ealloc(root->len); |
| |
| if(haveplan9) |
| root->xf->isplan9 = 1; |
| |
| fp->fmt = fmt; |
| fp->blksize = blksize; |
| fp->offset = 0; |
| fp->doffset = 0; |
| memmove(&fp->d, dp, dp->z.reclen); |
| root->qid.path = l32(dp->z.addr); |
| root->qid.type = QTDIR; |
| putbuf(dirp); |
| poperror(); |
| if(getdrec(root, rd) >= 0){ |
| n = rd->z.reclen-(34+rd->z.namelen); |
| s = (uchar*)rd->z.name + rd->z.namelen; |
| if((uintptr)s & 1){ |
| s++; |
| n--; |
| } |
| if(n >= 7 && s[0] == 'S' && s[1] == 'P' && s[2] == 7 && |
| s[3] == 1 && s[4] == 0xBE && s[5] == 0xEF){ |
| root->xf->issusp = 1; |
| root->xf->suspoff = s[6]; |
| n -= root->xf->suspoff; |
| s += root->xf->suspoff; |
| for(; n >= 4; s += l, n -= l){ |
| l = s[2]; |
| if(s[0] == 'E' && s[1] == 'R'){ |
| if(!norock && s[4] == 10 && memcmp(s+8, "RRIP_1991A", 10) == 0) |
| root->xf->isrock = 1; |
| break; |
| } else if(s[0] == 'C' && s[1] == 'E' && s[2] >= 28){ |
| n = getcontin(root->xf->d, s, &s); |
| continue; |
| } else if(s[0] == 'R' && s[1] == 'R'){ |
| if(!norock) |
| root->xf->isrock = 1; |
| break; |
| } else if(s[0] == 'S' && s[1] == 'T') |
| break; |
| } |
| } |
| } |
| if(root->xf->isrock) |
| chat("Rock Ridge..."); |
| fp->offset = 0; |
| fp->doffset = 0; |
| return 0; |
| } |
| |
| static void |
| iclone(Xfile *of, Xfile *nf) |
| { |
| USED(of); |
| USED(nf); |
| } |
| |
| static void |
| iwalkup(Xfile *f) |
| { |
| long paddr; |
| uchar dbuf[256]; |
| Drec *d = (Drec *)dbuf; |
| Xfile pf, ppf; |
| Isofile piso, ppiso; |
| |
| memset(&pf, 0, sizeof pf); |
| memset(&ppf, 0, sizeof ppf); |
| pf.ptr = &piso; |
| ppf.ptr = &ppiso; |
| if(opendotdot(f, &pf) < 0) |
| error("can't open pf"); |
| paddr = l32(pf.ptr->d.z.addr); |
| if(l32(f->ptr->d.z.addr) == paddr) |
| return; |
| if(opendotdot(&pf, &ppf) < 0) |
| error("can't open ppf"); |
| while(getdrec(&ppf, d) >= 0){ |
| if(l32(d->z.addr) == paddr){ |
| newdrec(f, d); |
| f->qid.path = paddr; |
| f->qid.type = QTDIR; |
| return; |
| } |
| } |
| error("can't find addr of .."); |
| } |
| |
| static int |
| casestrcmp(int isplan9, char *a, char *b) |
| { |
| int ca, cb; |
| |
| if(isplan9) |
| return strcmp(a, b); |
| for(;;) { |
| ca = *a++; |
| cb = *b++; |
| if(ca >= 'A' && ca <= 'Z') |
| ca += 'a' - 'A'; |
| if(cb >= 'A' && cb <= 'Z') |
| cb += 'a' - 'A'; |
| if(ca != cb) { |
| if(ca > cb) |
| return 1; |
| return -1; |
| } |
| if(ca == 0) |
| return 0; |
| } |
| } |
| |
| static void |
| iwalk(Xfile *f, char *name) |
| { |
| Isofile *ip = f->ptr; |
| uchar dbuf[256]; |
| char nbuf[4*Maxname]; |
| Drec *d = (Drec*)dbuf; |
| Dir dir; |
| char *p; |
| int len, vers, dvers; |
| |
| vers = -1; |
| if(p = strchr(name, ';')) { /* assign = */ |
| len = p-name; |
| if(len >= Maxname) |
| len = Maxname-1; |
| memmove(nbuf, name, len); |
| vers = strtoul(p+1, 0, 10); |
| name = nbuf; |
| } |
| /* |
| len = strlen(name); |
| if(len >= Maxname){ |
| len = Maxname-1; |
| if(name != nbuf){ |
| memmove(nbuf, name, len); |
| name = nbuf; |
| } |
| name[len] = 0; |
| } |
| */ |
| |
| chat("%d \"%s\"...", strlen(name), name); |
| ip->offset = 0; |
| setnames(&dir, nbuf); |
| while(getdrec(f, d) >= 0) { |
| dvers = rzdir(f->xf, &dir, ip->fmt, d); |
| if(casestrcmp(f->xf->isplan9||f->xf->isrock, name, dir.name) != 0) |
| continue; |
| newdrec(f, d); |
| f->qid.path = dir.qid.path; |
| f->qid.type = dir.qid.type; |
| USED(dvers); |
| return; |
| } |
| USED(vers); |
| error(Enonexist); |
| } |
| |
| static void |
| iopen(Xfile *f, int mode) |
| { |
| mode &= ~OCEXEC; |
| if(mode != OREAD && mode != OEXEC) |
| error(Eperm); |
| f->ptr->offset = 0; |
| f->ptr->doffset = 0; |
| } |
| |
| static void |
| icreate(Xfile *f, char *name, long perm, int mode) |
| { |
| USED(f); |
| USED(name); |
| USED(perm); |
| USED(mode); |
| error(Eperm); |
| } |
| |
| static long |
| ireaddir(Xfile *f, uchar *buf, long offset, long count) |
| { |
| Isofile *ip = f->ptr; |
| Dir d; |
| char names[4*Maxname]; |
| uchar dbuf[256]; |
| Drec *drec = (Drec *)dbuf; |
| int n, rcnt; |
| |
| if(offset==0){ |
| ip->offset = 0; |
| ip->doffset = 0; |
| }else if(offset != ip->doffset) |
| error("seek in directory not allowed"); |
| |
| rcnt = 0; |
| setnames(&d, names); |
| while(rcnt < count && getdrec(f, drec) >= 0){ |
| if(drec->z.namelen == 1){ |
| if(drec->z.name[0] == 0) |
| continue; |
| if(drec->z.name[0] == 1) |
| continue; |
| } |
| rzdir(f->xf, &d, ip->fmt, drec); |
| d.qid.vers = f->qid.vers; |
| if((n = convD2M(&d, buf+rcnt, count-rcnt)) <= BIT16SZ){ |
| ungetdrec(f); |
| break; |
| } |
| rcnt += n; |
| } |
| ip->doffset += rcnt; |
| return rcnt; |
| } |
| |
| static long |
| iread(Xfile *f, char *buf, vlong offset, long count) |
| { |
| int n, o, rcnt = 0; |
| long size; |
| vlong addr; |
| Isofile *ip = f->ptr; |
| Iobuf *p; |
| |
| size = l32(ip->d.z.size); |
| if(offset >= size) |
| return 0; |
| if(offset+count > size) |
| count = size - offset; |
| addr = ((vlong)l32(ip->d.z.addr) + ip->d.z.attrlen)*ip->blksize + offset; |
| o = addr % Sectorsize; |
| addr /= Sectorsize; |
| /*chat("d.addr=%ld, addr=%lld, o=%d...", l32(ip->d.z.addr), addr, o);*/ |
| n = Sectorsize - o; |
| |
| while(count > 0){ |
| if(n > count) |
| n = count; |
| p = getbuf(f->xf->d, addr); |
| memmove(&buf[rcnt], &p->iobuf[o], n); |
| putbuf(p); |
| count -= n; |
| rcnt += n; |
| ++addr; |
| o = 0; |
| n = Sectorsize; |
| } |
| return rcnt; |
| } |
| |
| static long |
| iwrite(Xfile *f, char *buf, vlong offset, long count) |
| { |
| USED(f); |
| USED(buf); |
| USED(offset); |
| USED(count); |
| error(Eperm); |
| return 0; |
| } |
| |
| static void |
| iclunk(Xfile *f) |
| { |
| USED(f); |
| } |
| |
| static void |
| iremove(Xfile *f) |
| { |
| USED(f); |
| error(Eperm); |
| } |
| |
| static void |
| istat(Xfile *f, Dir *d) |
| { |
| Isofile *ip = f->ptr; |
| |
| rzdir(f->xf, d, ip->fmt, &ip->d); |
| d->qid.vers = f->qid.vers; |
| if(d->qid.path==f->xf->rootqid.path){ |
| d->qid.path = 0; |
| d->qid.type = QTDIR; |
| } |
| } |
| |
| static void |
| iwstat(Xfile *f, Dir *d) |
| { |
| USED(f); |
| USED(d); |
| error(Eperm); |
| } |
| |
| static int |
| showdrec(int fd, int fmt, void *x) |
| { |
| Drec *d = (Drec *)x; |
| int namelen; |
| int syslen; |
| |
| if(d->z.reclen == 0) |
| return 0; |
| fprint(fd, "%d %d %ld %ld ", |
| d->z.reclen, d->z.attrlen, l32(d->z.addr), l32(d->z.size)); |
| fprint(fd, "%s 0x%2.2x %d %d %ld ", |
| rdate(d->z.date, fmt), (fmt=='z' ? d->z.flags : d->r.flags), |
| d->z.unitsize, d->z.gapsize, l16(d->z.vseqno)); |
| fprint(fd, "%d %s", d->z.namelen, nstr(d->z.name, d->z.namelen)); |
| if(fmt != 'J'){ |
| namelen = d->z.namelen + (1-(d->z.namelen&1)); |
| syslen = d->z.reclen - 33 - namelen; |
| if(syslen != 0) |
| fprint(fd, " %s", nstr(&d->z.name[namelen], syslen)); |
| } |
| fprint(fd, "\n"); |
| return d->z.reclen + (d->z.reclen&1); |
| } |
| |
| static void |
| newdrec(Xfile *f, Drec *dp) |
| { |
| Isofile *x = f->ptr; |
| Isofile *n; |
| int len; |
| |
| len = sizeof(Isofile) - sizeof(Drec) + dp->z.reclen; |
| n = ealloc(len); |
| n->fmt = x->fmt; |
| n->blksize = x->blksize; |
| n->offset = 0; |
| n->doffset = 0; |
| memmove(&n->d, dp, dp->z.reclen); |
| free(x); |
| f->ptr = n; |
| f->len = len; |
| } |
| |
| static void |
| ungetdrec(Xfile *f) |
| { |
| Isofile *ip = f->ptr; |
| |
| if(ip->offset >= ip->odelta){ |
| ip->offset -= ip->odelta; |
| ip->odelta = 0; |
| } |
| } |
| |
| static int |
| getdrec(Xfile *f, void *buf) |
| { |
| Isofile *ip = f->ptr; |
| int len = 0, boff = 0; |
| ulong size; |
| vlong addr; |
| Iobuf *p = 0; |
| |
| if(!ip) |
| return -1; |
| size = l32(ip->d.z.size); |
| while(ip->offset < size){ |
| addr = (l32(ip->d.z.addr)+ip->d.z.attrlen)*ip->blksize + ip->offset; |
| boff = addr % Sectorsize; |
| if(boff > Sectorsize-34){ |
| ip->offset += Sectorsize-boff; |
| continue; |
| } |
| p = getbuf(f->xf->d, addr/Sectorsize); |
| len = p->iobuf[boff]; |
| if(len >= 34) |
| break; |
| putbuf(p); |
| p = 0; |
| ip->offset += Sectorsize-boff; |
| } |
| if(p) { |
| memmove(buf, &p->iobuf[boff], len); |
| putbuf(p); |
| ip->odelta = len + (len&1); |
| ip->offset += ip->odelta; |
| return 0; |
| } |
| return -1; |
| } |
| |
| static int |
| opendotdot(Xfile *f, Xfile *pf) |
| { |
| uchar dbuf[256]; |
| Drec *d = (Drec *)dbuf; |
| Isofile *ip = f->ptr, *pip = pf->ptr; |
| |
| ip->offset = 0; |
| if(getdrec(f, d) < 0){ |
| chat("opendotdot: getdrec(.) failed..."); |
| return -1; |
| } |
| if(d->z.namelen != 1 || d->z.name[0] != 0){ |
| chat("opendotdot: no . entry..."); |
| return -1; |
| } |
| if(l32(d->z.addr) != l32(ip->d.z.addr)){ |
| chat("opendotdot: bad . address..."); |
| return -1; |
| } |
| if(getdrec(f, d) < 0){ |
| chat("opendotdot: getdrec(..) failed..."); |
| return -1; |
| } |
| if(d->z.namelen != 1 || d->z.name[0] != 1){ |
| chat("opendotdot: no .. entry..."); |
| return -1; |
| } |
| |
| pf->xf = f->xf; |
| pip->fmt = ip->fmt; |
| pip->blksize = ip->blksize; |
| pip->offset = 0; |
| pip->doffset = 0; |
| pip->d = *d; |
| return 0; |
| } |
| |
| enum { |
| Hname = 1, |
| Hmode = 2, |
| }; |
| |
| static int |
| rzdir(Xfs *fs, Dir *d, int fmt, Drec *dp) |
| { |
| int n, flags, i, j, lj, nl, vers, sysl, mode, l, have; |
| uchar *s; |
| char *p; |
| char buf[Maxname+UTFmax+1]; |
| uchar *q; |
| Rune r; |
| enum { ONAMELEN = 28 }; /* old Plan 9 directory name length */ |
| |
| have = 0; |
| flags = 0; |
| vers = -1; |
| d->qid.path = l32(dp->z.addr); |
| d->qid.type = 0; |
| d->qid.vers = 0; |
| n = dp->z.namelen; |
| memset(d->name, 0, Maxname); |
| if(n == 1) { |
| switch(dp->z.name[0]){ |
| case 1: |
| d->name[1] = '.'; |
| /* fall through */ |
| case 0: |
| d->name[0] = '.'; |
| have = Hname; |
| break; |
| default: |
| d->name[0] = tolower(dp->z.name[0]); |
| } |
| } else { |
| if(fmt == 'J'){ /* Joliet, 16-bit Unicode */ |
| q = (uchar*)dp->z.name; |
| for(i=j=lj=0; i<n && j<Maxname; i+=2){ |
| lj = j; |
| r = (q[i]<<8)|q[i+1]; |
| j += runetochar(buf+j, &r); |
| } |
| if(j >= Maxname) |
| j = lj; |
| memmove(d->name, buf, j); |
| }else{ |
| if(n >= Maxname) |
| n = Maxname-1; |
| for(i=0; i<n; i++) |
| d->name[i] = tolower(dp->z.name[i]); |
| } |
| } |
| |
| sysl = dp->z.reclen-(34+dp->z.namelen); |
| s = (uchar*)dp->z.name + dp->z.namelen; |
| if(((uintptr)s) & 1) { |
| s++; |
| sysl--; |
| } |
| if(fs->isplan9 && sysl > 0) { |
| /* |
| * get gid, uid, mode and possibly name |
| * from plan9 directory extension |
| */ |
| nl = *s; |
| if(nl >= ONAMELEN) |
| nl = ONAMELEN-1; |
| if(nl) { |
| memset(d->name, 0, ONAMELEN); |
| memmove(d->name, s+1, nl); |
| } |
| s += 1 + *s; |
| nl = *s; |
| if(nl >= ONAMELEN) |
| nl = ONAMELEN-1; |
| memset(d->uid, 0, ONAMELEN); |
| memmove(d->uid, s+1, nl); |
| s += 1 + *s; |
| nl = *s; |
| if(nl >= ONAMELEN) |
| nl = ONAMELEN-1; |
| memset(d->gid, 0, ONAMELEN); |
| memmove(d->gid, s+1, nl); |
| s += 1 + *s; |
| if(((uintptr)s) & 1) |
| s++; |
| d->mode = l32(s); |
| if(d->mode & DMDIR) |
| d->qid.type |= QTDIR; |
| } else { |
| d->mode = 0444; |
| switch(fmt) { |
| case 'z': |
| if(fs->isrock) |
| strcpy(d->gid, "ridge"); |
| else |
| strcpy(d->gid, "iso9660"); |
| flags = dp->z.flags; |
| break; |
| case 'r': |
| strcpy(d->gid, "sierra"); |
| flags = dp->r.flags; |
| break; |
| case 'J': |
| strcpy(d->gid, "joliet"); |
| flags = dp->z.flags; |
| break; |
| case '9': |
| strcpy(d->gid, "plan9"); |
| flags = dp->z.flags; |
| break; |
| } |
| if(flags & 0x02){ |
| d->qid.type |= QTDIR; |
| d->mode |= DMDIR|0111; |
| } |
| strcpy(d->uid, "cdrom"); |
| if(fmt!='9' && !(d->mode&DMDIR)){ |
| /* |
| * ISO 9660 actually requires that you always have a . and a ;, |
| * even if there is no version and no extension. Very few writers |
| * do this. If the version is present, we use it for qid.vers. |
| * If there is no extension but there is a dot, we strip it off. |
| * (VMS heads couldn't comprehend the dot as a file name character |
| * rather than as just a separator between name and extension.) |
| * |
| * We don't do this for directory names because directories are |
| * not allowed to have extensions and versions. |
| */ |
| if((p=strchr(d->name, ';')) != nil){ |
| vers = strtoul(p+1, 0, 0); |
| d->qid.vers = vers; |
| *p = '\0'; |
| } |
| if((p=strchr(d->name, '.')) != nil && *(p+1)=='\0') |
| *p = '\0'; |
| } |
| if(fs->issusp){ |
| nl = 0; |
| s += fs->suspoff; |
| sysl -= fs->suspoff; |
| for(; sysl >= 4 && have != (Hname|Hmode); sysl -= l, s += l){ |
| if(s[0] == 0 && ((uintptr)s & 1)){ |
| /* MacOS pads individual entries, contrary to spec */ |
| s++; |
| sysl--; |
| } |
| l = s[2]; |
| if(s[0] == 'P' && s[1] == 'X' && s[3] == 1){ |
| /* posix file attributes */ |
| mode = l32(s+4); |
| d->mode = mode & 0777; |
| if((mode & 0170000) == 040000){ |
| d->mode |= DMDIR; |
| d->qid.type |= QTDIR; |
| } |
| have |= Hmode; |
| } else if(s[0] == 'N' && s[1] == 'M' && s[3] == 1){ |
| /* alternative name */ |
| if((s[4] & ~1) == 0){ |
| i = nl+l-5; |
| if(i >= Maxname) |
| i = Maxname-1; |
| if((i -= nl) > 0){ |
| memmove(d->name+nl, s+5, i); |
| nl += i; |
| } |
| if(s[4] == 0) |
| have |= Hname; |
| } |
| } else if(s[0] == 'C' && s[1] == 'E' && s[2] >= 28){ |
| sysl = getcontin(fs->d, s, &s); |
| continue; |
| } else if(s[0] == 'S' && s[1] == 'T') |
| break; |
| } |
| } |
| } |
| d->length = 0; |
| if((d->mode & DMDIR) == 0) |
| d->length = l32(dp->z.size); |
| d->type = 0; |
| d->dev = 0; |
| d->atime = gtime(dp->z.date); |
| d->mtime = d->atime; |
| return vers; |
| } |
| |
| static int |
| getcontin(Xdata *dev, uchar *p, uchar **s) |
| { |
| long bn, off, len; |
| Iobuf *b; |
| |
| bn = l32(p+4); |
| off = l32(p+12); |
| len = l32(p+20); |
| chat("getcontin %d...", bn); |
| b = getbuf(dev, bn); |
| if(b == 0){ |
| *s = 0; |
| return 0; |
| } |
| *s = b->iobuf+off; |
| putbuf(b); |
| return len; |
| } |
| |
| static char * |
| nstr(uchar *p, int n) |
| { |
| static char buf[132]; |
| char *q = buf; |
| |
| while(--n >= 0){ |
| if(*p == '\\') |
| *q++ = '\\'; |
| if(' ' <= *p && *p <= '~') |
| *q++ = *p++; |
| else |
| q += sprint(q, "\\%2.2ux", *p++); |
| } |
| *q = 0; |
| return buf; |
| } |
| |
| static char * |
| rdate(uchar *p, int fmt) |
| { |
| static char buf[64]; |
| int htz, s, n; |
| |
| n = sprint(buf, "%2.2d.%2.2d.%2.2d %2.2d:%2.2d:%2.2d", |
| p[0], p[1], p[2], p[3], p[4], p[5]); |
| if(fmt == 'z'){ |
| htz = p[6]; |
| if(htz >= 128){ |
| htz = 256-htz; |
| s = '-'; |
| }else |
| s = '+'; |
| sprint(&buf[n], " (%c%.1f)", s, (float)htz/2); |
| } |
| return buf; |
| } |
| |
| static char |
| dmsize[12] = |
| { |
| 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, |
| }; |
| |
| #define dysize mydysize |
| |
| static int |
| dysize(int y) |
| { |
| |
| if((y%4) == 0) |
| return 366; |
| return 365; |
| } |
| |
| static long |
| gtime(uchar *p) /* yMdhmsz */ |
| { |
| long t; |
| int i, y, M, d, h, m, s, tz; |
| |
| y=p[0]; M=p[1]; d=p[2]; |
| h=p[3]; m=p[4]; s=p[5]; tz=p[6]; |
| USED(tz); |
| y += 1900; |
| if (y < 1970) |
| return 0; |
| if (M < 1 || M > 12) |
| return 0; |
| if (d < 1 || d > dmsize[M-1]) |
| if (!(M == 2 && d == 29 && dysize(y) == 366)) |
| return 0; |
| if (h > 23) |
| return 0; |
| if (m > 59) |
| return 0; |
| if (s > 59) |
| return 0; |
| t = 0; |
| for(i=1970; i<y; i++) |
| t += dysize(i); |
| if (dysize(y)==366 && M >= 3) |
| t++; |
| while(--M) |
| t += dmsize[M-1]; |
| t += d-1; |
| t = 24*t + h; |
| t = 60*t + m; |
| t = 60*t + s; |
| return t; |
| } |
| |
| #define p ((uchar*)arg) |
| |
| static long |
| l16(void *arg) |
| { |
| long v; |
| |
| v = ((long)p[1]<<8)|p[0]; |
| if (v >= 0x8000L) |
| v -= 0x10000L; |
| return v; |
| } |
| |
| static long |
| l32(void *arg) |
| { |
| return ((((((long)p[3]<<8)|p[2])<<8)|p[1])<<8)|p[0]; |
| } |
| |
| #undef p |