1 #include <u.h> 2 #include <libc.h> 3 #include <auth.h> 4 #include <fcall.h> 5 #include "dat.h" 6 #include "fns.h" 7 #include "iso9660.h" 8 9 static void ireset(void); 10 static int iattach(Xfile*); 11 static void iclone(Xfile*, Xfile*); 12 static void iwalkup(Xfile*); 13 static void iwalk(Xfile*, char*); 14 static void iopen(Xfile*, int); 15 static void icreate(Xfile*, char*, long, int); 16 static long ireaddir(Xfile*, uchar*, long, long); 17 static long iread(Xfile*, char*, vlong, long); 18 static long iwrite(Xfile*, char*, vlong, long); 19 static void iclunk(Xfile*); 20 static void iremove(Xfile*); 21 static void istat(Xfile*, Dir*); 22 static void iwstat(Xfile*, Dir*); 23 24 static char* nstr(uchar*, int); 25 static char* rdate(uchar*, int); 26 static int getcontin(Xdata*, uchar*, uchar**); 27 static int getdrec(Xfile*, void*); 28 static void ungetdrec(Xfile*); 29 static int opendotdot(Xfile*, Xfile*); 30 static int showdrec(int, int, void*); 31 static long gtime(uchar*); 32 static long l16(void*); 33 static long l32(void*); 34 static void newdrec(Xfile*, Drec*); 35 static int rzdir(Xfs*, Dir*, int, Drec*); 36 37 Xfsub isosub = 38 { 39 ireset, iattach, iclone, iwalkup, iwalk, iopen, icreate, 40 ireaddir, iread, iwrite, iclunk, iremove, istat, iwstat 41 }; 42 43 static vlong 44 fakemax(vlong len) 45 { 46 if(len == (1UL << 31) - 1) /* max. 9660 size? */ 47 len = (1ULL << 63) - 1; /* pretend it's vast */ 48 return len; 49 } 50 51 static void 52 ireset(void) 53 {} 54 55 static int 56 iattach(Xfile *root) 57 { 58 Xfs *cd = root->xf; 59 Iobuf *p; Voldesc *v; Isofile *fp; Drec *dp; 60 int fmt, blksize, i, n, l, haveplan9; 61 Iobuf *dirp; 62 uchar dbuf[256]; 63 Drec *rd = (Drec *)dbuf; 64 uchar *q, *s; 65 66 dirp = nil; 67 blksize = 0; 68 fmt = 0; 69 dp = nil; 70 haveplan9 = 0; 71 for(i=VOLDESC;i<VOLDESC+100; i++){ /* +100 for sanity */ 72 p = getbuf(cd->d, i); 73 v = (Voldesc*)(p->iobuf); 74 if(memcmp(v->byte, "\01CD001\01", 7) == 0){ /* iso */ 75 if(dirp) 76 putbuf(dirp); 77 dirp = p; 78 fmt = 'z'; 79 dp = (Drec*)v->z.desc.rootdir; 80 blksize = l16(v->z.desc.blksize); 81 chat("iso, blksize=%d...", blksize); 82 83 v = (Voldesc*)(dirp->iobuf); 84 haveplan9 = (strncmp((char*)v->z.boot.sysid, "PLAN 9", 6)==0); 85 if(haveplan9){ 86 if(noplan9) { 87 chat("ignoring plan9"); 88 haveplan9 = 0; 89 } else { 90 fmt = '9'; 91 chat("plan9 iso..."); 92 } 93 } 94 continue; 95 } 96 97 if(memcmp(&v->byte[8], "\01CDROM\01", 7) == 0){ /* high sierra */ 98 if(dirp) 99 putbuf(dirp); 100 dirp = p; 101 fmt = 'r'; 102 dp = (Drec*)v->r.desc.rootdir; 103 blksize = l16(v->r.desc.blksize); 104 chat("high sierra, blksize=%d...", blksize); 105 continue; 106 } 107 108 if(haveplan9==0 && !nojoliet 109 && memcmp(v->byte, "\02CD001\01", 7) == 0){ 110 chat("%d %d\n", haveplan9, nojoliet); 111 /* 112 * The right thing to do is walk the escape sequences looking 113 * for one of 25 2F 4[035], but Microsoft seems to not honor 114 * the format, which makes it hard to walk over. 115 */ 116 q = v->z.desc.escapes; 117 if(q[0] == 0x25 && q[1] == 0x2F && (q[2] == 0x40 || q[2] == 0x43 || q[2] == 0x45)){ /* Joliet, it appears */ 118 if(dirp) 119 putbuf(dirp); 120 dirp = p; 121 fmt = 'J'; 122 dp = (Drec*)v->z.desc.rootdir; 123 if(blksize != l16(v->z.desc.blksize)) 124 fprint(2, "warning: suspicious Joliet blocksize\n"); 125 chat("joliet..."); 126 continue; 127 } 128 } 129 putbuf(p); 130 if(v->byte[0] == 0xFF) 131 break; 132 } 133 134 if(fmt == 0){ 135 if(dirp) 136 putbuf(dirp); 137 return -1; 138 } 139 assert(dirp != nil); 140 141 if(chatty) 142 showdrec(2, fmt, dp); 143 if(blksize > Sectorsize){ 144 chat("blksize too big..."); 145 putbuf(dirp); 146 return -1; 147 } 148 if(waserror()){ 149 putbuf(dirp); 150 nexterror(); 151 } 152 root->len = sizeof(Isofile) - sizeof(Drec) + dp->reclen; 153 root->ptr = fp = ealloc(root->len); 154 155 if(haveplan9) 156 root->xf->isplan9 = 1; 157 158 fp->fmt = fmt; 159 fp->blksize = blksize; 160 fp->offset = 0; 161 fp->doffset = 0; 162 memmove(&fp->d, dp, dp->reclen); 163 root->qid.path = l32(dp->addr); 164 root->qid.type = QTDIR; 165 putbuf(dirp); 166 poperror(); 167 if(getdrec(root, rd) >= 0){ 168 n = rd->reclen-(34+rd->namelen); 169 s = (uchar*)rd->name + rd->namelen; 170 if((uintptr)s & 1){ 171 s++; 172 n--; 173 } 174 if(n >= 7 && s[0] == 'S' && s[1] == 'P' && s[2] == 7 && 175 s[3] == 1 && s[4] == 0xBE && s[5] == 0xEF){ 176 root->xf->issusp = 1; 177 root->xf->suspoff = s[6]; 178 n -= root->xf->suspoff; 179 s += root->xf->suspoff; 180 for(; n >= 4; s += l, n -= l){ 181 l = s[2]; 182 if(s[0] == 'E' && s[1] == 'R'){ 183 if(!norock && s[4] == 10 && memcmp(s+8, "RRIP_1991A", 10) == 0) 184 root->xf->isrock = 1; 185 break; 186 } else if(s[0] == 'C' && s[1] == 'E' && s[2] >= 28){ 187 n = getcontin(root->xf->d, s, &s); 188 continue; 189 } else if(s[0] == 'R' && s[1] == 'R'){ 190 if(!norock) 191 root->xf->isrock = 1; 192 break; 193 } else if(s[0] == 'S' && s[1] == 'T') 194 break; 195 } 196 } 197 } 198 if(root->xf->isrock) 199 chat("Rock Ridge..."); 200 fp->offset = 0; 201 fp->doffset = 0; 202 return 0; 203 } 204 205 static void 206 iclone(Xfile *of, Xfile *nf) 207 { 208 USED(of, nf); 209 } 210 211 static void 212 iwalkup(Xfile *f) 213 { 214 long paddr; 215 uchar dbuf[256]; 216 Drec *d = (Drec *)dbuf; 217 Xfile pf, ppf; 218 Isofile piso, ppiso; 219 220 memset(&pf, 0, sizeof pf); 221 memset(&ppf, 0, sizeof ppf); 222 pf.ptr = &piso; 223 ppf.ptr = &ppiso; 224 if(opendotdot(f, &pf) < 0) 225 error("can't open pf"); 226 paddr = l32(pf.ptr->d.addr); 227 if(l32(f->ptr->d.addr) == paddr) 228 return; 229 if(opendotdot(&pf, &ppf) < 0) 230 error("can't open ppf"); 231 while(getdrec(&ppf, d) >= 0){ 232 if(l32(d->addr) == paddr){ 233 newdrec(f, d); 234 f->qid.path = paddr; 235 f->qid.type = QTDIR; 236 return; 237 } 238 } 239 error("can't find addr of .."); 240 } 241 242 static int 243 casestrcmp(int isplan9, char *a, char *b) 244 { 245 int ca, cb; 246 247 if(isplan9) 248 return strcmp(a, b); 249 for(;;) { 250 ca = *a++; 251 cb = *b++; 252 if(ca >= 'A' && ca <= 'Z') 253 ca += 'a' - 'A'; 254 if(cb >= 'A' && cb <= 'Z') 255 cb += 'a' - 'A'; 256 if(ca != cb) { 257 if(ca > cb) 258 return 1; 259 return -1; 260 } 261 if(ca == 0) 262 return 0; 263 } 264 } 265 266 static void 267 iwalk(Xfile *f, char *name) 268 { 269 Isofile *ip = f->ptr; 270 uchar dbuf[256]; 271 char nbuf[4*Maxname]; 272 Drec *d = (Drec*)dbuf; 273 Dir dir; 274 char *p; 275 int len, vers, dvers; 276 277 vers = -1; 278 if(p = strchr(name, ';')) { /* assign = */ 279 len = p-name; 280 if(len >= Maxname) 281 len = Maxname-1; 282 memmove(nbuf, name, len); 283 vers = strtoul(p+1, 0, 10); 284 name = nbuf; 285 } 286 /* 287 len = strlen(name); 288 if(len >= Maxname){ 289 len = Maxname-1; 290 if(name != nbuf){ 291 memmove(nbuf, name, len); 292 name = nbuf; 293 } 294 name[len] = 0; 295 } 296 */ 297 298 chat("%d \"%s\"...", strlen(name), name); 299 ip->offset = 0; 300 setnames(&dir, nbuf); 301 while(getdrec(f, d) >= 0) { 302 dvers = rzdir(f->xf, &dir, ip->fmt, d); 303 if(casestrcmp(f->xf->isplan9||f->xf->isrock, name, dir.name) != 0) 304 continue; 305 newdrec(f, d); 306 f->qid.path = dir.qid.path; 307 f->qid.type = dir.qid.type; 308 USED(dvers); 309 return; 310 } 311 USED(vers); 312 error(Enonexist); 313 } 314 315 static void 316 iopen(Xfile *f, int mode) 317 { 318 mode &= ~OCEXEC; 319 if(mode != OREAD && mode != OEXEC) 320 error(Eperm); 321 f->ptr->offset = 0; 322 f->ptr->doffset = 0; 323 } 324 325 static void 326 icreate(Xfile *f, char *name, long perm, int mode) 327 { 328 USED(f, name, perm, mode); 329 error(Eperm); 330 } 331 332 static long 333 ireaddir(Xfile *f, uchar *buf, long offset, long count) 334 { 335 Isofile *ip = f->ptr; 336 Dir d; 337 char names[4*Maxname]; 338 uchar dbuf[256]; 339 Drec *drec = (Drec *)dbuf; 340 int n, rcnt; 341 342 if(offset==0){ 343 ip->offset = 0; 344 ip->doffset = 0; 345 }else if(offset != ip->doffset) 346 error("seek in directory not allowed"); 347 348 rcnt = 0; 349 setnames(&d, names); 350 while(rcnt < count && getdrec(f, drec) >= 0){ 351 if(drec->namelen == 1){ 352 if(drec->name[0] == 0) 353 continue; 354 if(drec->name[0] == 1) 355 continue; 356 } 357 rzdir(f->xf, &d, ip->fmt, drec); 358 d.qid.vers = f->qid.vers; 359 if((n = convD2M(&d, buf+rcnt, count-rcnt)) <= BIT16SZ){ 360 ungetdrec(f); 361 break; 362 } 363 rcnt += n; 364 } 365 ip->doffset += rcnt; 366 return rcnt; 367 } 368 369 static long 370 iread(Xfile *f, char *buf, vlong offset, long count) 371 { 372 int n, o, rcnt = 0; 373 vlong size, addr; 374 Isofile *ip = f->ptr; 375 Iobuf *p; 376 377 size = fakemax(l32(ip->d.size)); 378 if(offset >= size) 379 return 0; 380 if(offset+count > size) 381 count = size - offset; 382 addr = ((vlong)l32(ip->d.addr) + ip->d.attrlen)*ip->blksize + offset; 383 o = addr % Sectorsize; 384 addr /= Sectorsize; 385 /*chat("d.addr=%ld, addr=%lld, o=%d...", l32(ip->d.addr), addr, o);*/ 386 n = Sectorsize - o; 387 388 while(count > 0){ 389 if(n > count) 390 n = count; 391 p = getbuf(f->xf->d, addr); 392 memmove(&buf[rcnt], &p->iobuf[o], n); 393 putbuf(p); 394 count -= n; 395 rcnt += n; 396 ++addr; 397 o = 0; 398 n = Sectorsize; 399 } 400 return rcnt; 401 } 402 403 static long 404 iwrite(Xfile *f, char *buf, vlong offset, long count) 405 { 406 USED(f, buf, offset, count); 407 error(Eperm); 408 return 0; 409 } 410 411 static void 412 iclunk(Xfile *f) 413 { 414 USED(f); 415 } 416 417 static void 418 iremove(Xfile *f) 419 { 420 USED(f); 421 error(Eperm); 422 } 423 424 static void 425 istat(Xfile *f, Dir *d) 426 { 427 Isofile *ip = f->ptr; 428 429 rzdir(f->xf, d, ip->fmt, &ip->d); 430 d->qid.vers = f->qid.vers; 431 if(d->qid.path==f->xf->rootqid.path){ 432 d->qid.path = 0; 433 d->qid.type = QTDIR; 434 } 435 } 436 437 static void 438 iwstat(Xfile *f, Dir *d) 439 { 440 USED(f, d); 441 error(Eperm); 442 } 443 444 static int 445 showdrec(int fd, int fmt, void *x) 446 { 447 Drec *d = (Drec *)x; 448 int namelen; 449 int syslen; 450 451 if(d->reclen == 0) 452 return 0; 453 fprint(fd, "%d %d %ld %ld ", 454 d->reclen, d->attrlen, l32(d->addr), l32(d->size)); 455 fprint(fd, "%s 0x%2.2x %d %d %ld ", 456 rdate(d->date, fmt), (fmt=='z' ? d->flags : d->r_flags), 457 d->unitsize, d->gapsize, l16(d->vseqno)); 458 fprint(fd, "%d %s", d->namelen, nstr(d->name, d->namelen)); 459 if(fmt != 'J'){ 460 namelen = d->namelen + (1-(d->namelen&1)); 461 syslen = d->reclen - 33 - namelen; 462 if(syslen != 0) 463 fprint(fd, " %s", nstr(&d->name[namelen], syslen)); 464 } 465 fprint(fd, "\n"); 466 return d->reclen + (d->reclen&1); 467 } 468 469 static void 470 newdrec(Xfile *f, Drec *dp) 471 { 472 Isofile *x = f->ptr; 473 Isofile *n; 474 int len; 475 476 len = sizeof(Isofile) - sizeof(Drec) + dp->reclen; 477 n = ealloc(len); 478 n->fmt = x->fmt; 479 n->blksize = x->blksize; 480 n->offset = 0; 481 n->doffset = 0; 482 memmove(&n->d, dp, dp->reclen); 483 free(x); 484 f->ptr = n; 485 f->len = len; 486 } 487 488 static void 489 ungetdrec(Xfile *f) 490 { 491 Isofile *ip = f->ptr; 492 493 if(ip->offset >= ip->odelta){ 494 ip->offset -= ip->odelta; 495 ip->odelta = 0; 496 } 497 } 498 499 static int 500 getdrec(Xfile *f, void *buf) 501 { 502 Isofile *ip = f->ptr; 503 int len = 0, boff = 0; 504 vlong addr; 505 uvlong size; 506 Iobuf *p = 0; 507 508 if(!ip) 509 return -1; 510 size = fakemax(l32(ip->d.size)); 511 while(ip->offset < size){ 512 addr = (l32(ip->d.addr)+ip->d.attrlen)*ip->blksize + ip->offset; 513 boff = addr % Sectorsize; 514 if(boff > Sectorsize-34){ 515 ip->offset += Sectorsize-boff; 516 continue; 517 } 518 p = getbuf(f->xf->d, addr/Sectorsize); 519 len = p->iobuf[boff]; 520 if(len >= 34) 521 break; 522 putbuf(p); 523 p = 0; 524 ip->offset += Sectorsize-boff; 525 } 526 if(p) { 527 memmove(buf, &p->iobuf[boff], len); 528 putbuf(p); 529 ip->odelta = len + (len&1); 530 ip->offset += ip->odelta; 531 return 0; 532 } 533 return -1; 534 } 535 536 static int 537 opendotdot(Xfile *f, Xfile *pf) 538 { 539 uchar dbuf[256]; 540 Drec *d = (Drec *)dbuf; 541 Isofile *ip = f->ptr, *pip = pf->ptr; 542 543 ip->offset = 0; 544 if(getdrec(f, d) < 0){ 545 chat("opendotdot: getdrec(.) failed..."); 546 return -1; 547 } 548 if(d->namelen != 1 || d->name[0] != 0){ 549 chat("opendotdot: no . entry..."); 550 return -1; 551 } 552 if(l32(d->addr) != l32(ip->d.addr)){ 553 chat("opendotdot: bad . address..."); 554 return -1; 555 } 556 if(getdrec(f, d) < 0){ 557 chat("opendotdot: getdrec(..) failed..."); 558 return -1; 559 } 560 if(d->namelen != 1 || d->name[0] != 1){ 561 chat("opendotdot: no .. entry..."); 562 return -1; 563 } 564 565 pf->xf = f->xf; 566 pip->fmt = ip->fmt; 567 pip->blksize = ip->blksize; 568 pip->offset = 0; 569 pip->doffset = 0; 570 pip->d = *d; 571 return 0; 572 } 573 574 enum { 575 Hname = 1, 576 Hmode = 2, 577 }; 578 579 static int 580 rzdir(Xfs *fs, Dir *d, int fmt, Drec *dp) 581 { 582 int n, flags, i, j, lj, nl, vers, sysl, mode, l, have; 583 uchar *s; 584 char *p; 585 char buf[Maxname+UTFmax+1]; 586 uchar *q; 587 Rune r; 588 enum { ONAMELEN = 28 }; /* old Plan 9 directory name length */ 589 590 have = 0; 591 flags = 0; 592 vers = -1; 593 d->qid.path = l32(dp->addr); 594 d->qid.type = 0; 595 d->qid.vers = 0; 596 n = dp->namelen; 597 memset(d->name, 0, Maxname); 598 if(n == 1) { 599 switch(dp->name[0]){ 600 case 1: 601 d->name[1] = '.'; 602 /* fall through */ 603 case 0: 604 d->name[0] = '.'; 605 have = Hname; 606 break; 607 default: 608 d->name[0] = tolower(dp->name[0]); 609 } 610 } else { 611 if(fmt == 'J'){ /* Joliet, 16-bit Unicode */ 612 q = (uchar*)dp->name; 613 for(i=j=lj=0; i<n && j<Maxname; i+=2){ 614 lj = j; 615 r = (q[i]<<8)|q[i+1]; 616 j += runetochar(buf+j, &r); 617 } 618 if(j >= Maxname) 619 j = lj; 620 memmove(d->name, buf, j); 621 }else{ 622 if(n >= Maxname) 623 n = Maxname-1; 624 for(i=0; i<n; i++) 625 d->name[i] = tolower(dp->name[i]); 626 } 627 } 628 629 sysl = dp->reclen-(34+dp->namelen); 630 s = (uchar*)dp->name + dp->namelen; 631 if(((uintptr)s) & 1) { 632 s++; 633 sysl--; 634 } 635 if(fs->isplan9 && sysl > 0) { 636 /* 637 * get gid, uid, mode and possibly name 638 * from plan9 directory extension 639 */ 640 nl = *s; 641 if(nl >= ONAMELEN) 642 nl = ONAMELEN-1; 643 if(nl) { 644 memset(d->name, 0, ONAMELEN); 645 memmove(d->name, s+1, nl); 646 } 647 s += 1 + *s; 648 nl = *s; 649 if(nl >= ONAMELEN) 650 nl = ONAMELEN-1; 651 memset(d->uid, 0, ONAMELEN); 652 memmove(d->uid, s+1, nl); 653 s += 1 + *s; 654 nl = *s; 655 if(nl >= ONAMELEN) 656 nl = ONAMELEN-1; 657 memset(d->gid, 0, ONAMELEN); 658 memmove(d->gid, s+1, nl); 659 s += 1 + *s; 660 if(((uintptr)s) & 1) 661 s++; 662 d->mode = l32(s); 663 if(d->mode & DMDIR) 664 d->qid.type |= QTDIR; 665 } else { 666 d->mode = 0444; 667 switch(fmt) { 668 case 'z': 669 if(fs->isrock) 670 strcpy(d->gid, "ridge"); 671 else 672 strcpy(d->gid, "iso9660"); 673 flags = dp->flags; 674 break; 675 case 'r': 676 strcpy(d->gid, "sierra"); 677 flags = dp->r_flags; 678 break; 679 case 'J': 680 strcpy(d->gid, "joliet"); 681 flags = dp->flags; 682 break; 683 case '9': 684 strcpy(d->gid, "plan9"); 685 flags = dp->flags; 686 break; 687 } 688 if(flags & 0x02){ 689 d->qid.type |= QTDIR; 690 d->mode |= DMDIR|0111; 691 } 692 strcpy(d->uid, "cdrom"); 693 if(fmt!='9' && !(d->mode&DMDIR)){ 694 /* 695 * ISO 9660 actually requires that you always have a . and a ;, 696 * even if there is no version and no extension. Very few writers 697 * do this. If the version is present, we use it for qid.vers. 698 * If there is no extension but there is a dot, we strip it off. 699 * (VMS heads couldn't comprehend the dot as a file name character 700 * rather than as just a separator between name and extension.) 701 * 702 * We don't do this for directory names because directories are 703 * not allowed to have extensions and versions. 704 */ 705 if((p=strchr(d->name, ';')) != nil){ 706 vers = strtoul(p+1, 0, 0); 707 d->qid.vers = vers; 708 *p = '\0'; 709 } 710 if((p=strchr(d->name, '.')) != nil && *(p+1)=='\0') 711 *p = '\0'; 712 } 713 if(fs->issusp){ 714 nl = 0; 715 s += fs->suspoff; 716 sysl -= fs->suspoff; 717 for(; sysl >= 4 && have != (Hname|Hmode); sysl -= l, s += l){ 718 if(s[0] == 0 && ((uintptr)s & 1)){ 719 /* MacOS pads individual entries, contrary to spec */ 720 s++; 721 sysl--; 722 } 723 l = s[2]; 724 if(s[0] == 'P' && s[1] == 'X' && s[3] == 1){ 725 /* posix file attributes */ 726 mode = l32(s+4); 727 d->mode = mode & 0777; 728 if((mode & 0170000) == 040000){ 729 d->mode |= DMDIR; 730 d->qid.type |= QTDIR; 731 } 732 have |= Hmode; 733 } else if(s[0] == 'N' && s[1] == 'M' && s[3] == 1){ 734 /* alternative name */ 735 if((s[4] & ~1) == 0){ 736 i = nl+l-5; 737 if(i >= Maxname) 738 i = Maxname-1; 739 if((i -= nl) > 0){ 740 memmove(d->name+nl, s+5, i); 741 nl += i; 742 } 743 if(s[4] == 0) 744 have |= Hname; 745 } 746 } else if(s[0] == 'C' && s[1] == 'E' && s[2] >= 28){ 747 sysl = getcontin(fs->d, s, &s); 748 continue; 749 } else if(s[0] == 'S' && s[1] == 'T') 750 break; 751 } 752 } 753 } 754 d->length = 0; 755 if((d->mode & DMDIR) == 0) 756 d->length = fakemax(l32(dp->size)); 757 d->type = 0; 758 d->dev = 0; 759 d->atime = gtime(dp->date); 760 d->mtime = d->atime; 761 return vers; 762 } 763 764 static int 765 getcontin(Xdata *dev, uchar *p, uchar **s) 766 { 767 long bn, off, len; 768 Iobuf *b; 769 770 bn = l32(p+4); 771 off = l32(p+12); 772 len = l32(p+20); 773 chat("getcontin %d...", bn); 774 b = getbuf(dev, bn); 775 if(b == 0){ 776 *s = 0; 777 return 0; 778 } 779 *s = b->iobuf+off; 780 putbuf(b); 781 return len; 782 } 783 784 static char * 785 nstr(uchar *p, int n) 786 { 787 static char buf[132]; 788 char *q = buf; 789 790 while(--n >= 0){ 791 if(*p == '\\') 792 *q++ = '\\'; 793 if(' ' <= *p && *p <= '~') 794 *q++ = *p++; 795 else 796 q += sprint(q, "\\%2.2ux", *p++); 797 } 798 *q = 0; 799 return buf; 800 } 801 802 static char * 803 rdate(uchar *p, int fmt) 804 { 805 static char buf[64]; 806 int htz, s, n; 807 808 n = sprint(buf, "%2.2d.%2.2d.%2.2d %2.2d:%2.2d:%2.2d", 809 p[0], p[1], p[2], p[3], p[4], p[5]); 810 if(fmt == 'z'){ 811 htz = p[6]; 812 if(htz >= 128){ 813 htz = 256-htz; 814 s = '-'; 815 }else 816 s = '+'; 817 sprint(&buf[n], " (%c%.1f)", s, (float)htz/2); 818 } 819 return buf; 820 } 821 822 static char 823 dmsize[12] = 824 { 825 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 826 }; 827 828 static int 829 dysize(int y) 830 { 831 832 if((y%4) == 0) 833 return 366; 834 return 365; 835 } 836 837 static long 838 gtime(uchar *p) /* yMdhmsz */ 839 { 840 long t; 841 int i, y, M, d, h, m, s, tz; 842 843 y=p[0]; M=p[1]; d=p[2]; 844 h=p[3]; m=p[4]; s=p[5]; tz=p[6]; 845 USED(tz); 846 y += 1900; 847 if (y < 1970) 848 return 0; 849 if (M < 1 || M > 12) 850 return 0; 851 if (d < 1 || d > dmsize[M-1]) 852 if (!(M == 2 && d == 29 && dysize(y) == 366)) 853 return 0; 854 if (h > 23) 855 return 0; 856 if (m > 59) 857 return 0; 858 if (s > 59) 859 return 0; 860 t = 0; 861 for(i=1970; i<y; i++) 862 t += dysize(i); 863 if (dysize(y)==366 && M >= 3) 864 t++; 865 while(--M) 866 t += dmsize[M-1]; 867 t += d-1; 868 t = 24*t + h; 869 t = 60*t + m; 870 t = 60*t + s; 871 return t; 872 } 873 874 #define p ((uchar*)arg) 875 876 static long 877 l16(void *arg) 878 { 879 long v; 880 881 v = ((long)p[1]<<8)|p[0]; 882 if (v >= 0x8000L) 883 v -= 0x10000L; 884 return v; 885 } 886 887 static long 888 l32(void *arg) 889 { 890 return ((((((long)p[3]<<8)|p[2])<<8)|p[1])<<8)|p[0]; 891 } 892 893 #undef p 894