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