scc-ar.c - scc - simple c99 compiler
(HTM) git clone git://git.simple-cc.org/scc
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) Submodules
(DIR) README
(DIR) 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 }