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
       ---
       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 }