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