scc

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

ar.c (11275B)


      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 	} else if (posname && !strcmp(posname, m->fname)) {
    264 		where = (bflag) ? AFTER : BEFORE;
    265 		m->cur = AFTER;
    266 	} else {
    267 		where = m->cur;
    268 	}
    269 	copy(m, &tmps[where]);
    270 }
    271 
    272 static void
    273 extract(struct member *m, int argc, char *argv[])
    274 {
    275 	int c;
    276 	long siz;
    277 	FILE *fp;
    278 	struct fprop prop;
    279 	struct ar_hdr *hdr = &m->hdr;
    280 
    281 	if (argc > 0 && !inlist(m->fname, argc, argv))
    282 		return;
    283 	if (vflag)
    284 		printf("x - %s\n", m->fname);
    285 	siz = m->size;
    286 
    287 	if ((fp = fopen(m->fname, "wb")) == NULL)
    288 		goto error_file;
    289 	while (siz-- > 0 && (c = getc(m->src)) != EOF)
    290 		putc(c, fp);
    291 	fflush(fp);
    292 	if (fclose(fp) == EOF)
    293 		goto error_file;
    294 
    295 	prop.uid = atol(hdr->ar_uid);
    296 	prop.gid = atol(hdr->ar_gid);
    297 	prop.mode = m->mode;
    298 	prop.time = totime(m->date);
    299 	if (setstat(m->fname, &prop) < 0)
    300 		error("%s: setting file attributes", m->fname);
    301 	return;
    302 
    303 error_file:
    304 	error("error extracting file: %s", errstr());
    305 }
    306 
    307 static void
    308 print(struct member *m, int argc, char *argv[])
    309 {
    310 	long siz;
    311 	int c;
    312 
    313 	if (argc > 0 && !inlist(m->fname, argc, argv))
    314 		return;
    315 	if (vflag)
    316 		printf("\n<%s>\n\n", m->fname);
    317 	siz = m->size;
    318 	while (siz-- > 0 && (c = getc(m->src)) != EOF)
    319 		putchar(c);
    320 }
    321 
    322 static void
    323 list(struct member *m, int argc, char *argv[])
    324 {
    325 	time_t t;
    326 	struct ar_hdr *hdr = &m->hdr;
    327 	char mtime[30];
    328 
    329 	if (argc > 0  && !inlist(m->fname, argc, argv))
    330 		return;
    331 	if (!vflag) {
    332 		printf("%s\n", m->fname);
    333 	} else {
    334 		t = totime(m->date);
    335 		strftime(mtime, sizeof(mtime), "%c", localtime(&t));
    336 		printf("%s %ld/%ld\t%s %s\n",
    337 		       perms(m),
    338 		       atol(hdr->ar_uid),
    339 		       atol(hdr->ar_gid),
    340 		       mtime,
    341 		       m->fname);
    342 	}
    343 }
    344 
    345 static void
    346 del(struct member *m, int argc, char *argv[])
    347 {
    348 	if (inlist(m->fname, argc, argv)) {
    349 		if (vflag)
    350 			printf("d - %s\n", m->fname);
    351 		return;
    352 	}
    353 	copy(m, &tmps[BEFORE]);
    354 }
    355 
    356 static char *
    357 getfname(struct ar_hdr *hdr)
    358 {
    359 	static char fname[SARNAM+1];
    360 	char *p;
    361 
    362 	memcpy(fname, hdr->ar_name, SARNAM);
    363 
    364 	if (p = strchr(fname, ' '))
    365 		*p = '\0';
    366 	else
    367 		fname[SARNAM] = '\0';
    368 
    369 	return fname;
    370 }
    371 
    372 static long long
    373 getnum(char *s, int size, int base)
    374 {
    375 	int c;
    376 	long long val;
    377 	char *p;
    378 	static char digits[] = "0123456789";
    379 
    380 	for (val = 0; size > 0; val += c) {
    381 		--size;
    382 		if ((c = *s++) == ' ')
    383 			break;
    384 		if ((p = strchr(digits, c)) == NULL)
    385 			return -1;
    386 		if ((c = p - digits) >= base)
    387 			return -1;
    388 		val *= base;
    389 	}
    390 
    391 	while (size > 0 && *s++ == ' ')
    392 		--size;
    393 	return (size == 0) ? val : -1;
    394 }
    395 
    396 static int
    397 valid(struct member *m)
    398 {
    399 	struct ar_hdr *hdr = &m->hdr;
    400 
    401 	m->fname = getfname(&m->hdr);
    402 	m->size = getnum(hdr->ar_size, sizeof(hdr->ar_size), 10);
    403 	m->mode = getnum(hdr->ar_mode, sizeof(hdr->ar_mode), 8);
    404 	m->date = getnum(hdr->ar_date, sizeof(hdr->ar_date), 10);
    405 
    406 	if (strncmp(hdr->ar_fmag, ARFMAG, sizeof(hdr->ar_fmag)) ||
    407 	    m->size < 0 || m->mode < 0 || m->date < 0) {
    408 		return 0;
    409 	}
    410 	return 1;
    411 }
    412 
    413 static void
    414 run(FILE *fp, int argc, char *argv[],
    415     void (*fun)(struct member *, int argc, char *files[]))
    416 {
    417 	struct member m;
    418 
    419 	m.src = fp;
    420 	m.cur = BEFORE;
    421 
    422 	while (fread(&m.hdr, sizeof(m.hdr), 1, fp) == 1) {
    423 		fpos_t pos;
    424 
    425 		if (!valid(&m))
    426 			error("corrupted member '%s'", m.fname);
    427 		fgetpos(fp, &pos);
    428 		(*fun)(&m, argc, argv);
    429 		fsetpos(fp, &pos);
    430 		fseek(fp, m.size+1 & ~1, SEEK_CUR);
    431 	}
    432 	if (ferror(fp))
    433 		error("reading members: %s", errstr());
    434 	fclose(fp);
    435 }
    436 
    437 static void
    438 merge(void)
    439 {
    440 	FILE *fp, *fi;
    441 	int c, i;
    442 
    443 	if ((fp = fopen(arfile, "wb")) == NULL)
    444 		error("reopening archive: %s", errstr());
    445 
    446 	fputs(ARMAG, fp);
    447 
    448 	for (i = 0; i < 3; i++) {
    449 		if ((fi = tmps[i].fp) == NULL)
    450 			continue;
    451 		fseek(fi, 0, SEEK_SET);
    452 		while ((c = getc(fi)) != EOF)
    453 			putc(c, fp);
    454 		if (ferror(fi))
    455 			error("error in temporary: %s", errstr());
    456 	}
    457 
    458 	if (fclose(fp) == EOF)
    459 		error("writing archive file: %s", errstr());
    460 }
    461 
    462 static void
    463 closetmp(int which)
    464 {
    465 	struct tmp *tmp = &tmps[which];
    466 
    467 	if (!tmp->fp)
    468 		return;
    469 	if (fclose(tmp->fp) == EOF)
    470 		error("closing temporaries: %s", errstr());
    471 }
    472 
    473 static void
    474 opentmp(char *fname, int which)
    475 {
    476 	struct tmp *tmp = &tmps[which];
    477 
    478 	if (lflag) {
    479 		tmp->name = fname;
    480 		tmp->fp = fopen(fname, "w+b");
    481 	} else {
    482 		tmp->fp = tmpfile();
    483 	}
    484 
    485 	if (tmp->fp == NULL)
    486 		error("creating temporary: %s", errstr());
    487 }
    488 
    489 static void
    490 ar(int key, char *argv[], int argc)
    491 {
    492 	FILE *fp;
    493 
    494 	fp = openar();
    495 	if (argc == 0 &&
    496 	    (key == 'r' || key == 'd' || key == 'm' || key == 'q')) {
    497 		if (fclose(fp) == EOF)
    498 			error("early close of archive file: %s", errstr());
    499 		return;
    500 	}
    501 
    502 	if (key == 'r' || key == 'm' || key == 'd')
    503 		opentmp("ar.tmp1", BEFORE);
    504 	if (key == 'r' || key == 'm') {
    505 		opentmp("ar.tmp2", INDOT);
    506 		opentmp("ar.tmp3", AFTER);
    507 	}
    508 
    509 	switch (key) {
    510 	case 'r':
    511 		run(fp, argc, argv, update);
    512 		insert(argc, argv);
    513 		merge();
    514 		break;
    515 	case 'm':
    516 		run(fp, argc, argv, move);
    517 		merge();
    518 		break;
    519 	case 'd':
    520 		run(fp, argc, argv, del);
    521 		merge();
    522 		break;
    523 	case 't':
    524 		run(fp, argc, argv, list);
    525 		break;
    526 	case 'p':
    527 		run(fp, argc, argv, print);
    528 		break;
    529 	case 'x':
    530 		run(fp, argc, argv, extract);
    531 		break;
    532 	case 'q':
    533 		append(fp, argv);
    534 		break;
    535 	}
    536 
    537 	closetmp(BEFORE);
    538 	closetmp(INDOT);
    539 	closetmp(AFTER);
    540 
    541 	for ( ; argc-- > 0; ++argv) {
    542 		if (*argv)
    543 			error("No member named '%s'", *argv);
    544 	}
    545 }
    546 
    547 static void
    548 checkfnames(int argc, char *argv[])
    549 {
    550 	size_t l;
    551 	char *p;
    552 
    553 	for ( ; argc-- > 0; ++argv) {
    554 		p = canonical(*argv);
    555 		l = strcspn(p, invalidchars);
    556 		if (l > 16)
    557 			error("file: '%s': name too long", *argv);
    558 		if (p[l] != '\0')
    559 			error("file: '%s': name invalid", *argv);
    560 	}
    561 }
    562 
    563 static void
    564 usage(void)
    565 {
    566 	fputs("ar [-drqtpmx][posname] [-vuaibcl] [posname] arfile name ...\n",
    567 	      stderr);
    568 	exit(1);
    569 }
    570 
    571 int
    572 main(int argc, char *argv[])
    573 {
    574 	int key, nkey = 0, pos = 0;
    575 
    576 	atexit(cleanup);
    577 	ARGBEGIN {
    578 	case 'd':
    579 		nkey++;
    580 		key = 'd';
    581 		break;
    582 	case 'r':
    583 		nkey++;
    584 		key = 'r';
    585 		break;
    586 	case 'q':
    587 		nkey++;
    588 		key = 'q';
    589 		break;
    590 	case 't':
    591 		nkey++;
    592 		key = 't';
    593 		break;
    594 	case 'p':
    595 		nkey++;
    596 		key = 'p';
    597 		break;
    598 	case 'm':
    599 		nkey++;
    600 		key = 'm';
    601 		break;
    602 	case 'x':
    603 		nkey++;
    604 		key = 'x';
    605 		break;
    606 	case 'a':
    607 		aflag = 1;
    608 		pos++;
    609 		posname = EARGF(usage());
    610 		break;
    611 	case 'i':
    612 	case 'b':
    613 		bflag = 1;
    614 		pos++;
    615 		posname = EARGF(usage());
    616 		break;
    617 	case 'v':
    618 		vflag = 1;
    619 		break;
    620 	case 'c':
    621 		cflag = 1;
    622 		break;
    623 	case 'l':
    624 		lflag = 1;
    625 		break;
    626 	case 'u':
    627 		uflag = 1;
    628 		break;
    629 	default:
    630 		usage();
    631 	} ARGEND
    632 
    633 	if (nkey == 0 || nkey > 1 || pos > 1 || argc == 0 ||
    634 	    (aflag || bflag) && !(key == 'm' || key == 'r') ||
    635 	    cflag && !(key == 'q' || key == 'r') ||
    636 	    uflag && key != 'r')
    637 		usage();
    638 
    639 	signal(SIGINT, sigfun);
    640 	signal(SIGQUIT, sigfun);
    641 	signal(SIGTERM, sigfun);
    642 
    643 	arfile = *argv;
    644 	checkfnames(--argc, ++argv);
    645 	ar(key, argv, argc);
    646 
    647 	fflush(stdout);
    648 	if (ferror(stdout))
    649 		error("error writing to stdout: %s", errstr());
    650 
    651 	return 0;
    652 }