1 %{ 2 /*- 3 * Copyright (c) 2008 Kai Wang 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer 11 * in this position and unchanged. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include <sys/mman.h> 32 #include <sys/param.h> 33 #include <sys/queue.h> 34 #include <sys/stat.h> 35 #include <archive.h> 36 #include <archive_entry.h> 37 #include <dirent.h> 38 #include <errno.h> 39 #include <fcntl.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <sysexits.h> 44 #include <unistd.h> 45 46 #include "ar.h" 47 48 #define TEMPLATE "arscp.XXXXXXXX" 49 50 struct list { 51 char *str; 52 struct list *next; 53 }; 54 55 56 extern int yylex(void); 57 58 static void yyerror(const char *); 59 static void arscp_addlib(char *archive, struct list *list); 60 static void arscp_addmod(struct list *list); 61 static void arscp_clear(void); 62 static int arscp_copy(int ifd, int ofd); 63 static void arscp_create(char *in, char *out); 64 static void arscp_delete(struct list *list); 65 static void arscp_dir(char *archive, struct list *list, char *rlt); 66 static void arscp_end(int eval); 67 static void arscp_extract(struct list *list); 68 static void arscp_free_argv(void); 69 static void arscp_free_mlist(struct list *list); 70 static void arscp_list(void); 71 static struct list *arscp_mlist(struct list *list, char *str); 72 static void arscp_mlist2argv(struct list *list); 73 static int arscp_mlist_len(struct list *list); 74 static void arscp_open(char *fname); 75 static void arscp_prompt(void); 76 static void arscp_replace(struct list *list); 77 static void arscp_save(void); 78 static int arscp_target_exist(void); 79 80 extern int lineno; 81 82 static struct bsdar *bsdar; 83 static char *target; 84 static char *tmpac; 85 static int interactive; 86 static int verbose; 87 88 %} 89 90 %token ADDLIB 91 %token ADDMOD 92 %token CLEAR 93 %token CREATE 94 %token DELETE 95 %token DIRECTORY 96 %token END 97 %token EXTRACT 98 %token LIST 99 %token OPEN 100 %token REPLACE 101 %token VERBOSE 102 %token SAVE 103 %token LP 104 %token RP 105 %token COMMA 106 %token EOL 107 %token <str> FNAME 108 %type <list> mod_list 109 110 %union { 111 char *str; 112 struct list *list; 113 } 114 115 %% 116 117 begin 118 : { arscp_prompt(); } ar_script 119 ; 120 121 ar_script 122 : cmd_list 123 | 124 ; 125 126 mod_list 127 : FNAME { $$ = arscp_mlist(NULL, $1); } 128 | mod_list separator FNAME { $$ = arscp_mlist($1, $3); } 129 ; 130 131 separator 132 : COMMA 133 | 134 ; 135 136 cmd_list 137 : rawcmd 138 | cmd_list rawcmd 139 ; 140 141 rawcmd 142 : cmd EOL { arscp_prompt(); } 143 ; 144 145 cmd 146 : addlib_cmd 147 | addmod_cmd 148 | clear_cmd 149 | create_cmd 150 | delete_cmd 151 | directory_cmd 152 | end_cmd 153 | extract_cmd 154 | list_cmd 155 | open_cmd 156 | replace_cmd 157 | verbose_cmd 158 | save_cmd 159 | invalid_cmd 160 | empty_cmd 161 | error 162 ; 163 164 addlib_cmd 165 : ADDLIB FNAME LP mod_list RP { arscp_addlib($2, $4); } 166 | ADDLIB FNAME { arscp_addlib($2, NULL); } 167 ; 168 169 addmod_cmd 170 : ADDMOD mod_list { arscp_addmod($2); } 171 ; 172 173 clear_cmd 174 : CLEAR { arscp_clear(); } 175 ; 176 177 create_cmd 178 : CREATE FNAME { arscp_create(NULL, $2); } 179 ; 180 181 delete_cmd 182 : DELETE mod_list { arscp_delete($2); } 183 ; 184 185 directory_cmd 186 : DIRECTORY FNAME { arscp_dir($2, NULL, NULL); } 187 | DIRECTORY FNAME LP mod_list RP { arscp_dir($2, $4, NULL); } 188 | DIRECTORY FNAME LP mod_list RP FNAME { arscp_dir($2, $4, $6); } 189 ; 190 191 end_cmd 192 : END { arscp_end(EX_OK); } 193 ; 194 195 extract_cmd 196 : EXTRACT mod_list { arscp_extract($2); } 197 ; 198 199 list_cmd 200 : LIST { arscp_list(); } 201 ; 202 203 open_cmd 204 : OPEN FNAME { arscp_open($2); } 205 ; 206 207 replace_cmd 208 : REPLACE mod_list { arscp_replace($2); } 209 ; 210 211 save_cmd 212 : SAVE { arscp_save(); } 213 ; 214 215 verbose_cmd 216 : VERBOSE { verbose = !verbose; } 217 ; 218 219 empty_cmd 220 : 221 ; 222 223 invalid_cmd 224 : FNAME { yyerror(NULL); } 225 ; 226 227 %% 228 229 /* ARGSUSED */ 230 static void 231 yyerror(const char *s) 232 { 233 234 (void) s; 235 printf("Syntax error in archive script, line %d\n", lineno); 236 } 237 238 /* 239 * arscp_open first open an archive and check its validity. If the archive 240 * format is valid, it calls arscp_create to create a temporary copy of 241 * the archive. 242 */ 243 static void 244 arscp_open(char *fname) 245 { 246 struct archive *a; 247 struct archive_entry *entry; 248 int r; 249 250 if ((a = archive_read_new()) == NULL) 251 bsdar_errc(bsdar, EX_SOFTWARE, 0, "archive_read_new failed"); 252 archive_read_support_format_ar(a); 253 AC(archive_read_open_filename(a, fname, DEF_BLKSZ)); 254 if ((r = archive_read_next_header(a, &entry))) 255 bsdar_warnc(bsdar, 0, "%s", archive_error_string(a)); 256 AC(archive_read_close(a)); 257 AC(archive_read_free(a)); 258 if (r != ARCHIVE_OK) 259 return; 260 arscp_create(fname, fname); 261 } 262 263 /* 264 * Create archive. in != NULL indicate it's a OPEN cmd, and resulting 265 * archive is based on modification of an existing one. If in == NULL, 266 * we are in CREATE cmd and a new empty archive will be created. 267 */ 268 static void 269 arscp_create(char *in, char *out) 270 { 271 struct archive *a; 272 int ifd, ofd; 273 274 /* Delete previously created temporary archive, if any. */ 275 if (tmpac) { 276 if (unlink(tmpac) < 0) 277 bsdar_errc(bsdar, EX_IOERR, errno, "unlink failed"); 278 free(tmpac); 279 } 280 281 tmpac = strdup(TEMPLATE); 282 if (tmpac == NULL) 283 bsdar_errc(bsdar, EX_SOFTWARE, errno, "strdup failed"); 284 if ((ofd = mkstemp(tmpac)) < 0) 285 bsdar_errc(bsdar, EX_IOERR, errno, "mkstemp failed"); 286 287 if (in) { 288 /* 289 * Command OPEN creates a temporary copy of the 290 * input archive. 291 */ 292 if ((ifd = open(in, O_RDONLY)) < 0) { 293 bsdar_warnc(bsdar, errno, "open failed"); 294 return; 295 } 296 if (arscp_copy(ifd, ofd)) { 297 bsdar_warnc(bsdar, 0, "arscp_copy failed"); 298 return; 299 } 300 close(ifd); 301 close(ofd); 302 } else { 303 /* 304 * Command CREATE creates an "empty" archive. 305 * (archive with only global header) 306 */ 307 if ((a = archive_write_new()) == NULL) 308 bsdar_errc(bsdar, EX_SOFTWARE, 0, 309 "archive_write_new failed"); 310 archive_write_set_format_ar_svr4(a); 311 AC(archive_write_open_fd(a, ofd)); 312 AC(archive_write_close(a)); 313 AC(archive_write_free(a)); 314 } 315 316 /* Override previous target, if any. */ 317 if (target) 318 free(target); 319 320 target = out; 321 bsdar->filename = tmpac; 322 } 323 324 /* A file copying implementation using mmap. */ 325 static int 326 arscp_copy(int ifd, int ofd) 327 { 328 struct stat sb; 329 char *buf, *p; 330 ssize_t w; 331 size_t bytes; 332 333 if (fstat(ifd, &sb) < 0) { 334 bsdar_warnc(bsdar, errno, "fstate failed"); 335 return (1); 336 } 337 if ((p = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, ifd, 338 (off_t)0)) == MAP_FAILED) { 339 bsdar_warnc(bsdar, errno, "mmap failed"); 340 return (1); 341 } 342 for (buf = p, bytes = sb.st_size; bytes > 0; bytes -= w) { 343 w = write(ofd, buf, bytes); 344 if (w <= 0) { 345 bsdar_warnc(bsdar, errno, "write failed"); 346 break; 347 } 348 } 349 if (munmap(p, sb.st_size) < 0) 350 bsdar_errc(bsdar, EX_SOFTWARE, errno, "munmap failed"); 351 if (bytes > 0) 352 return (1); 353 354 return (0); 355 } 356 357 /* 358 * Add all modules of archive to current archive, if list != NULL, 359 * only those modules specified in 'list' will be added. 360 */ 361 static void 362 arscp_addlib(char *archive, struct list *list) 363 { 364 365 if (!arscp_target_exist()) 366 return; 367 arscp_mlist2argv(list); 368 bsdar->addlib = archive; 369 ar_mode_A(bsdar); 370 arscp_free_argv(); 371 arscp_free_mlist(list); 372 } 373 374 /* Add modules into current archive. */ 375 static void 376 arscp_addmod(struct list *list) 377 { 378 379 if (!arscp_target_exist()) 380 return; 381 arscp_mlist2argv(list); 382 ar_mode_q(bsdar); 383 arscp_free_argv(); 384 arscp_free_mlist(list); 385 } 386 387 /* Delete modules from current archive. */ 388 static void 389 arscp_delete(struct list *list) 390 { 391 392 if (!arscp_target_exist()) 393 return; 394 arscp_mlist2argv(list); 395 ar_mode_d(bsdar); 396 arscp_free_argv(); 397 arscp_free_mlist(list); 398 } 399 400 /* Extract modules from current archive. */ 401 static void 402 arscp_extract(struct list *list) 403 { 404 405 if (!arscp_target_exist()) 406 return; 407 arscp_mlist2argv(list); 408 ar_mode_x(bsdar); 409 arscp_free_argv(); 410 arscp_free_mlist(list); 411 } 412 413 /* List modules of archive. (Simple Mode) */ 414 static void 415 arscp_list(void) 416 { 417 418 if (!arscp_target_exist()) 419 return; 420 bsdar->argc = 0; 421 bsdar->argv = NULL; 422 /* Always verbose. */ 423 bsdar->options |= AR_V; 424 ar_mode_t(bsdar); 425 bsdar->options &= ~AR_V; 426 } 427 428 /* List modules of archive. (Advance Mode) */ 429 static void 430 arscp_dir(char *archive, struct list *list, char *rlt) 431 { 432 FILE *out; 433 434 /* If rlt != NULL, redirect output to it */ 435 out = NULL; 436 if (rlt) { 437 out = stdout; 438 if ((stdout = fopen(rlt, "w")) == NULL) 439 bsdar_errc(bsdar, EX_IOERR, errno, 440 "fopen %s failed", rlt); 441 } 442 443 bsdar->filename = archive; 444 if (list) 445 arscp_mlist2argv(list); 446 else { 447 bsdar->argc = 0; 448 bsdar->argv = NULL; 449 } 450 if (verbose) 451 bsdar->options |= AR_V; 452 ar_mode_t(bsdar); 453 bsdar->options &= ~AR_V; 454 455 if (rlt) { 456 if (fclose(stdout) == EOF) 457 bsdar_errc(bsdar, EX_IOERR, errno, 458 "fclose %s failed", rlt); 459 stdout = out; 460 free(rlt); 461 } 462 free(archive); 463 bsdar->filename = tmpac; 464 arscp_free_argv(); 465 arscp_free_mlist(list); 466 } 467 468 469 /* Replace modules of current archive. */ 470 static void 471 arscp_replace(struct list *list) 472 { 473 474 if (!arscp_target_exist()) 475 return; 476 arscp_mlist2argv(list); 477 ar_mode_r(bsdar); 478 arscp_free_argv(); 479 arscp_free_mlist(list); 480 } 481 482 /* Rename the temporary archive to the target archive. */ 483 static void 484 arscp_save(void) 485 { 486 mode_t mask; 487 488 if (target) { 489 if (rename(tmpac, target) < 0) 490 bsdar_errc(bsdar, EX_IOERR, errno, "rename failed"); 491 /* 492 * mkstemp creates temp files with mode 0600, here we 493 * set target archive mode per process umask. 494 */ 495 mask = umask(0); 496 umask(mask); 497 if (chmod(target, 0666 & ~mask) < 0) 498 bsdar_errc(bsdar, EX_IOERR, errno, "chmod failed"); 499 free(tmpac); 500 free(target); 501 tmpac = NULL; 502 target= NULL; 503 bsdar->filename = NULL; 504 } else 505 bsdar_warnc(bsdar, 0, "no open output archive"); 506 } 507 508 /* 509 * Discard all the contents of current archive. This is achieved by 510 * invoking CREATE cmd on current archive. 511 */ 512 static void 513 arscp_clear(void) 514 { 515 char *new_target; 516 517 if (target) { 518 new_target = strdup(target); 519 if (new_target == NULL) 520 bsdar_errc(bsdar, EX_SOFTWARE, errno, "strdup failed"); 521 arscp_create(NULL, new_target); 522 } 523 } 524 525 /* 526 * Quit ar(1). Note that END cmd will not SAVE current archive 527 * before exit. 528 */ 529 static void 530 arscp_end(int eval) 531 { 532 533 if (target) 534 free(target); 535 if (tmpac) { 536 if (unlink(tmpac) == -1) 537 bsdar_errc(bsdar, EX_IOERR, errno, "unlink %s failed", 538 tmpac); 539 free(tmpac); 540 } 541 542 exit(eval); 543 } 544 545 /* 546 * Check if target specified, i.e, whether OPEN or CREATE has been 547 * issued by user. 548 */ 549 static int 550 arscp_target_exist(void) 551 { 552 553 if (target) 554 return (1); 555 556 bsdar_warnc(bsdar, 0, "no open output archive"); 557 return (0); 558 } 559 560 /* Construct module list. */ 561 static struct list * 562 arscp_mlist(struct list *list, char *str) 563 { 564 struct list *l; 565 566 l = malloc(sizeof(*l)); 567 if (l == NULL) 568 bsdar_errc(bsdar, EX_SOFTWARE, errno, "malloc failed"); 569 l->str = str; 570 l->next = list; 571 572 return (l); 573 } 574 575 /* Calculate the length of a mlist. */ 576 static int 577 arscp_mlist_len(struct list *list) 578 { 579 int len; 580 581 for(len = 0; list; list = list->next) 582 len++; 583 584 return (len); 585 } 586 587 /* Free the space allocated for mod_list. */ 588 static void 589 arscp_free_mlist(struct list *list) 590 { 591 struct list *l; 592 593 /* Note that list->str was freed in arscp_free_argv. */ 594 for(; list; list = l) { 595 l = list->next; 596 free(list); 597 } 598 } 599 600 /* Convert mlist to argv array. */ 601 static void 602 arscp_mlist2argv(struct list *list) 603 { 604 char **argv; 605 int i, n; 606 607 n = arscp_mlist_len(list); 608 argv = malloc(n * sizeof(*argv)); 609 if (argv == NULL) 610 bsdar_errc(bsdar, EX_SOFTWARE, errno, "malloc failed"); 611 612 /* Note that module names are stored in reverse order in mlist. */ 613 for(i = n - 1; i >= 0; i--, list = list->next) { 614 if (list == NULL) 615 bsdar_errc(bsdar, EX_SOFTWARE, errno, "invalid mlist"); 616 argv[i] = list->str; 617 } 618 619 bsdar->argc = n; 620 bsdar->argv = argv; 621 } 622 623 /* Free space allocated for argv array and its elements. */ 624 static void 625 arscp_free_argv(void) 626 { 627 int i; 628 629 for(i = 0; i < bsdar->argc; i++) 630 free(bsdar->argv[i]); 631 632 free(bsdar->argv); 633 } 634 635 /* Show a prompt if we are in interactive mode */ 636 static void 637 arscp_prompt(void) 638 { 639 640 if (interactive) { 641 printf("AR >"); 642 fflush(stdout); 643 } 644 } 645 646 /* Main function for ar script mode. */ 647 void 648 ar_mode_script(struct bsdar *ar) 649 { 650 651 bsdar = ar; 652 interactive = isatty(fileno(stdin)); 653 while(yyparse()) { 654 if (!interactive) 655 arscp_end(1); 656 } 657 658 /* Script ends without END */ 659 arscp_end(EX_OK); 660 } 661