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