1 /* 2 * ar - portable (ascii) format version 3 */ 4 #include <u.h> 5 #include <libc.h> 6 #include <bio.h> 7 #include <mach.h> 8 #include <ar.h> 9 10 /* 11 * The algorithm uses up to 3 temp files. The "pivot member" is the 12 * archive member specified by and a, b, or i option. The temp files are 13 * astart - contains existing members up to and including the pivot member. 14 * amiddle - contains new files moved or inserted behind the pivot. 15 * aend - contains the existing members that follow the pivot member. 16 * When all members have been processed, function 'install' streams the 17 * temp files, in order, back into the archive. 18 */ 19 20 typedef struct Arsymref 21 { 22 char *name; 23 int type; 24 int len; 25 vlong offset; 26 struct Arsymref *next; 27 } Arsymref; 28 29 typedef struct Armember /* Temp file entry - one per archive member */ 30 { 31 struct Armember *next; 32 struct ar_hdr hdr; 33 long size; 34 long date; 35 void *member; 36 } Armember; 37 38 typedef struct Arfile /* Temp file control block - one per tempfile */ 39 { 40 int paged; /* set when some data paged to disk */ 41 char *fname; /* paging file name */ 42 int fd; /* paging file descriptor */ 43 vlong size; 44 Armember *head; /* head of member chain */ 45 Armember *tail; /* tail of member chain */ 46 Arsymref *sym; /* head of defined symbol chain */ 47 } Arfile; 48 49 typedef struct Hashchain 50 { 51 char *name; 52 struct Hashchain *next; 53 } Hashchain; 54 55 #define NHASH 1024 56 57 /* 58 * macro to portably read/write archive header. 59 * 'cmd' is read/write/Bread/Bwrite, etc. 60 */ 61 #define HEADER_IO(cmd, f, h) cmd(f, h.name, sizeof(h.name)) != sizeof(h.name)\ 62 || cmd(f, h.date, sizeof(h.date)) != sizeof(h.date)\ 63 || cmd(f, h.uid, sizeof(h.uid)) != sizeof(h.uid)\ 64 || cmd(f, h.gid, sizeof(h.gid)) != sizeof(h.gid)\ 65 || cmd(f, h.mode, sizeof(h.mode)) != sizeof(h.mode)\ 66 || cmd(f, h.size, sizeof(h.size)) != sizeof(h.size)\ 67 || cmd(f, h.fmag, sizeof(h.fmag)) != sizeof(h.fmag) 68 69 /* constants and flags */ 70 char *man = "mrxtdpq"; 71 char *opt = "uvnbailo"; 72 char artemp[] = "/tmp/vXXXXX"; 73 char movtemp[] = "/tmp/v1XXXXX"; 74 char tailtemp[] = "/tmp/v2XXXXX"; 75 char symdef[] = "__.SYMDEF"; 76 77 int aflag; /* command line flags */ 78 int bflag; 79 int cflag; 80 int oflag; 81 int uflag; 82 int vflag; 83 84 Arfile *astart, *amiddle, *aend; /* Temp file control block pointers */ 85 int allobj = 1; /* set when all members are object files of the same type */ 86 int symdefsize; /* size of symdef file */ 87 int dupfound; /* flag for duplicate symbol */ 88 Hashchain *hash[NHASH]; /* hash table of text symbols */ 89 90 #define ARNAMESIZE sizeof(astart->tail->hdr.name) 91 92 char poname[ARNAMESIZE+1]; /* name of pivot member */ 93 char *file; /* current file or member being worked on */ 94 Biobuf bout; 95 Biobuf bar; 96 97 void arcopy(Biobuf*, Arfile*, Armember*); 98 int arcreate(char*); 99 void arfree(Arfile*); 100 void arinsert(Arfile*, Armember*); 101 char *armalloc(int); 102 void armove(Biobuf*, Arfile*, Armember*); 103 void arread(Biobuf*, Armember*, int); 104 void arstream(int, Arfile*); 105 int arwrite(int, Armember*); 106 int bamatch(char*, char*); 107 int duplicate(char*); 108 Armember *getdir(Biobuf*); 109 int getspace(void); 110 void install(char*, Arfile*, Arfile*, Arfile*, int); 111 void longt(Armember*); 112 int match(int, char**); 113 void mesg(int, char*); 114 Arfile *newtempfile(char*); 115 Armember *newmember(void); 116 void objsym(Sym*, void*); 117 int openar(char*, int, int); 118 int page(Arfile*); 119 void pmode(long); 120 void rl(int); 121 void scanobj(Biobuf*, Arfile*, long); 122 void select(int*, long); 123 void setcom(void(*)(char*, int, char**)); 124 void skip(Biobuf*, vlong); 125 int symcomp(void*, void*); 126 void trim(char*, char*, int); 127 void usage(void); 128 void wrerr(void); 129 void wrsym(Biobuf*, long, Arsymref*); 130 131 void rcmd(char*, int, char**); /* command processing */ 132 void dcmd(char*, int, char**); 133 void xcmd(char*, int, char**); 134 void tcmd(char*, int, char**); 135 void pcmd(char*, int, char**); 136 void mcmd(char*, int, char**); 137 void qcmd(char*, int, char**); 138 void (*comfun)(char*, int, char**); 139 140 void 141 main(int argc, char *argv[]) 142 { 143 char *cp; 144 145 Binit(&bout, 1, OWRITE); 146 if(argc < 3) 147 usage(); 148 for (cp = argv[1]; *cp; cp++) { 149 switch(*cp) { 150 case 'a': aflag = 1; break; 151 case 'b': bflag = 1; break; 152 case 'c': cflag = 1; break; 153 case 'd': setcom(dcmd); break; 154 case 'i': bflag = 1; break; 155 case 'l': 156 strcpy(artemp, "vXXXXX"); 157 strcpy(movtemp, "v1XXXXX"); 158 strcpy(tailtemp, "v2XXXXX"); 159 break; 160 case 'm': setcom(mcmd); break; 161 case 'o': oflag = 1; break; 162 case 'p': setcom(pcmd); break; 163 case 'q': setcom(qcmd); break; 164 case 'r': setcom(rcmd); break; 165 case 't': setcom(tcmd); break; 166 case 'u': uflag = 1; break; 167 case 'v': vflag = 1; break; 168 case 'x': setcom(xcmd); break; 169 default: 170 fprint(2, "ar: bad option `%c'\n", *cp); 171 exits("error"); 172 } 173 } 174 if (aflag && bflag) { 175 fprint(2, "ar: only one of 'a' and 'b' can be specified\n"); 176 usage(); 177 } 178 if(aflag || bflag) { 179 trim(argv[2], poname, sizeof(poname)); 180 argv++; 181 argc--; 182 if(argc < 3) 183 usage(); 184 } 185 if(comfun == 0) { 186 if(uflag == 0) { 187 fprint(2, "ar: one of [%s] must be specified\n", man); 188 usage(); 189 } 190 setcom(rcmd); 191 } 192 cp = argv[2]; 193 argc -= 3; 194 argv += 3; 195 (*comfun)(cp, argc, argv); /* do the command */ 196 cp = 0; 197 while (argc--) { 198 if (*argv) { 199 fprint(2, "ar: %s not found\n", *argv); 200 cp = "error"; 201 } 202 argv++; 203 } 204 exits(cp); 205 } 206 /* 207 * select a command 208 */ 209 void 210 setcom(void (*fun)(char *, int, char**)) 211 { 212 213 if(comfun != 0) { 214 fprint(2, "ar: only one of [%s] allowed\n", man); 215 usage(); 216 } 217 comfun = fun; 218 } 219 /* 220 * perform the 'r' and 'u' commands 221 */ 222 void 223 rcmd(char *arname, int count, char **files) 224 { 225 int fd; 226 int i; 227 Arfile *ap; 228 Armember *bp; 229 Dir *d; 230 Biobuf *bfile; 231 232 fd = openar(arname, ORDWR, 1); 233 if (fd >= 0) { 234 Binit(&bar, fd, OREAD); 235 Bseek(&bar,seek(fd,0,1), 1); 236 } 237 astart = newtempfile(artemp); 238 ap = astart; 239 aend = 0; 240 for(i = 0; fd >= 0; i++) { 241 bp = getdir(&bar); 242 if (!bp) 243 break; 244 if (bamatch(file, poname)) { /* check for pivot */ 245 aend = newtempfile(tailtemp); 246 ap = aend; 247 } 248 /* pitch symdef file */ 249 if (i == 0 && strcmp(file, symdef) == 0) { 250 skip(&bar, bp->size); 251 continue; 252 } 253 if (count && !match(count, files)) { 254 scanobj(&bar, ap, bp->size); 255 arcopy(&bar, ap, bp); 256 continue; 257 } 258 bfile = Bopen(file, OREAD); 259 if (!bfile) { 260 if (count != 0) 261 fprint(2, "ar: cannot open %s\n", file); 262 scanobj(&bar, ap, bp->size); 263 arcopy(&bar, ap, bp); 264 continue; 265 } 266 d = dirfstat(Bfildes(bfile)); 267 if(d == nil) 268 fprint(2, "ar: cannot stat %s: %r\n", file); 269 if (uflag && (d==nil || d->mtime <= bp->date)) { 270 scanobj(&bar, ap, bp->size); 271 arcopy(&bar, ap, bp); 272 Bterm(bfile); 273 free(d); 274 continue; 275 } 276 mesg('r', file); 277 skip(&bar, bp->size); 278 scanobj(bfile, ap, d->length); 279 free(d); 280 armove(bfile, ap, bp); 281 Bterm(bfile); 282 } 283 if(fd >= 0) 284 close(fd); 285 /* copy in remaining files named on command line */ 286 for (i = 0; i < count; i++) { 287 file = files[i]; 288 if(file == 0) 289 continue; 290 files[i] = 0; 291 bfile = Bopen(file, OREAD); 292 if (!bfile) 293 fprint(2, "ar: %s cannot open\n", file); 294 else { 295 mesg('a', file); 296 d = dirfstat(Bfildes(bfile)); 297 if (d == nil) 298 fprint(2, "can't stat %s\n", file); 299 else { 300 scanobj(bfile, astart, d->length); 301 armove(bfile, astart, newmember()); 302 free(d); 303 } 304 Bterm(bfile); 305 } 306 } 307 if(fd < 0 && !cflag) 308 install(arname, astart, 0, aend, 1); /* issue 'creating' msg */ 309 else 310 install(arname, astart, 0, aend, 0); 311 } 312 313 void 314 dcmd(char *arname, int count, char **files) 315 { 316 Armember *bp; 317 int fd, i; 318 319 if (!count) 320 return; 321 fd = openar(arname, ORDWR, 0); 322 Binit(&bar, fd, OREAD); 323 Bseek(&bar,seek(fd,0,1), 1); 324 astart = newtempfile(artemp); 325 for (i = 0; bp = getdir(&bar); i++) { 326 if(match(count, files)) { 327 mesg('d', file); 328 skip(&bar, bp->size); 329 if (strcmp(file, symdef) == 0) 330 allobj = 0; 331 } else if (i == 0 && strcmp(file, symdef) == 0) 332 skip(&bar, bp->size); 333 else { 334 scanobj(&bar, astart, bp->size); 335 arcopy(&bar, astart, bp); 336 } 337 } 338 close(fd); 339 install(arname, astart, 0, 0, 0); 340 } 341 342 void 343 xcmd(char *arname, int count, char **files) 344 { 345 int fd, f, mode, i; 346 Armember *bp; 347 Dir dx; 348 349 fd = openar(arname, OREAD, 0); 350 Binit(&bar, fd, OREAD); 351 Bseek(&bar,seek(fd,0,1), 1); 352 i = 0; 353 while (bp = getdir(&bar)) { 354 if(count == 0 || match(count, files)) { 355 mode = strtoul(bp->hdr.mode, 0, 8) & 0777; 356 f = create(file, OWRITE, mode); 357 if(f < 0) { 358 fprint(2, "ar: %s cannot create\n", file); 359 skip(&bar, bp->size); 360 } else { 361 mesg('x', file); 362 arcopy(&bar, 0, bp); 363 if (write(f, bp->member, bp->size) < 0) 364 wrerr(); 365 if(oflag) { 366 nulldir(&dx); 367 dx.atime = bp->date; 368 dx.mtime = bp->date; 369 if(dirwstat(file, &dx) < 0) 370 perror(file); 371 } 372 free(bp->member); 373 close(f); 374 } 375 free(bp); 376 if (count && ++i >= count) 377 break; 378 } else { 379 skip(&bar, bp->size); 380 free(bp); 381 } 382 } 383 close(fd); 384 } 385 void 386 pcmd(char *arname, int count, char **files) 387 { 388 int fd; 389 Armember *bp; 390 391 fd = openar(arname, OREAD, 0); 392 Binit(&bar, fd, OREAD); 393 Bseek(&bar,seek(fd,0,1), 1); 394 while(bp = getdir(&bar)) { 395 if(count == 0 || match(count, files)) { 396 if(vflag) 397 print("\n<%s>\n\n", file); 398 arcopy(&bar, 0, bp); 399 if (write(1, bp->member, bp->size) < 0) 400 wrerr(); 401 } else 402 skip(&bar, bp->size); 403 free(bp); 404 } 405 close(fd); 406 } 407 void 408 mcmd(char *arname, int count, char **files) 409 { 410 int fd, i; 411 Arfile *ap; 412 Armember *bp; 413 414 if (count == 0) 415 return; 416 fd = openar(arname, ORDWR, 0); 417 Binit(&bar, fd, OREAD); 418 Bseek(&bar,seek(fd,0,1), 1); 419 astart = newtempfile(artemp); 420 amiddle = newtempfile(movtemp); 421 aend = 0; 422 ap = astart; 423 for (i = 0; bp = getdir(&bar); i++) { 424 if (bamatch(file, poname)) { 425 aend = newtempfile(tailtemp); 426 ap = aend; 427 } 428 if(match(count, files)) { 429 mesg('m', file); 430 scanobj(&bar, amiddle, bp->size); 431 arcopy(&bar, amiddle, bp); 432 } else 433 /* 434 * pitch the symdef file if it is at the beginning 435 * of the archive and we aren't inserting in front 436 * of it (ap == astart). 437 */ 438 if (ap == astart && i == 0 && strcmp(file, symdef) == 0) 439 skip(&bar, bp->size); 440 else { 441 scanobj(&bar, ap, bp->size); 442 arcopy(&bar, ap, bp); 443 } 444 } 445 close(fd); 446 if (poname[0] && aend == 0) 447 fprint(2, "ar: %s not found - files moved to end.\n", poname); 448 install(arname, astart, amiddle, aend, 0); 449 } 450 void 451 tcmd(char *arname, int count, char **files) 452 { 453 int fd; 454 Armember *bp; 455 char name[ARNAMESIZE+1]; 456 457 fd = openar(arname, OREAD, 0); 458 Binit(&bar, fd, OREAD); 459 Bseek(&bar,seek(fd,0,1), 1); 460 while(bp = getdir(&bar)) { 461 if(count == 0 || match(count, files)) { 462 if(vflag) 463 longt(bp); 464 trim(file, name, ARNAMESIZE); 465 Bprint(&bout, "%s\n", name); 466 } 467 skip(&bar, bp->size); 468 free(bp); 469 } 470 close(fd); 471 } 472 void 473 qcmd(char *arname, int count, char **files) 474 { 475 int fd, i; 476 Armember *bp; 477 Biobuf *bfile; 478 479 if(aflag || bflag) { 480 fprint(2, "ar: abi not allowed with q\n"); 481 exits("error"); 482 } 483 fd = openar(arname, ORDWR, 1); 484 if (fd < 0) { 485 if(!cflag) 486 fprint(2, "ar: creating %s\n", arname); 487 fd = arcreate(arname); 488 } 489 Binit(&bar, fd, OREAD); 490 Bseek(&bar,seek(fd,0,1), 1); 491 /* leave note group behind when writing archive; i.e. sidestep interrupts */ 492 rfork(RFNOTEG); 493 Bseek(&bar, 0, 2); 494 bp = newmember(); 495 for(i=0; i<count && files[i]; i++) { 496 file = files[i]; 497 files[i] = 0; 498 bfile = Bopen(file, OREAD); 499 if(!bfile) 500 fprint(2, "ar: %s cannot open\n", file); 501 else { 502 mesg('q', file); 503 armove(bfile, 0, bp); 504 if (!arwrite(fd, bp)) 505 wrerr(); 506 free(bp->member); 507 bp->member = 0; 508 Bterm(bfile); 509 } 510 } 511 free(bp); 512 close(fd); 513 } 514 515 /* 516 * extract the symbol references from an object file 517 */ 518 void 519 scanobj(Biobuf *b, Arfile *ap, long size) 520 { 521 int obj; 522 vlong offset; 523 Dir *d; 524 static int lastobj = -1; 525 526 if (!allobj) /* non-object file encountered */ 527 return; 528 offset = Boffset(b); 529 obj = objtype(b, 0); 530 if (obj < 0) { /* not an object file */ 531 allobj = 0; 532 d = dirfstat(Bfildes(b)); 533 if (d != nil && d->length == 0) 534 fprint(2, "ar: zero length file %s\n", file); 535 free(d); 536 Bseek(b, offset, 0); 537 return; 538 } 539 if (lastobj >= 0 && obj != lastobj) { 540 fprint(2, "ar: inconsistent object file %s\n", file); 541 allobj = 0; 542 Bseek(b, offset, 0); 543 return; 544 } 545 lastobj = obj; 546 if (!readar(b, obj, offset+size, 0)) { 547 fprint(2, "ar: invalid symbol reference in file %s\n", file); 548 allobj = 0; 549 Bseek(b, offset, 0); 550 return; 551 } 552 Bseek(b, offset, 0); 553 objtraverse(objsym, ap); 554 } 555 556 /* 557 * add text and data symbols to the symbol list 558 */ 559 void 560 objsym(Sym *s, void *p) 561 { 562 int n; 563 Arsymref *as; 564 Arfile *ap; 565 566 if (s->type != 'T' && s->type != 'D') 567 return; 568 ap = (Arfile*)p; 569 as = (Arsymref*)armalloc(sizeof(Arsymref)); 570 as->offset = ap->size; 571 n = strlen(s->name); 572 as->name = armalloc(n+1); 573 strcpy(as->name, s->name); 574 if(s->type == 'T' && duplicate(as->name)) { 575 dupfound = 1; 576 fprint(2, "duplicate text symbol: %s\n", as->name); 577 free(as->name); 578 free(as); 579 return; 580 } 581 as->type = s->type; 582 symdefsize += 4+(n+1)+1; 583 as->len = n; 584 as->next = ap->sym; 585 ap->sym = as; 586 } 587 588 /* 589 * Check the symbol table for duplicate text symbols 590 */ 591 int 592 duplicate(char *name) 593 { 594 Hashchain *p; 595 char *cp; 596 int h; 597 598 h = 0; 599 for(cp = name; *cp; h += *cp++) 600 h *= 1119; 601 if(h < 0) 602 h = ~h; 603 h %= NHASH; 604 605 for(p = hash[h]; p; p = p->next) 606 if(strcmp(p->name, name) == 0) 607 return 1; 608 p = (Hashchain*) armalloc(sizeof(Hashchain)); 609 p->next = hash[h]; 610 p->name = name; 611 hash[h] = p; 612 return 0; 613 } 614 615 /* 616 * open an archive and validate its header 617 */ 618 int 619 openar(char *arname, int mode, int errok) 620 { 621 int fd; 622 char mbuf[SARMAG]; 623 624 fd = open(arname, mode); 625 if(fd >= 0){ 626 if(read(fd, mbuf, SARMAG) != SARMAG || strncmp(mbuf, ARMAG, SARMAG)) { 627 fprint(2, "ar: %s not in archive format\n", arname); 628 exits("error"); 629 } 630 }else if(!errok){ 631 fprint(2, "ar: cannot open %s: %r\n", arname); 632 exits("error"); 633 } 634 return fd; 635 } 636 637 /* 638 * create an archive and set its header 639 */ 640 int 641 arcreate(char *arname) 642 { 643 int fd; 644 645 fd = create(arname, OWRITE, 0664); 646 if(fd < 0){ 647 fprint(2, "ar: cannot create %s: %r\n", arname); 648 exits("error"); 649 } 650 if(write(fd, ARMAG, SARMAG) != SARMAG) 651 wrerr(); 652 return fd; 653 } 654 655 /* 656 * error handling 657 */ 658 void 659 wrerr(void) 660 { 661 perror("ar: write error"); 662 exits("error"); 663 } 664 665 void 666 rderr(void) 667 { 668 perror("ar: read error"); 669 exits("error"); 670 } 671 672 void 673 phaseerr(int offset) 674 { 675 fprint(2, "ar: phase error at offset %d\n", offset); 676 exits("error"); 677 } 678 679 void 680 usage(void) 681 { 682 fprint(2, "usage: ar [%s][%s] archive files ...\n", opt, man); 683 exits("error"); 684 } 685 686 /* 687 * read the header for the next archive member 688 */ 689 Armember * 690 getdir(Biobuf *b) 691 { 692 Armember *bp; 693 char *cp; 694 static char name[ARNAMESIZE+1]; 695 696 bp = newmember(); 697 if(HEADER_IO(Bread, b, bp->hdr)) { 698 free(bp); 699 return 0; 700 } 701 if(strncmp(bp->hdr.fmag, ARFMAG, sizeof(bp->hdr.fmag))) 702 phaseerr(Boffset(b)); 703 strncpy(name, bp->hdr.name, sizeof(bp->hdr.name)); 704 cp = name+sizeof(name)-1; 705 while(*--cp==' ') 706 ; 707 cp[1] = '\0'; 708 file = name; 709 bp->date = strtol(bp->hdr.date, 0, 0); 710 bp->size = strtol(bp->hdr.size, 0, 0); 711 return bp; 712 } 713 714 /* 715 * Copy the file referenced by fd to the temp file 716 */ 717 void 718 armove(Biobuf *b, Arfile *ap, Armember *bp) 719 { 720 char *cp; 721 Dir *d; 722 723 d = dirfstat(Bfildes(b)); 724 if (d == nil) { 725 fprint(2, "ar: cannot stat %s\n", file); 726 return; 727 } 728 trim(file, bp->hdr.name, sizeof(bp->hdr.name)); 729 for (cp = strchr(bp->hdr.name, 0); /* blank pad on right */ 730 cp < bp->hdr.name+sizeof(bp->hdr.name); cp++) 731 *cp = ' '; 732 sprint(bp->hdr.date, "%-12ld", d->mtime); 733 sprint(bp->hdr.uid, "%-6d", 0); 734 sprint(bp->hdr.gid, "%-6d", 0); 735 sprint(bp->hdr.mode, "%-8lo", d->mode); 736 sprint(bp->hdr.size, "%-10lld", d->length); 737 strncpy(bp->hdr.fmag, ARFMAG, 2); 738 bp->size = d->length; 739 arread(b, bp, bp->size); 740 if (d->length&0x01) 741 d->length++; 742 if (ap) { 743 arinsert(ap, bp); 744 ap->size += d->length+SAR_HDR; 745 } 746 free(d); 747 } 748 749 /* 750 * Copy the archive member at the current offset into the temp file. 751 */ 752 void 753 arcopy(Biobuf *b, Arfile *ap, Armember *bp) 754 { 755 long n; 756 757 n = bp->size; 758 if (n & 01) 759 n++; 760 arread(b, bp, n); 761 if (ap) { 762 arinsert(ap, bp); 763 ap->size += n+SAR_HDR; 764 } 765 } 766 767 /* 768 * Skip an archive member 769 */ 770 void 771 skip(Biobuf *bp, vlong len) 772 { 773 if (len & 01) 774 len++; 775 Bseek(bp, len, 1); 776 } 777 778 /* 779 * Stream the three temp files to an archive 780 */ 781 void 782 install(char *arname, Arfile *astart, Arfile *amiddle, Arfile *aend, int createflag) 783 { 784 int fd; 785 786 if(allobj && dupfound) { 787 fprint(2, "%s not changed\n", arname); 788 return; 789 } 790 /* leave note group behind when copying back; i.e. sidestep interrupts */ 791 rfork(RFNOTEG); 792 793 if(createflag) 794 fprint(2, "ar: creating %s\n", arname); 795 fd = arcreate(arname); 796 797 if(allobj) 798 rl(fd); 799 800 if (astart) { 801 arstream(fd, astart); 802 arfree(astart); 803 } 804 if (amiddle) { 805 arstream(fd, amiddle); 806 arfree(amiddle); 807 } 808 if (aend) { 809 arstream(fd, aend); 810 arfree(aend); 811 } 812 close(fd); 813 } 814 815 void 816 rl(int fd) 817 { 818 819 Biobuf b; 820 char *cp; 821 struct ar_hdr a; 822 long len; 823 824 Binit(&b, fd, OWRITE); 825 Bseek(&b,seek(fd,0,1), 0); 826 827 len = symdefsize; 828 if(len&01) 829 len++; 830 sprint(a.date, "%-12ld", time(0)); 831 sprint(a.uid, "%-6d", 0); 832 sprint(a.gid, "%-6d", 0); 833 sprint(a.mode, "%-8lo", 0644L); 834 sprint(a.size, "%-10ld", len); 835 strncpy(a.fmag, ARFMAG, 2); 836 strcpy(a.name, symdef); 837 for (cp = strchr(a.name, 0); /* blank pad on right */ 838 cp < a.name+sizeof(a.name); cp++) 839 *cp = ' '; 840 if(HEADER_IO(Bwrite, &b, a)) 841 wrerr(); 842 843 len += Boffset(&b); 844 if (astart) { 845 wrsym(&b, len, astart->sym); 846 len += astart->size; 847 } 848 if(amiddle) { 849 wrsym(&b, len, amiddle->sym); 850 len += amiddle->size; 851 } 852 if(aend) 853 wrsym(&b, len, aend->sym); 854 855 if(symdefsize&0x01) 856 Bputc(&b, 0); 857 Bterm(&b); 858 } 859 860 /* 861 * Write the defined symbols to the symdef file 862 */ 863 void 864 wrsym(Biobuf *bp, long offset, Arsymref *as) 865 { 866 int off; 867 868 while(as) { 869 Bputc(bp, as->type); 870 off = as->offset+offset; 871 Bputc(bp, off); 872 Bputc(bp, off>>8); 873 Bputc(bp, off>>16); 874 Bputc(bp, off>>24); 875 if (Bwrite(bp, as->name, as->len+1) != as->len+1) 876 wrerr(); 877 as = as->next; 878 } 879 } 880 881 /* 882 * Check if the archive member matches an entry on the command line. 883 */ 884 int 885 match(int count, char **files) 886 { 887 int i; 888 char name[ARNAMESIZE+1]; 889 890 for(i=0; i<count; i++) { 891 if(files[i] == 0) 892 continue; 893 trim(files[i], name, ARNAMESIZE); 894 if(strncmp(name, file, ARNAMESIZE) == 0) { 895 file = files[i]; 896 files[i] = 0; 897 return 1; 898 } 899 } 900 return 0; 901 } 902 903 /* 904 * compare the current member to the name of the pivot member 905 */ 906 int 907 bamatch(char *file, char *pivot) 908 { 909 static int state = 0; 910 911 switch(state) 912 { 913 case 0: /* looking for position file */ 914 if (aflag) { 915 if (strncmp(file, pivot, ARNAMESIZE) == 0) 916 state = 1; 917 } else if (bflag) { 918 if (strncmp(file, pivot, ARNAMESIZE) == 0) { 919 state = 2; /* found */ 920 return 1; 921 } 922 } 923 break; 924 case 1: /* found - after previous file */ 925 state = 2; 926 return 1; 927 case 2: /* already found position file */ 928 break; 929 } 930 return 0; 931 } 932 933 /* 934 * output a message, if 'v' option was specified 935 */ 936 void 937 mesg(int c, char *file) 938 { 939 940 if(vflag) 941 Bprint(&bout, "%c - %s\n", c, file); 942 } 943 944 /* 945 * isolate file name by stripping leading directories and trailing slashes 946 */ 947 void 948 trim(char *s, char *buf, int n) 949 { 950 char *p; 951 952 for(;;) { 953 p = strrchr(s, '/'); 954 if (!p) { /* no slash in name */ 955 strncpy(buf, s, n); 956 return; 957 } 958 if (p[1] != 0) { /* p+1 is first char of file name */ 959 strncpy(buf, p+1, n); 960 return; 961 } 962 *p = 0; /* strip trailing slash */ 963 } 964 } 965 966 /* 967 * utilities for printing long form of 't' command 968 */ 969 #define SUID 04000 970 #define SGID 02000 971 #define ROWN 0400 972 #define WOWN 0200 973 #define XOWN 0100 974 #define RGRP 040 975 #define WGRP 020 976 #define XGRP 010 977 #define ROTH 04 978 #define WOTH 02 979 #define XOTH 01 980 #define STXT 01000 981 982 void 983 longt(Armember *bp) 984 { 985 char *cp; 986 987 pmode(strtoul(bp->hdr.mode, 0, 8)); 988 Bprint(&bout, "%3ld/%1ld", strtol(bp->hdr.uid, 0, 0), strtol(bp->hdr.gid, 0, 0)); 989 Bprint(&bout, "%7ld", bp->size); 990 cp = ctime(bp->date); 991 Bprint(&bout, " %-12.12s %-4.4s ", cp+4, cp+24); 992 } 993 994 int m1[] = { 1, ROWN, 'r', '-' }; 995 int m2[] = { 1, WOWN, 'w', '-' }; 996 int m3[] = { 2, SUID, 's', XOWN, 'x', '-' }; 997 int m4[] = { 1, RGRP, 'r', '-' }; 998 int m5[] = { 1, WGRP, 'w', '-' }; 999 int m6[] = { 2, SGID, 's', XGRP, 'x', '-' }; 1000 int m7[] = { 1, ROTH, 'r', '-' }; 1001 int m8[] = { 1, WOTH, 'w', '-' }; 1002 int m9[] = { 2, STXT, 't', XOTH, 'x', '-' }; 1003 1004 int *m[] = { m1, m2, m3, m4, m5, m6, m7, m8, m9}; 1005 1006 void 1007 pmode(long mode) 1008 { 1009 int **mp; 1010 1011 for(mp = &m[0]; mp < &m[9];) 1012 select(*mp++, mode); 1013 } 1014 1015 void 1016 select(int *ap, long mode) 1017 { 1018 int n; 1019 1020 n = *ap++; 1021 while(--n>=0 && (mode&*ap++)==0) 1022 ap++; 1023 Bputc(&bout, *ap); 1024 } 1025 1026 /* 1027 * Temp file I/O subsystem. We attempt to cache all three temp files in 1028 * core. When we run out of memory we spill to disk. 1029 * The I/O model assumes that temp files: 1030 * 1) are only written on the end 1031 * 2) are only read from the beginning 1032 * 3) are only read after all writing is complete. 1033 * The architecture uses one control block per temp file. Each control 1034 * block anchors a chain of buffers, each containing an archive member. 1035 */ 1036 Arfile * 1037 newtempfile(char *name) /* allocate a file control block */ 1038 { 1039 Arfile *ap; 1040 1041 ap = (Arfile *) armalloc(sizeof(Arfile)); 1042 ap->fname = name; 1043 return ap; 1044 } 1045 1046 Armember * 1047 newmember(void) /* allocate a member buffer */ 1048 { 1049 return (Armember *)armalloc(sizeof(Armember)); 1050 } 1051 1052 void 1053 arread(Biobuf *b, Armember *bp, int n) /* read an image into a member buffer */ 1054 { 1055 int i; 1056 1057 bp->member = armalloc(n); 1058 i = Bread(b, bp->member, n); 1059 if (i < 0) { 1060 free(bp->member); 1061 bp->member = 0; 1062 rderr(); 1063 } 1064 } 1065 1066 /* 1067 * insert a member buffer into the member chain 1068 */ 1069 void 1070 arinsert(Arfile *ap, Armember *bp) 1071 { 1072 bp->next = 0; 1073 if (!ap->tail) 1074 ap->head = bp; 1075 else 1076 ap->tail->next = bp; 1077 ap->tail = bp; 1078 } 1079 1080 /* 1081 * stream the members in a temp file to the file referenced by 'fd'. 1082 */ 1083 void 1084 arstream(int fd, Arfile *ap) 1085 { 1086 Armember *bp; 1087 int i; 1088 char buf[8192]; 1089 1090 if (ap->paged) { /* copy from disk */ 1091 seek(ap->fd, 0, 0); 1092 for (;;) { 1093 i = read(ap->fd, buf, sizeof(buf)); 1094 if (i < 0) 1095 rderr(); 1096 if (i == 0) 1097 break; 1098 if (write(fd, buf, i) != i) 1099 wrerr(); 1100 } 1101 close(ap->fd); 1102 ap->paged = 0; 1103 } 1104 /* dump the in-core buffers */ 1105 for (bp = ap->head; bp; bp = bp->next) { 1106 if (!arwrite(fd, bp)) 1107 wrerr(); 1108 } 1109 } 1110 1111 /* 1112 * write a member to 'fd'. 1113 */ 1114 int 1115 arwrite(int fd, Armember *bp) 1116 { 1117 int len; 1118 1119 if(HEADER_IO(write, fd, bp->hdr)) 1120 return 0; 1121 len = bp->size; 1122 if (len & 01) 1123 len++; 1124 if (write(fd, bp->member, len) != len) 1125 return 0; 1126 return 1; 1127 } 1128 1129 /* 1130 * Spill a member to a disk copy of a temp file 1131 */ 1132 int 1133 page(Arfile *ap) 1134 { 1135 Armember *bp; 1136 1137 bp = ap->head; 1138 if (!ap->paged) { /* not yet paged - create file */ 1139 ap->fname = mktemp(ap->fname); 1140 ap->fd = create(ap->fname, ORDWR|ORCLOSE, 0600); 1141 if (ap->fd < 0) { 1142 fprint(2,"ar: can't create temp file\n"); 1143 return 0; 1144 } 1145 ap->paged = 1; 1146 } 1147 if (!arwrite(ap->fd, bp)) /* write member and free buffer block */ 1148 return 0; 1149 ap->head = bp->next; 1150 if (ap->tail == bp) 1151 ap->tail = bp->next; 1152 free(bp->member); 1153 free(bp); 1154 return 1; 1155 } 1156 1157 /* 1158 * try to reclaim space by paging. we try to spill the start, middle, 1159 * and end files, in that order. there is no particular reason for the 1160 * ordering. 1161 */ 1162 int 1163 getspace(void) 1164 { 1165 if (astart && astart->head && page(astart)) 1166 return 1; 1167 if (amiddle && amiddle->head && page(amiddle)) 1168 return 1; 1169 if (aend && aend->head && page(aend)) 1170 return 1; 1171 return 0; 1172 } 1173 1174 void 1175 arfree(Arfile *ap) /* free a member buffer */ 1176 { 1177 Armember *bp, *next; 1178 1179 for (bp = ap->head; bp; bp = next) { 1180 next = bp->next; 1181 if (bp->member) 1182 free(bp->member); 1183 free(bp); 1184 } 1185 free(ap); 1186 } 1187 1188 /* 1189 * allocate space for a control block or member buffer. if the malloc 1190 * fails we try to reclaim space by spilling previously allocated 1191 * member buffers. 1192 */ 1193 char * 1194 armalloc(int n) 1195 { 1196 char *cp; 1197 1198 do { 1199 cp = malloc(n); 1200 if (cp) { 1201 memset(cp, 0, n); 1202 return cp; 1203 } 1204 } while (getspace()); 1205 fprint(2, "ar: out of memory\n"); 1206 exits("malloc"); 1207 return 0; 1208 } 1209