xref: /original-bsd/usr.bin/ar/ar.c (revision 79cf7955)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 static char sccsid[] = "@(#)ar.c	5.3 (Berkeley) 10/22/87";
9 #endif not lint
10 
11 #ifndef lint
12 char copyright[] =
13 "@(#) Copyright (c) 1980 Regents of the University of California.\n\
14  All rights reserved.\n";
15 #endif not lint
16 
17 /*
18  * ar - portable (ascii) format version
19  */
20 #include <sys/param.h>
21 #include <sys/stat.h>
22 #include <sys/time.h>
23 
24 #include <stdio.h>
25 #include <ar.h>
26 
27 struct	stat	stbuf;
28 struct	ar_hdr	arbuf;
29 struct	lar_hdr {
30 	char	lar_name[16];
31 	long	lar_date;
32 	u_short	lar_uid;
33 	u_short	lar_gid;
34 	u_short	lar_mode;
35 	long	lar_size;
36 } larbuf;
37 
38 #define	SKIP	1
39 #define	IODD	2
40 #define	OODD	4
41 #define	HEAD	8
42 
43 char	*man	=	{ "mrxtdpq" };
44 char	*opt	=	{ "uvnbailo" };
45 
46 int	signum[] = {SIGHUP, SIGINT, SIGQUIT, 0};
47 int	sigdone();
48 long	lseek();
49 int	rcmd();
50 int	dcmd();
51 int	xcmd();
52 int	tcmd();
53 int	pcmd();
54 int	mcmd();
55 int	qcmd();
56 int	(*comfun)();
57 char	flg[26];
58 char	**namv;
59 int	namc;
60 char	*arnam;
61 char	*ponam;
62 char	*tmpnam		=	{ "/tmp/vXXXXX" };
63 char	*tmp1nam	=	{ "/tmp/v1XXXXX" };
64 char	*tmp2nam	=	{ "/tmp/v2XXXXX" };
65 char	*tfnam;
66 char	*tf1nam;
67 char	*tf2nam;
68 char	*file;
69 char	name[16];
70 int	af;
71 int	tf;
72 int	tf1;
73 int	tf2;
74 int	qf;
75 int	bastate;
76 char	buf[MAXBSIZE];
77 int	truncate;			/* ok to truncate argument filenames */
78 
79 char	*trim();
80 char	*mktemp();
81 char	*ctime();
82 
83 main(argc, argv)
84 char *argv[];
85 {
86 	register i;
87 	register char *cp;
88 
89 	for(i=0; signum[i]; i++)
90 		if(signal(signum[i], SIG_IGN) != SIG_IGN)
91 			signal(signum[i], sigdone);
92 	if(argc < 3)
93 		usage();
94 	cp = argv[1];
95 	for(cp = argv[1]; *cp; cp++)
96 	switch(*cp) {
97 	case 'o':
98 	case 'l':
99 	case 'v':
100 	case 'u':
101 	case 'n':
102 	case 'a':
103 	case 'b':
104 	case 'c':
105 	case 'i':
106 		flg[*cp - 'a']++;
107 		continue;
108 
109 	case 'r':
110 		setcom(rcmd);
111 		continue;
112 
113 	case 'd':
114 		setcom(dcmd);
115 		continue;
116 
117 	case 'x':
118 		setcom(xcmd);
119 		continue;
120 
121 	case 't':
122 		setcom(tcmd);
123 		continue;
124 
125 	case 'p':
126 		setcom(pcmd);
127 		continue;
128 
129 	case 'm':
130 		setcom(mcmd);
131 		continue;
132 
133 	case 'q':
134 		setcom(qcmd);
135 		continue;
136 
137 	default:
138 		fprintf(stderr, "ar: bad option `%c'\n", *cp);
139 		done(1);
140 	}
141 	if(flg['l'-'a']) {
142 		tmpnam = "vXXXXX";
143 		tmp1nam = "v1XXXXX";
144 		tmp2nam = "v2XXXXX";
145 	}
146 	if(flg['i'-'a'])
147 		flg['b'-'a']++;
148 	if(flg['a'-'a'] || flg['b'-'a']) {
149 		bastate = 1;
150 		ponam = trim(argv[2]);
151 		argv++;
152 		argc--;
153 		if(argc < 3)
154 			usage();
155 	}
156 	arnam = argv[2];
157 	namv = argv+3;
158 	namc = argc-3;
159 	if(comfun == 0) {
160 		if(flg['u'-'a'] == 0) {
161 			fprintf(stderr, "ar: one of [%s] must be specified\n", man);
162 			done(1);
163 		}
164 		setcom(rcmd);
165 	}
166 	(*comfun)();
167 	done(notfound());
168 }
169 
170 setcom(fun)
171 int (*fun)();
172 {
173 
174 	if(comfun != 0) {
175 		fprintf(stderr, "ar: only one of [%s] allowed\n", man);
176 		done(1);
177 	}
178 	comfun = fun;
179 }
180 
181 rcmd()
182 {
183 	register f;
184 
185 	init();
186 	getaf();
187 	while(!getdir()) {
188 		bamatch();
189 		if(namc == 0 || match()) {
190 			f = stats();
191 			if(f < 0) {
192 				if(namc)
193 					fprintf(stderr, "ar: cannot open %s\n", file);
194 				goto cp;
195 			}
196 			if(flg['u'-'a'])
197 				if(stbuf.st_mtime <= larbuf.lar_date) {
198 					close(f);
199 					goto cp;
200 				}
201 			mesg('r');
202 			copyfil(af, -1, IODD+SKIP);
203 			movefil(f);
204 			continue;
205 		}
206 	cp:
207 		mesg('c');
208 		copyfil(af, tf, IODD+OODD+HEAD);
209 	}
210 	cleanup();
211 }
212 
213 dcmd()
214 {
215 
216 	init();
217 	if(getaf())
218 		noar();
219 	while(!getdir()) {
220 		if(match()) {
221 			mesg('d');
222 			copyfil(af, -1, IODD+SKIP);
223 			continue;
224 		}
225 		mesg('c');
226 		copyfil(af, tf, IODD+OODD+HEAD);
227 	}
228 	install();
229 }
230 
231 xcmd()
232 {
233 	register f;
234 	struct timeval tv[2];
235 
236 	if(getaf())
237 		noar();
238 	while(!getdir()) {
239 		if(namc == 0 || match()) {
240 			f = creat(file, larbuf.lar_mode & 0777);
241 			if(f < 0) {
242 				fprintf(stderr, "ar: %s cannot create\n", file);
243 				goto sk;
244 			}
245 			mesg('x');
246 			copyfil(af, f, IODD);
247 			close(f);
248 			if (flg['o'-'a']) {
249 				tv[0].tv_sec = tv[1].tv_sec = larbuf.lar_date;
250 				tv[0].tv_usec = tv[1].tv_usec = 0;
251 				utimes(file, tv);
252 			}
253 			continue;
254 		}
255 	sk:
256 		mesg('c');
257 		copyfil(af, -1, IODD+SKIP);
258 		if (namc > 0  &&  !morefil())
259 			done(0);
260 	}
261 }
262 
263 pcmd()
264 {
265 
266 	if(getaf())
267 		noar();
268 	while(!getdir()) {
269 		if(namc == 0 || match()) {
270 			if(flg['v'-'a']) {
271 				printf("\n<%s>\n\n", file);
272 				fflush(stdout);
273 			}
274 			copyfil(af, 1, IODD);
275 			continue;
276 		}
277 		copyfil(af, -1, IODD+SKIP);
278 	}
279 }
280 
281 mcmd()
282 {
283 
284 	init();
285 	if(getaf())
286 		noar();
287 	tf2nam = mktemp(tmp2nam);
288 	close(creat(tf2nam, 0600));
289 	tf2 = open(tf2nam, 2);
290 	if(tf2 < 0) {
291 		fprintf(stderr, "ar: cannot create third temp\n");
292 		done(1);
293 	}
294 	while(!getdir()) {
295 		bamatch();
296 		if(match()) {
297 			mesg('m');
298 			copyfil(af, tf2, IODD+OODD+HEAD);
299 			continue;
300 		}
301 		mesg('c');
302 		copyfil(af, tf, IODD+OODD+HEAD);
303 	}
304 	install();
305 }
306 
307 tcmd()
308 {
309 
310 	if(getaf())
311 		noar();
312 	while(!getdir()) {
313 		if(namc == 0 || match()) {
314 			if(flg['v'-'a'])
315 				longt();
316 			printf("%s\n", trim(file));
317 		}
318 		copyfil(af, -1, IODD+SKIP);
319 	}
320 }
321 
322 qcmd()
323 {
324 	register i, f;
325 
326 	if (flg['a'-'a'] || flg['b'-'a']) {
327 		fprintf(stderr, "ar: abi not allowed with q\n");
328 		done(1);
329 	}
330 	truncate++;
331 	getqf();
332 	for(i=0; signum[i]; i++)
333 		signal(signum[i], SIG_IGN);
334 	lseek(qf, 0l, 2);
335 	for(i=0; i<namc; i++) {
336 		file = namv[i];
337 		if(file == 0)
338 			continue;
339 		namv[i] = 0;
340 		mesg('q');
341 		f = stats();
342 		if(f < 0) {
343 			fprintf(stderr, "ar: %s cannot open\n", file);
344 			continue;
345 		}
346 		tf = qf;
347 		movefil(f);
348 		qf = tf;
349 	}
350 }
351 
352 init()
353 {
354 
355 	tfnam = mktemp(tmpnam);
356 	close(creat(tfnam, 0600));
357 	tf = open(tfnam, 2);
358 	if(tf < 0) {
359 		fprintf(stderr, "ar: cannot create temp file\n");
360 		done(1);
361 	}
362 	if (write(tf, ARMAG, SARMAG) != SARMAG)
363 		wrerr();
364 }
365 
366 getaf()
367 {
368 	char mbuf[SARMAG];
369 
370 	af = open(arnam, 0);
371 	if(af < 0)
372 		return(1);
373 	if (read(af, mbuf, SARMAG) != SARMAG || strncmp(mbuf, ARMAG, SARMAG)) {
374 		fprintf(stderr, "ar: %s not in archive format\n", arnam);
375 		done(1);
376 	}
377 	return(0);
378 }
379 
380 getqf()
381 {
382 	char mbuf[SARMAG];
383 
384 	if ((qf = open(arnam, 2)) < 0) {
385 		if(!flg['c'-'a'])
386 			fprintf(stderr, "ar: creating %s\n", arnam);
387 		if ((qf = creat(arnam, 0666)) < 0) {
388 			fprintf(stderr, "ar: cannot create %s\n", arnam);
389 			done(1);
390 		}
391 		if (write(qf, ARMAG, SARMAG) != SARMAG)
392 			wrerr();
393 	} else if (read(qf, mbuf, SARMAG) != SARMAG
394 		|| strncmp(mbuf, ARMAG, SARMAG)) {
395 		fprintf(stderr, "ar: %s not in archive format\n", arnam);
396 		done(1);
397 	}
398 }
399 
400 usage()
401 {
402 	printf("usage: ar [%s][%s] archive files ...\n", man, opt);
403 	done(1);
404 }
405 
406 noar()
407 {
408 
409 	fprintf(stderr, "ar: %s does not exist\n", arnam);
410 	done(1);
411 }
412 
413 sigdone()
414 {
415 	done(100);
416 }
417 
418 done(c)
419 {
420 
421 	if(tfnam)
422 		unlink(tfnam);
423 	if(tf1nam)
424 		unlink(tf1nam);
425 	if(tf2nam)
426 		unlink(tf2nam);
427 	exit(c);
428 }
429 
430 notfound()
431 {
432 	register i, n;
433 
434 	n = 0;
435 	for(i=0; i<namc; i++)
436 		if(namv[i]) {
437 			fprintf(stderr, "ar: %s not found\n", namv[i]);
438 			n++;
439 		}
440 	return(n);
441 }
442 
443 morefil()
444 {
445 	register i, n;
446 
447 	n = 0;
448 	for(i=0; i<namc; i++)
449 		if(namv[i])
450 			n++;
451 	return(n);
452 }
453 
454 cleanup()
455 {
456 	register i, f;
457 
458 	truncate++;
459 	for(i=0; i<namc; i++) {
460 		file = namv[i];
461 		if(file == 0)
462 			continue;
463 		namv[i] = 0;
464 		mesg('a');
465 		f = stats();
466 		if(f < 0) {
467 			fprintf(stderr, "ar: %s cannot open\n", file);
468 			continue;
469 		}
470 		movefil(f);
471 	}
472 	install();
473 }
474 
475 install()
476 {
477 	register i;
478 
479 	for(i=0; signum[i]; i++)
480 		signal(signum[i], SIG_IGN);
481 	if(af < 0)
482 		if(!flg['c'-'a'])
483 			fprintf(stderr, "ar: creating %s\n", arnam);
484 	close(af);
485 	af = creat(arnam, 0666);
486 	if(af < 0) {
487 		fprintf(stderr, "ar: cannot create %s\n", arnam);
488 		done(1);
489 	}
490 	if(tfnam) {
491 		lseek(tf, 0l, 0);
492 		while((i = read(tf, buf, MAXBSIZE)) > 0)
493 			if (write(af, buf, i) != i)
494 				wrerr();
495 	}
496 	if(tf2nam) {
497 		lseek(tf2, 0l, 0);
498 		while((i = read(tf2, buf, MAXBSIZE)) > 0)
499 			if (write(af, buf, i) != i)
500 				wrerr();
501 	}
502 	if(tf1nam) {
503 		lseek(tf1, 0l, 0);
504 		while((i = read(tf1, buf, MAXBSIZE)) > 0)
505 			if (write(af, buf, i) != i)
506 				wrerr();
507 	}
508 }
509 
510 /*
511  * insert the file 'file'
512  * into the temporary file
513  */
514 movefil(f)
515 {
516 	char buf[sizeof(arbuf)+1];
517 
518 	(void)sprintf(buf, "%-16s%-12ld%-6u%-6u%-8o%-10ld%-2s",
519 	   trim(file),
520 	   stbuf.st_mtime,
521 	   (u_short)stbuf.st_uid,
522 	   (u_short)stbuf.st_gid,
523 	   stbuf.st_mode,
524 	   stbuf.st_size,
525 	   ARFMAG);
526 	strncpy((char *)&arbuf, buf, sizeof(arbuf));
527 	larbuf.lar_size = stbuf.st_size;
528 	copyfil(f, tf, OODD+HEAD);
529 	close(f);
530 }
531 
532 stats()
533 {
534 	register f;
535 
536 	f = open(file, 0);
537 	if(f < 0)
538 		return(f);
539 	if(fstat(f, &stbuf) < 0) {
540 		close(f);
541 		return(-1);
542 	}
543 	return(f);
544 }
545 
546 /*
547  * copy next file
548  * size given in arbuf
549  */
550 copyfil(fi, fo, flag)
551 {
552 	register i, o;
553 	int pe;
554 
555 	if(flag & HEAD) {
556 		for (i=sizeof(arbuf.ar_name)-1; i>=0; i--) {
557 			if (arbuf.ar_name[i]==' ')
558 				continue;
559 			else if (arbuf.ar_name[i]=='\0')
560 				arbuf.ar_name[i] = ' ';
561 			else
562 				break;
563 		}
564 		if (write(fo, (char *)&arbuf, sizeof arbuf) != sizeof arbuf)
565 			wrerr();
566 	}
567 	pe = 0;
568 	while(larbuf.lar_size > 0) {
569 		i = o = MAXBSIZE;
570 		if(larbuf.lar_size < i) {
571 			i = o = larbuf.lar_size;
572 			if(i&1) {
573 				buf[i] = '\n';
574 				if(flag & IODD)
575 					i++;
576 				if(flag & OODD)
577 					o++;
578 			}
579 		}
580 		if(read(fi, buf, i) != i)
581 			pe++;
582 		if((flag & SKIP) == 0)
583 			if (write(fo, buf, o) != o)
584 				wrerr();
585 		larbuf.lar_size -= MAXBSIZE;
586 	}
587 	if(pe)
588 		phserr();
589 }
590 
591 getdir()
592 {
593 	register char *cp;
594 	register i;
595 
596 	i = read(af, (char *)&arbuf, sizeof arbuf);
597 	if(i != sizeof arbuf) {
598 		if(tf1nam) {
599 			i = tf;
600 			tf = tf1;
601 			tf1 = i;
602 		}
603 		return(1);
604 	}
605 	if (strncmp(arbuf.ar_fmag, ARFMAG, sizeof(arbuf.ar_fmag))) {
606 		fprintf(stderr, "ar: malformed archive (at %ld)\n", lseek(af, 0L, 1));
607 		done(1);
608 	}
609 	cp = arbuf.ar_name + sizeof(arbuf.ar_name);
610 	while (*--cp==' ')
611 		;
612 	*++cp = '\0';
613 	strncpy(name, arbuf.ar_name, sizeof(arbuf.ar_name));
614 	file = name;
615 	strncpy(larbuf.lar_name, name, sizeof(larbuf.lar_name));
616 	sscanf(arbuf.ar_date, "%ld", &larbuf.lar_date);
617 	sscanf(arbuf.ar_uid, "%hd", &larbuf.lar_uid);
618 	sscanf(arbuf.ar_gid, "%hd", &larbuf.lar_gid);
619 	sscanf(arbuf.ar_mode, "%ho", &larbuf.lar_mode);
620 	sscanf(arbuf.ar_size, "%ld", &larbuf.lar_size);
621 	return(0);
622 }
623 
624 match()
625 {
626 	register i;
627 
628 	for(i=0; i<namc; i++) {
629 		if(namv[i] == 0)
630 			continue;
631 		if(strcmp(trim(namv[i]), file) == 0) {
632 			file = namv[i];
633 			namv[i] = 0;
634 			return(1);
635 		}
636 	}
637 	return(0);
638 }
639 
640 bamatch()
641 {
642 	register f;
643 
644 	switch(bastate) {
645 
646 	case 1:
647 		if(strcmp(file, ponam) != 0)
648 			return;
649 		bastate = 2;
650 		if(flg['a'-'a'])
651 			return;
652 
653 	case 2:
654 		bastate = 0;
655 		tf1nam = mktemp(tmp1nam);
656 		close(creat(tf1nam, 0600));
657 		f = open(tf1nam, 2);
658 		if(f < 0) {
659 			fprintf(stderr, "ar: cannot create second temp\n");
660 			return;
661 		}
662 		tf1 = tf;
663 		tf = f;
664 	}
665 }
666 
667 phserr()
668 {
669 
670 	fprintf(stderr, "ar: phase error on %s\n", file);
671 }
672 
673 mesg(c)
674 {
675 
676 	if(flg['v'-'a'])
677 		if(c != 'c' || flg['v'-'a'] > 1)
678 			printf("%c - %s\n", c, file);
679 }
680 
681 char *
682 trim(s)
683 char *s;
684 {
685 	register char *p1, *p2;
686 
687 	/* Strip trailing slashes */
688 	for(p1 = s; *p1; p1++)
689 		;
690 	while(p1 > s) {
691 		if(*--p1 != '/')
692 			break;
693 		*p1 = 0;
694 	}
695 
696 	/* Find last component of path; do not zap the path */
697 	p2 = s;
698 	for(p1 = s; *p1; p1++)
699 		if(*p1 == '/')
700 			p2 = p1+1;
701 
702 	/*
703 	 * Truncate name if too long, only if we are doing an 'add'
704 	 * type operation. We only allow 15 cause rest of ar
705 	 * isn't smart enough to deal with non-null terminated
706 	 * names.  Need an exit status convention...
707 	 * Need yet another new archive format...
708 	 */
709 	if (truncate && strlen(p2) > sizeof(arbuf.ar_name) - 1) {
710 		fprintf(stderr, "ar: filename %s truncated to ", p2);
711 		*(p2 + sizeof(arbuf.ar_name) - 1) = '\0';
712 		fprintf(stderr, "%s\n", p2);
713 	}
714 	return(p2);
715 }
716 
717 #define	IFMT	060000
718 #define	ISARG	01000
719 #define	LARGE	010000
720 #define	SUID	04000
721 #define	SGID	02000
722 #define	ROWN	0400
723 #define	WOWN	0200
724 #define	XOWN	0100
725 #define	RGRP	040
726 #define	WGRP	020
727 #define	XGRP	010
728 #define	ROTH	04
729 #define	WOTH	02
730 #define	XOTH	01
731 #define	STXT	01000
732 
733 longt()
734 {
735 	register char *cp;
736 
737 	pmode();
738 	printf("%3d/%1d", larbuf.lar_uid, larbuf.lar_gid);
739 	printf("%7ld", larbuf.lar_size);
740 	cp = ctime(&larbuf.lar_date);
741 	printf(" %-12.12s %-4.4s ", cp+4, cp+20);
742 }
743 
744 int	m1[] = { 1, ROWN, 'r', '-' };
745 int	m2[] = { 1, WOWN, 'w', '-' };
746 int	m3[] = { 2, SUID, 's', XOWN, 'x', '-' };
747 int	m4[] = { 1, RGRP, 'r', '-' };
748 int	m5[] = { 1, WGRP, 'w', '-' };
749 int	m6[] = { 2, SGID, 's', XGRP, 'x', '-' };
750 int	m7[] = { 1, ROTH, 'r', '-' };
751 int	m8[] = { 1, WOTH, 'w', '-' };
752 int	m9[] = { 2, STXT, 't', XOTH, 'x', '-' };
753 
754 int	*m[] = { m1, m2, m3, m4, m5, m6, m7, m8, m9};
755 
756 pmode()
757 {
758 	register int **mp;
759 
760 	for (mp = &m[0]; mp < &m[9];)
761 		select(*mp++);
762 }
763 
764 select(pairp)
765 int *pairp;
766 {
767 	register int n, *ap;
768 
769 	ap = pairp;
770 	n = *ap++;
771 	while (--n>=0 && (larbuf.lar_mode&*ap++)==0)
772 		ap++;
773 	putchar(*ap);
774 }
775 
776 wrerr()
777 {
778 	perror("ar write error");
779 	done(1);
780 }
781