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 if(isplan9) 246 return strcmp(a, b); 247 return cistrcmp(a, b); 248 } 249 250 static void 251 iwalk(Xfile *f, char *name) 252 { 253 Isofile *ip = f->ptr; 254 uchar dbuf[256]; 255 char nbuf[4*Maxname]; 256 Drec *d = (Drec*)dbuf; 257 Dir dir; 258 char *p; 259 int len, vers, dvers; 260 261 vers = -1; 262 if(p = strchr(name, ';')) { /* assign = */ 263 len = p-name; 264 if(len >= Maxname) 265 len = Maxname-1; 266 memmove(nbuf, name, len); 267 vers = strtoul(p+1, 0, 10); 268 name = nbuf; 269 } 270 /* 271 len = strlen(name); 272 if(len >= Maxname){ 273 len = Maxname-1; 274 if(name != nbuf){ 275 memmove(nbuf, name, len); 276 name = nbuf; 277 } 278 name[len] = 0; 279 } 280 */ 281 282 chat("%d \"%s\"...", strlen(name), name); 283 ip->offset = 0; 284 setnames(&dir, nbuf); 285 while(getdrec(f, d) >= 0) { 286 dvers = rzdir(f->xf, &dir, ip->fmt, d); 287 if(casestrcmp(f->xf->isplan9||f->xf->isrock, name, dir.name) != 0) 288 continue; 289 newdrec(f, d); 290 f->qid.path = dir.qid.path; 291 f->qid.type = dir.qid.type; 292 USED(dvers); 293 return; 294 } 295 USED(vers); 296 error(Enonexist); 297 } 298 299 static void 300 iopen(Xfile *f, int mode) 301 { 302 mode &= ~OCEXEC; 303 if(mode != OREAD && mode != OEXEC) 304 error(Eperm); 305 f->ptr->offset = 0; 306 f->ptr->doffset = 0; 307 } 308 309 static void 310 icreate(Xfile *f, char *name, long perm, int mode) 311 { 312 USED(f, name, perm, mode); 313 error(Eperm); 314 } 315 316 static long 317 ireaddir(Xfile *f, uchar *buf, long offset, long count) 318 { 319 Isofile *ip = f->ptr; 320 Dir d; 321 char names[4*Maxname]; 322 uchar dbuf[256]; 323 Drec *drec = (Drec *)dbuf; 324 int n, rcnt; 325 326 if(offset==0){ 327 ip->offset = 0; 328 ip->doffset = 0; 329 }else if(offset != ip->doffset) 330 error("seek in directory not allowed"); 331 332 rcnt = 0; 333 setnames(&d, names); 334 while(rcnt < count && getdrec(f, drec) >= 0){ 335 if(drec->namelen == 1){ 336 if(drec->name[0] == 0) 337 continue; 338 if(drec->name[0] == 1) 339 continue; 340 } 341 rzdir(f->xf, &d, ip->fmt, drec); 342 d.qid.vers = f->qid.vers; 343 if((n = convD2M(&d, buf+rcnt, count-rcnt)) <= BIT16SZ){ 344 ungetdrec(f); 345 break; 346 } 347 rcnt += n; 348 } 349 ip->doffset += rcnt; 350 return rcnt; 351 } 352 353 static long 354 iread(Xfile *f, char *buf, vlong offset, long count) 355 { 356 int n, o, rcnt = 0; 357 vlong size, addr; 358 Isofile *ip = f->ptr; 359 Iobuf *p; 360 361 size = fakemax(l32(ip->d.size)); 362 if(offset >= size) 363 return 0; 364 if(offset+count > size) 365 count = size - offset; 366 addr = ((vlong)l32(ip->d.addr) + ip->d.attrlen)*ip->blksize + offset; 367 o = addr % Sectorsize; 368 addr /= Sectorsize; 369 /*chat("d.addr=%ld, addr=%lld, o=%d...", l32(ip->d.addr), addr, o);*/ 370 n = Sectorsize - o; 371 372 while(count > 0){ 373 if(n > count) 374 n = count; 375 p = getbuf(f->xf->d, addr); 376 memmove(&buf[rcnt], &p->iobuf[o], n); 377 putbuf(p); 378 count -= n; 379 rcnt += n; 380 ++addr; 381 o = 0; 382 n = Sectorsize; 383 } 384 return rcnt; 385 } 386 387 static long 388 iwrite(Xfile *f, char *buf, vlong offset, long count) 389 { 390 USED(f, buf, offset, count); 391 error(Eperm); 392 return 0; 393 } 394 395 static void 396 iclunk(Xfile *f) 397 { 398 USED(f); 399 } 400 401 static void 402 iremove(Xfile *f) 403 { 404 USED(f); 405 error(Eperm); 406 } 407 408 static void 409 istat(Xfile *f, Dir *d) 410 { 411 Isofile *ip = f->ptr; 412 413 rzdir(f->xf, d, ip->fmt, &ip->d); 414 d->qid.vers = f->qid.vers; 415 if(d->qid.path==f->xf->rootqid.path){ 416 d->qid.path = 0; 417 d->qid.type = QTDIR; 418 } 419 } 420 421 static void 422 iwstat(Xfile *f, Dir *d) 423 { 424 USED(f, d); 425 error(Eperm); 426 } 427 428 static int 429 showdrec(int fd, int fmt, void *x) 430 { 431 Drec *d = (Drec *)x; 432 int namelen; 433 int syslen; 434 435 if(d->reclen == 0) 436 return 0; 437 fprint(fd, "%d %d %ld %ld ", 438 d->reclen, d->attrlen, l32(d->addr), l32(d->size)); 439 fprint(fd, "%s 0x%2.2x %d %d %ld ", 440 rdate(d->date, fmt), (fmt=='z' ? d->flags : d->r_flags), 441 d->unitsize, d->gapsize, l16(d->vseqno)); 442 fprint(fd, "%d %s", d->namelen, nstr(d->name, d->namelen)); 443 if(fmt != 'J'){ 444 namelen = d->namelen + (1-(d->namelen&1)); 445 syslen = d->reclen - 33 - namelen; 446 if(syslen != 0) 447 fprint(fd, " %s", nstr(&d->name[namelen], syslen)); 448 } 449 fprint(fd, "\n"); 450 return d->reclen + (d->reclen&1); 451 } 452 453 static void 454 newdrec(Xfile *f, Drec *dp) 455 { 456 Isofile *x = f->ptr; 457 Isofile *n; 458 int len; 459 460 len = sizeof(Isofile) - sizeof(Drec) + dp->reclen; 461 n = ealloc(len); 462 n->fmt = x->fmt; 463 n->blksize = x->blksize; 464 n->offset = 0; 465 n->doffset = 0; 466 memmove(&n->d, dp, dp->reclen); 467 free(x); 468 f->ptr = n; 469 f->len = len; 470 } 471 472 static void 473 ungetdrec(Xfile *f) 474 { 475 Isofile *ip = f->ptr; 476 477 if(ip->offset >= ip->odelta){ 478 ip->offset -= ip->odelta; 479 ip->odelta = 0; 480 } 481 } 482 483 static int 484 getdrec(Xfile *f, void *buf) 485 { 486 Isofile *ip = f->ptr; 487 int len = 0, boff = 0; 488 vlong addr; 489 uvlong size; 490 Iobuf *p = 0; 491 492 if(!ip) 493 return -1; 494 size = fakemax(l32(ip->d.size)); 495 while(ip->offset < size){ 496 addr = (l32(ip->d.addr)+ip->d.attrlen)*ip->blksize + ip->offset; 497 boff = addr % Sectorsize; 498 if(boff > Sectorsize-34){ 499 ip->offset += Sectorsize-boff; 500 continue; 501 } 502 p = getbuf(f->xf->d, addr/Sectorsize); 503 len = p->iobuf[boff]; 504 if(len >= 34) 505 break; 506 putbuf(p); 507 p = 0; 508 ip->offset += Sectorsize-boff; 509 } 510 if(p) { 511 memmove(buf, &p->iobuf[boff], len); 512 putbuf(p); 513 ip->odelta = len + (len&1); 514 ip->offset += ip->odelta; 515 return 0; 516 } 517 return -1; 518 } 519 520 static int 521 opendotdot(Xfile *f, Xfile *pf) 522 { 523 uchar dbuf[256]; 524 Drec *d = (Drec *)dbuf; 525 Isofile *ip = f->ptr, *pip = pf->ptr; 526 527 ip->offset = 0; 528 if(getdrec(f, d) < 0){ 529 chat("opendotdot: getdrec(.) failed..."); 530 return -1; 531 } 532 if(d->namelen != 1 || d->name[0] != 0){ 533 chat("opendotdot: no . entry..."); 534 return -1; 535 } 536 if(l32(d->addr) != l32(ip->d.addr)){ 537 chat("opendotdot: bad . address..."); 538 return -1; 539 } 540 if(getdrec(f, d) < 0){ 541 chat("opendotdot: getdrec(..) failed..."); 542 return -1; 543 } 544 if(d->namelen != 1 || d->name[0] != 1){ 545 chat("opendotdot: no .. entry..."); 546 return -1; 547 } 548 549 pf->xf = f->xf; 550 pip->fmt = ip->fmt; 551 pip->blksize = ip->blksize; 552 pip->offset = 0; 553 pip->doffset = 0; 554 pip->d = *d; 555 return 0; 556 } 557 558 enum { 559 Hname = 1, 560 Hmode = 2, 561 }; 562 563 static int 564 rzdir(Xfs *fs, Dir *d, int fmt, Drec *dp) 565 { 566 int n, flags, i, j, lj, nl, vers, sysl, mode, l, have; 567 uchar *s; 568 char *p; 569 char buf[Maxname+UTFmax+1]; 570 uchar *q; 571 Rune r; 572 enum { ONAMELEN = 28 }; /* old Plan 9 directory name length */ 573 574 have = 0; 575 flags = 0; 576 vers = -1; 577 d->qid.path = l32(dp->addr); 578 d->qid.type = 0; 579 d->qid.vers = 0; 580 n = dp->namelen; 581 memset(d->name, 0, Maxname); 582 if(n == 1) { 583 switch(dp->name[0]){ 584 case 1: 585 d->name[1] = '.'; 586 /* fall through */ 587 case 0: 588 d->name[0] = '.'; 589 have = Hname; 590 break; 591 default: 592 d->name[0] = tolower(dp->name[0]); 593 } 594 } else { 595 if(fmt == 'J'){ /* Joliet, 16-bit Unicode */ 596 q = (uchar*)dp->name; 597 for(i=j=lj=0; i<n && j<Maxname; i+=2){ 598 lj = j; 599 r = (q[i]<<8)|q[i+1]; 600 j += runetochar(buf+j, &r); 601 } 602 if(j >= Maxname) 603 j = lj; 604 memmove(d->name, buf, j); 605 }else{ 606 if(n >= Maxname) 607 n = Maxname-1; 608 for(i=0; i<n; i++) 609 d->name[i] = tolower(dp->name[i]); 610 } 611 } 612 613 sysl = dp->reclen-(34+dp->namelen); 614 s = (uchar*)dp->name + dp->namelen; 615 if(((uintptr)s) & 1) { 616 s++; 617 sysl--; 618 } 619 if(fs->isplan9 && sysl > 0) { 620 /* 621 * get gid, uid, mode and possibly name 622 * from plan9 directory extension 623 */ 624 nl = *s; 625 if(nl >= ONAMELEN) 626 nl = ONAMELEN-1; 627 if(nl) { 628 memset(d->name, 0, ONAMELEN); 629 memmove(d->name, s+1, nl); 630 } 631 s += 1 + *s; 632 nl = *s; 633 if(nl >= ONAMELEN) 634 nl = ONAMELEN-1; 635 memset(d->uid, 0, ONAMELEN); 636 memmove(d->uid, s+1, nl); 637 s += 1 + *s; 638 nl = *s; 639 if(nl >= ONAMELEN) 640 nl = ONAMELEN-1; 641 memset(d->gid, 0, ONAMELEN); 642 memmove(d->gid, s+1, nl); 643 s += 1 + *s; 644 if(((uintptr)s) & 1) 645 s++; 646 d->mode = l32(s); 647 if(d->mode & DMDIR) 648 d->qid.type |= QTDIR; 649 } else { 650 d->mode = 0444; 651 switch(fmt) { 652 case 'z': 653 if(fs->isrock) 654 strcpy(d->gid, "ridge"); 655 else 656 strcpy(d->gid, "iso9660"); 657 flags = dp->flags; 658 break; 659 case 'r': 660 strcpy(d->gid, "sierra"); 661 flags = dp->r_flags; 662 break; 663 case 'J': 664 strcpy(d->gid, "joliet"); 665 flags = dp->flags; 666 break; 667 case '9': 668 strcpy(d->gid, "plan9"); 669 flags = dp->flags; 670 break; 671 } 672 if(flags & 0x02){ 673 d->qid.type |= QTDIR; 674 d->mode |= DMDIR|0111; 675 } 676 strcpy(d->uid, "cdrom"); 677 if(fmt!='9' && !(d->mode&DMDIR)){ 678 /* 679 * ISO 9660 actually requires that you always have a . and a ;, 680 * even if there is no version and no extension. Very few writers 681 * do this. If the version is present, we use it for qid.vers. 682 * If there is no extension but there is a dot, we strip it off. 683 * (VMS heads couldn't comprehend the dot as a file name character 684 * rather than as just a separator between name and extension.) 685 * 686 * We don't do this for directory names because directories are 687 * not allowed to have extensions and versions. 688 */ 689 if((p=strchr(d->name, ';')) != nil){ 690 vers = strtoul(p+1, 0, 0); 691 d->qid.vers = vers; 692 *p = '\0'; 693 } 694 if((p=strchr(d->name, '.')) != nil && *(p+1)=='\0') 695 *p = '\0'; 696 } 697 if(fs->issusp){ 698 nl = 0; 699 s += fs->suspoff; 700 sysl -= fs->suspoff; 701 for(; sysl >= 4 && have != (Hname|Hmode); sysl -= l, s += l){ 702 if(s[0] == 0 && ((uintptr)s & 1)){ 703 /* MacOS pads individual entries, contrary to spec */ 704 s++; 705 sysl--; 706 } 707 l = s[2]; 708 if(s[0] == 'P' && s[1] == 'X' && s[3] == 1){ 709 /* posix file attributes */ 710 mode = l32(s+4); 711 d->mode = mode & 0777; 712 if((mode & 0170000) == 040000){ 713 d->mode |= DMDIR; 714 d->qid.type |= QTDIR; 715 } 716 have |= Hmode; 717 } else if(s[0] == 'N' && s[1] == 'M' && s[3] == 1){ 718 /* alternative name */ 719 if((s[4] & ~1) == 0){ 720 i = nl+l-5; 721 if(i >= Maxname) 722 i = Maxname-1; 723 if((i -= nl) > 0){ 724 memmove(d->name+nl, s+5, i); 725 nl += i; 726 } 727 if(s[4] == 0) 728 have |= Hname; 729 } 730 } else if(s[0] == 'C' && s[1] == 'E' && s[2] >= 28){ 731 sysl = getcontin(fs->d, s, &s); 732 continue; 733 } else if(s[0] == 'S' && s[1] == 'T') 734 break; 735 } 736 } 737 } 738 d->length = 0; 739 if((d->mode & DMDIR) == 0) 740 d->length = fakemax(l32(dp->size)); 741 d->type = 0; 742 d->dev = 0; 743 d->atime = gtime(dp->date); 744 d->mtime = d->atime; 745 return vers; 746 } 747 748 static int 749 getcontin(Xdata *dev, uchar *p, uchar **s) 750 { 751 long bn, off, len; 752 Iobuf *b; 753 754 bn = l32(p+4); 755 off = l32(p+12); 756 len = l32(p+20); 757 chat("getcontin %d...", bn); 758 b = getbuf(dev, bn); 759 if(b == 0){ 760 *s = 0; 761 return 0; 762 } 763 *s = b->iobuf+off; 764 putbuf(b); 765 return len; 766 } 767 768 static char * 769 nstr(uchar *p, int n) 770 { 771 static char buf[132]; 772 char *q = buf; 773 774 while(--n >= 0){ 775 if(*p == '\\') 776 *q++ = '\\'; 777 if(' ' <= *p && *p <= '~') 778 *q++ = *p++; 779 else 780 q += sprint(q, "\\%2.2ux", *p++); 781 } 782 *q = 0; 783 return buf; 784 } 785 786 static char * 787 rdate(uchar *p, int fmt) 788 { 789 static char buf[64]; 790 int htz, s, n; 791 792 n = sprint(buf, "%2.2d.%2.2d.%2.2d %2.2d:%2.2d:%2.2d", 793 p[0], p[1], p[2], p[3], p[4], p[5]); 794 if(fmt == 'z'){ 795 htz = p[6]; 796 if(htz >= 128){ 797 htz = 256-htz; 798 s = '-'; 799 }else 800 s = '+'; 801 sprint(&buf[n], " (%c%.1f)", s, (float)htz/2); 802 } 803 return buf; 804 } 805 806 static char 807 dmsize[12] = 808 { 809 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 810 }; 811 812 static int 813 dysize(int y) 814 { 815 816 if((y%4) == 0) 817 return 366; 818 return 365; 819 } 820 821 static long 822 gtime(uchar *p) /* yMdhmsz */ 823 { 824 long t; 825 int i, y, M, d, h, m, s, tz; 826 827 y=p[0]; M=p[1]; d=p[2]; 828 h=p[3]; m=p[4]; s=p[5]; tz=p[6]; 829 USED(tz); 830 y += 1900; 831 if (y < 1970) 832 return 0; 833 if (M < 1 || M > 12) 834 return 0; 835 if (d < 1 || d > dmsize[M-1]) 836 if (!(M == 2 && d == 29 && dysize(y) == 366)) 837 return 0; 838 if (h > 23) 839 return 0; 840 if (m > 59) 841 return 0; 842 if (s > 59) 843 return 0; 844 t = 0; 845 for(i=1970; i<y; i++) 846 t += dysize(i); 847 if (dysize(y)==366 && M >= 3) 848 t++; 849 while(--M) 850 t += dmsize[M-1]; 851 t += d-1; 852 t = 24*t + h; 853 t = 60*t + m; 854 t = 60*t + s; 855 return t; 856 } 857 858 #define p ((uchar*)arg) 859 860 static long 861 l16(void *arg) 862 { 863 long v; 864 865 v = ((long)p[1]<<8)|p[0]; 866 if (v >= 0x8000L) 867 v -= 0x10000L; 868 return v; 869 } 870 871 static long 872 l32(void *arg) 873 { 874 return ((((((long)p[3]<<8)|p[2])<<8)|p[1])<<8)|p[0]; 875 } 876 877 #undef p 878