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