scc

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

ar.c (11630B)


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