scc

A fork of https://git.simple-cc.org/scc/ for Qute

git clone https://git.8pit.net/scc.git

  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
 13enum {
 14	BEFORE,
 15	INDOT,
 16	AFTER,
 17};
 18
 19struct tmp {
 20	char *name;
 21	FILE *fp;
 22} tmps[3];
 23
 24char *argv0;
 25
 26static int bflag, vflag, cflag, lflag, uflag, aflag;
 27static char *arfile, *posname;
 28static char invalidchars[] = " ";
 29
 30struct 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
 52static void
 53cleanup(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
 65static char *
 66errstr(void)
 67{
 68	return strerror(errno);
 69}
 70
 71static void
 72error(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 */
 91static void
 92sigfun(int signum)
 93{
 94	cleanup();
 95	_Exit(1);
 96}
 97
 98static FILE *
 99openar(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
121static void
122archive(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
156static void
157append(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
173static void
174copy(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
190static void
191letters(unsigned long val, char *s)
192{
193	*s++ = (val & 04) ? 'r' : '-';
194	*s++ = (val & 02) ? 'w' : '-';
195	*s++ = (val & 01) ? 'x' : '-';
196}
197
198static char *
199perms(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
211static char *
212inlist(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
226static int
227older(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
236static void
237move(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
254static void
255insert(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
265static void
266update(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
288static void
289extract(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
319error_file:
320	error("error extracting file: %s", errstr());
321}
322
323static void
324print(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
338static void
339list(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
361static void
362del(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
372static char *
373getfname(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
388static long long
389getnum(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
412static int
413valid(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
429static void
430run(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
453static void
454merge(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
478static void
479closetmp(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
489static void
490opentmp(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
505static void
506ar(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
563static void
564checkfnames(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
579static void
580usage(void)
581{
582	fputs("scc-ar [-cluv] [-d|-m|-p|-q|-r|-t|-x] [-a|-b|-i posname] archive [file...]\n",
583	      stderr);
584	exit(1);
585}
586
587int
588main(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}