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 }