scc

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

ar.c (11302B)


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