scc

simple c99 compiler
git clone git://git.simple-cc.org/scc
Log | Files | Refs | README | LICENSE

ar.c (11319B)


      1 static char sccsid[] = "@(#) ./ar/main.c";
      2 
      3 #include <errno.h>
      4 #include <signal.h>
      5 #include <stdarg.h>
      6 #include <stdio.h>
      7 #include <stdlib.h>
      8 #include <string.h>
      9 #include <time.h>
     10 
     11 #include "sys.h"
     12 
     13 #include <scc/ar.h>
     14 #include <scc/arg.h>
     15 
     16 enum {
     17 	BEFORE,
     18 	INDOT,
     19 	AFTER,
     20 };
     21 
     22 struct tmp {
     23 	char *name;
     24 	FILE *fp;
     25 } tmps[3];
     26 
     27 char *argv0;
     28 
     29 static int bflag, vflag, cflag, lflag, uflag, aflag;
     30 static char *arfile, *posname;
     31 
     32 struct member {
     33 	FILE *src;
     34 	struct ar_hdr hdr;
     35 	int cur;
     36 	char *fname;
     37 	long size;
     38 	long mode;
     39 	long long date;
     40 };
     41 
     42 static void
     43 cleanup(void)
     44 {
     45 	int i;
     46 
     47 	for (i = 0; i < 3; i++) {
     48 		if (tmps[i].name)
     49 			remove(tmps[i].name);
     50 	}
     51 }
     52 
     53 static char *
     54 errstr(void)
     55 {
     56 	return strerror(errno);
     57 }
     58 
     59 static void
     60 error(char *fmt, ...)
     61 {
     62 	va_list va;
     63 
     64 	va_start(va, fmt);
     65 	fprintf(stderr, "ar: %s: ", arfile);
     66 	vfprintf(stderr, fmt, va);
     67 	putc('\n', stderr);
     68 	va_end(va);
     69 
     70 	exit(EXIT_FAILURE);
     71 }
     72 
     73 /*
     74  * I do know that you cannot call remove from a signal handler
     75  * but we only can use stdio function to deal with files
     76  * because we are C99 compliant, and it is very likely that
     77  * remove is going to work in this case
     78  */
     79 static void
     80 sigfun(int signum)
     81 {
     82 	cleanup();
     83 	_Exit(1);
     84 }
     85 
     86 static FILE *
     87 openar(void)
     88 {
     89 	FILE *fp;
     90 	char magic[SARMAG+1];
     91 
     92 	if ((fp = fopen(arfile,"r+b")) == NULL) {
     93 		if (!cflag)
     94 			fprintf(stderr, "ar: creating %s\n", arfile);
     95 		if ((fp = fopen(arfile, "w+b")) == NULL)
     96 			error("opening archive: %s", errstr());
     97 		fputs(ARMAG, fp);
     98 		if (fflush(fp) == EOF)
     99 			error("writing magic number: %s", errstr());
    100 	} else {
    101 		if (fgets(magic, sizeof(magic), fp) == NULL)
    102 			error("error reading magic number: %s", errstr());
    103 		if (strcmp(magic, ARMAG))
    104 			error("invalid magic number '%s'", magic);
    105 	}
    106 	return fp;
    107 }
    108 
    109 static void
    110 archive(char *pname, FILE *to, char letter)
    111 {
    112 	int c;
    113 	FILE *from;
    114 	char mtime[13], *fname;
    115 	struct fprop prop;
    116 
    117 	fname = canonical(pname);
    118 
    119 	if (vflag)
    120 		printf("%c - %s\n", letter, fname);
    121 	if ((from = fopen(pname, "rb")) == NULL)
    122 		error("opening member '%s': %s", pname, errstr());
    123 	if (getstat(pname, &prop) < 0)
    124 		error("error getting '%s' attributes", pname);
    125 
    126 	strftime(mtime, sizeof(mtime), "%s", gmtime(&prop.time));
    127 	fprintf(to,
    128 	        "%-16.16s%-12s%-6u%-6u%-8lo%-10ld`\n",
    129 	        fname,
    130 	        mtime,
    131 	        prop.uid,
    132 	        prop.gid,
    133 	        prop.mode,
    134 	        prop.size);
    135 
    136 	while ((c = getc(from)) != EOF)
    137 		putc(c, to);
    138 	if (prop.size & 1)
    139 		putc('\n', to);
    140 	if (ferror(from))
    141 		error("reading input '%s': %s", pname, errstr());
    142 	fclose(from);
    143 }
    144 
    145 static void
    146 append(FILE *fp, char *argv[])
    147 {
    148 	char *fname;
    149 
    150 	if (fseek(fp, 0, SEEK_END) == EOF)
    151 		error("seeking archive: %s", errstr());
    152 
    153 	for ( ; fname = *argv; ++argv) {
    154 		*argv = NULL;
    155 		archive(fname, fp, 'a');
    156 	}
    157 
    158 	if (fclose(fp) == EOF)
    159 		error("error writing archive: %s", errstr());
    160 }
    161 
    162 static void
    163 copy(struct member *m, struct tmp *tmp)
    164 {
    165 	int c;
    166 	size_t siz = m->size;
    167 	struct ar_hdr *hdr = &m->hdr;
    168 
    169 	fwrite(hdr, sizeof(*hdr), 1, tmp->fp);
    170 	if ((siz & 1) == 1)
    171 		siz++;
    172 	while (siz--) {
    173 		if ((c = getc(m->src)) == EOF)
    174 			break;
    175 		fputc(c, tmp->fp);
    176 	}
    177 }
    178 
    179 static void
    180 letters(unsigned long val, char *s)
    181 {
    182 	*s++ = (val & 04) ? 'r' : '-';
    183 	*s++ = (val & 02) ? 'w' : '-';
    184 	*s++ = (val & 01) ? 'x' : '-';
    185 }
    186 
    187 static char *
    188 perms(struct member *m)
    189 {
    190 	static char buf[10];
    191 
    192 	letters(m->mode >> 6, buf);
    193 	letters(m->mode >> 3, buf+3);
    194 	letters(m->mode, buf +6);
    195 	buf[9] = '\0';
    196 
    197 	return buf;
    198 }
    199 
    200 static char *
    201 inlist(char *fname, int argc, char *argv[])
    202 {
    203 	char *p;
    204 
    205 	for ( ; argc-- > 0; ++argv) {
    206 		if (*argv && !strcmp(canonical(*argv), fname)) {
    207 			p = *argv;
    208 			*argv = NULL;
    209 			return p;
    210 		}
    211 	}
    212 	return NULL;
    213 }
    214 
    215 static int
    216 older(struct member *m, char *pname)
    217 {
    218 	struct fprop prop;
    219 
    220 	if (getstat(pname, &prop) < 0)
    221 		error("error getting '%s' attributes", pname);
    222 	return prop.time > m->date;
    223 }
    224 
    225 static void
    226 move(struct member *m, int argc, char *argv[])
    227 {
    228 	int where;
    229 
    230 	if (inlist(m->fname, argc, argv)) {
    231 		if (vflag)
    232 			printf("m - %s\n", m->fname);
    233 		where = INDOT;
    234 	} else if (posname && !strcmp(posname, m->fname)) {
    235 		where = (bflag) ? AFTER : BEFORE;
    236 		m->cur = AFTER;
    237 	} else {
    238 		where = m->cur;
    239 	}
    240 	copy(m, &tmps[where]);
    241 }
    242 
    243 static void
    244 insert(int argc, char *argv[])
    245 {
    246 	for (; argc-- > 0; ++argv) {
    247 		if (*argv) {
    248 			archive(*argv, tmps[INDOT].fp, 'a');
    249 			*argv = NULL;
    250 		}
    251 	}
    252 }
    253 
    254 static void
    255 update(struct member *m, int argc, char *argv[])
    256 {
    257 	int where;
    258 	FILE *fp = tmps[BEFORE].fp;
    259 	char *pname;
    260 
    261 	if (pname = inlist(m->fname, argc, argv)) {
    262 		if (uflag && older(m, pname))
    263 			archive(pname, tmps[m->cur].fp, 'r');
    264 		return;
    265 	} else if (posname && !strcmp(posname, m->fname)) {
    266 		where = (bflag) ? AFTER : BEFORE;
    267 		m->cur = AFTER;
    268 	} else {
    269 		where = m->cur;
    270 	}
    271 	copy(m, &tmps[where]);
    272 }
    273 
    274 static void
    275 extract(struct member *m, int argc, char *argv[])
    276 {
    277 	int c;
    278 	long siz;
    279 	FILE *fp;
    280 	struct fprop prop;
    281 	struct ar_hdr *hdr = &m->hdr;
    282 
    283 	if (argc > 0 && !inlist(m->fname, argc, argv))
    284 		return;
    285 	if (vflag)
    286 		printf("x - %s\n", m->fname);
    287 	siz = m->size;
    288 
    289 	if ((fp = fopen(m->fname, "wb")) == NULL)
    290 		goto error_file;
    291 	while (siz-- > 0 && (c = getc(m->src)) != EOF)
    292 		putc(c, fp);
    293 	fflush(fp);
    294 	if (fclose(fp) == EOF)
    295 		goto error_file;
    296 
    297 	prop.uid = atol(hdr->ar_uid);
    298 	prop.gid = atol(hdr->ar_gid);
    299 	prop.mode = m->mode;
    300 	prop.time = totime(m->date);
    301 	if (setstat(m->fname, &prop) < 0)
    302 		error("%s: setting file attributes", m->fname);
    303 	return;
    304 
    305 error_file:
    306 	error("error extracting file: %s", errstr());
    307 }
    308 
    309 static void
    310 print(struct member *m, int argc, char *argv[])
    311 {
    312 	long siz;
    313 	int c;
    314 
    315 	if (argc > 0 && !inlist(m->fname, argc, argv))
    316 		return;
    317 	if (vflag)
    318 		printf("\n<%s>\n\n", m->fname);
    319 	siz = m->size;
    320 	while (siz-- > 0 && (c = getc(m->src)) != EOF)
    321 		putchar(c);
    322 }
    323 
    324 static void
    325 list(struct member *m, int argc, char *argv[])
    326 {
    327 	time_t t;
    328 	struct ar_hdr *hdr = &m->hdr;
    329 	char mtime[30];
    330 
    331 	if (argc > 0  && !inlist(m->fname, argc, argv))
    332 		return;
    333 	if (!vflag) {
    334 		printf("%s\n", m->fname);
    335 	} else {
    336 		t = totime(m->date);
    337 		strftime(mtime, sizeof(mtime), "%c", localtime(&t));
    338 		printf("%s %ld/%ld\t%s %s\n",
    339 		       perms(m),
    340 		       atol(hdr->ar_uid),
    341 		       atol(hdr->ar_gid),
    342 		       mtime,
    343 		       m->fname);
    344 	}
    345 }
    346 
    347 static void
    348 del(struct member *m, int argc, char *argv[])
    349 {
    350 	if (inlist(m->fname, argc, argv)) {
    351 		if (vflag)
    352 			printf("d - %s\n", m->fname);
    353 		return;
    354 	}
    355 	copy(m, &tmps[BEFORE]);
    356 }
    357 
    358 static char *
    359 getfname(struct ar_hdr *hdr)
    360 {
    361 	static char fname[SARNAM+1];
    362 	char *p;
    363 
    364 	memcpy(fname, hdr->ar_name, SARNAM);
    365 
    366 	if (p = strchr(fname, ' '))
    367 		*p = '\0';
    368 	else
    369 		fname[SARNAM] = '\0';
    370 
    371 	return fname;
    372 }
    373 
    374 static long long
    375 getnum(char *s, int size, int base)
    376 {
    377 	int c;
    378 	long long val;
    379 	char *p;
    380 	static char digits[] = "0123456789";
    381 
    382 	for (val = 0; size > 0; val += c) {
    383 		--size;
    384 		if ((c = *s++) == ' ')
    385 			break;
    386 		if ((p = strchr(digits, c)) == NULL)
    387 			return -1;
    388 		if ((c = p - digits) >= base)
    389 			return -1;
    390 		val *= base;
    391 	}
    392 
    393 	while (size > 0 && *s++ == ' ')
    394 		--size;
    395 	return (size == 0) ? val : -1;
    396 }
    397 
    398 static int
    399 valid(struct member *m)
    400 {
    401 	struct ar_hdr *hdr = &m->hdr;
    402 
    403 	m->fname = getfname(&m->hdr);
    404 	m->size = getnum(hdr->ar_size, sizeof(hdr->ar_size), 10);
    405 	m->mode = getnum(hdr->ar_mode, sizeof(hdr->ar_mode), 8);
    406 	m->date = getnum(hdr->ar_date, sizeof(hdr->ar_date), 10);
    407 
    408 	if (strncmp(hdr->ar_fmag, ARFMAG, sizeof(hdr->ar_fmag)) ||
    409 	    m->size < 0 || m->mode < 0 || m->date < 0) {
    410 		return 0;
    411 	}
    412 	return 1;
    413 }
    414 
    415 static void
    416 run(FILE *fp, int argc, char *argv[],
    417     void (*fun)(struct member *, int argc, char *files[]))
    418 {
    419 	struct member m;
    420 
    421 	m.src = fp;
    422 	m.cur = BEFORE;
    423 
    424 	while (fread(&m.hdr, sizeof(m.hdr), 1, fp) == 1) {
    425 		fpos_t pos;
    426 
    427 		if (!valid(&m))
    428 			error("corrupted member '%s'", m.fname);
    429 		fgetpos(fp, &pos);
    430 		(*fun)(&m, argc, argv);
    431 		fsetpos(fp, &pos);
    432 		fseek(fp, m.size+1 & ~1, SEEK_CUR);
    433 	}
    434 	if (ferror(fp))
    435 		error("reading members: %s", errstr());
    436 	fclose(fp);
    437 }
    438 
    439 static void
    440 merge(void)
    441 {
    442 	FILE *fp, *fi;
    443 	int c, i;
    444 
    445 	if ((fp = fopen(arfile, "wb")) == NULL)
    446 		error("reopening archive: %s", errstr());
    447 
    448 	fputs(ARMAG, fp);
    449 
    450 	for (i = 0; i < 3; i++) {
    451 		if ((fi = tmps[i].fp) == NULL)
    452 			continue;
    453 		fseek(fi, 0, SEEK_SET);
    454 		while ((c = getc(fi)) != EOF)
    455 			putc(c, fp);
    456 		if (ferror(fi))
    457 			error("error in temporary: %s", errstr());
    458 	}
    459 
    460 	if (fclose(fp) == EOF)
    461 		error("writing archive file: %s", errstr());
    462 }
    463 
    464 static void
    465 closetmp(int which)
    466 {
    467 	struct tmp *tmp = &tmps[which];
    468 
    469 	if (!tmp->fp)
    470 		return;
    471 	if (fclose(tmp->fp) == EOF)
    472 		error("closing temporaries: %s", errstr());
    473 }
    474 
    475 static void
    476 opentmp(char *fname, int which)
    477 {
    478 	struct tmp *tmp = &tmps[which];
    479 
    480 	if (lflag) {
    481 		tmp->name = fname;
    482 		tmp->fp = fopen(fname, "w+b");
    483 	} else {
    484 		tmp->fp = tmpfile();
    485 	}
    486 
    487 	if (tmp->fp == NULL)
    488 		error("creating temporary: %s", errstr());
    489 }
    490 
    491 static void
    492 ar(int key, char *argv[], int argc)
    493 {
    494 	FILE *fp;
    495 
    496 	fp = openar();
    497 	if (argc == 0 &&
    498 	    (key == 'r' || key == 'd' || key == 'm' || key == 'q')) {
    499 		if (fclose(fp) == EOF)
    500 			error("early close of archive file: %s", errstr());
    501 		return;
    502 	}
    503 
    504 	if (key == 'r' || key == 'm' || key == 'd')
    505 		opentmp("ar.tmp1", BEFORE);
    506 	if (key == 'r' || key == 'm') {
    507 		opentmp("ar.tmp2", INDOT);
    508 		opentmp("ar.tmp3", AFTER);
    509 	}
    510 
    511 	switch (key) {
    512 	case 'r':
    513 		run(fp, argc, argv, update);
    514 		insert(argc, argv);
    515 		merge();
    516 		break;
    517 	case 'm':
    518 		run(fp, argc, argv, move);
    519 		merge();
    520 		break;
    521 	case 'd':
    522 		run(fp, argc, argv, del);
    523 		merge();
    524 		break;
    525 	case 't':
    526 		run(fp, argc, argv, list);
    527 		break;
    528 	case 'p':
    529 		run(fp, argc, argv, print);
    530 		break;
    531 	case 'x':
    532 		run(fp, argc, argv, extract);
    533 		break;
    534 	case 'q':
    535 		append(fp, argv);
    536 		break;
    537 	}
    538 
    539 	closetmp(BEFORE);
    540 	closetmp(INDOT);
    541 	closetmp(AFTER);
    542 
    543 	for ( ; argc-- > 0; ++argv) {
    544 		if (*argv)
    545 			error("No member named '%s'", *argv);
    546 	}
    547 }
    548 
    549 static void
    550 checkfnames(int argc, char *argv[])
    551 {
    552 	size_t l;
    553 	char *p;
    554 
    555 	for ( ; argc-- > 0; ++argv) {
    556 		p = canonical(*argv);
    557 		l = strcspn(p, invalidchars);
    558 		if (l > 16)
    559 			error("file: '%s': name too long", *argv);
    560 		if (p[l] != '\0')
    561 			error("file: '%s': name invalid", *argv);
    562 	}
    563 }
    564 
    565 static void
    566 usage(void)
    567 {
    568 	fputs("ar [-drqtpmx][posname] [-vuaibcl] [posname] arfile name ...\n",
    569 	      stderr);
    570 	exit(1);
    571 }
    572 
    573 int
    574 main(int argc, char *argv[])
    575 {
    576 	int key, nkey = 0, pos = 0;
    577 
    578 	atexit(cleanup);
    579 	ARGBEGIN {
    580 	case 'd':
    581 		nkey++;
    582 		key = 'd';
    583 		break;
    584 	case 'r':
    585 		nkey++;
    586 		key = 'r';
    587 		break;
    588 	case 'q':
    589 		nkey++;
    590 		key = 'q';
    591 		break;
    592 	case 't':
    593 		nkey++;
    594 		key = 't';
    595 		break;
    596 	case 'p':
    597 		nkey++;
    598 		key = 'p';
    599 		break;
    600 	case 'm':
    601 		nkey++;
    602 		key = 'm';
    603 		break;
    604 	case 'x':
    605 		nkey++;
    606 		key = 'x';
    607 		break;
    608 	case 'a':
    609 		aflag = 1;
    610 		pos++;
    611 		posname = EARGF(usage());
    612 		break;
    613 	case 'i':
    614 	case 'b':
    615 		bflag = 1;
    616 		pos++;
    617 		posname = EARGF(usage());
    618 		break;
    619 	case 'v':
    620 		vflag = 1;
    621 		break;
    622 	case 'c':
    623 		cflag = 1;
    624 		break;
    625 	case 'l':
    626 		lflag = 1;
    627 		break;
    628 	case 'u':
    629 		uflag = 1;
    630 		break;
    631 	default:
    632 		usage();
    633 	} ARGEND
    634 
    635 	if (nkey == 0 || nkey > 1 || pos > 1 || argc == 0 ||
    636 	    (aflag || bflag) && !(key == 'm' || key == 'r') ||
    637 	    cflag && !(key == 'q' || key == 'r') ||
    638 	    uflag && key != 'r')
    639 		usage();
    640 
    641 	signal(SIGINT, sigfun);
    642 	signal(SIGQUIT, sigfun);
    643 	signal(SIGTERM, sigfun);
    644 
    645 	arfile = *argv;
    646 	checkfnames(--argc, ++argv);
    647 	ar(key, argv, argc);
    648 
    649 	fflush(stdout);
    650 	if (ferror(stdout))
    651 		error("error writing to stdout: %s", errstr());
    652 
    653 	return 0;
    654 }