scc

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

scc-ar.c (11614B)


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