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 49struct list { 50 char *str; 51 struct list *next; 52}; 53 54 55extern int yylex(void); 56 57static void yyerror(const char *); 58static void arscp_addlib(char *archive, struct list *list); 59static void arscp_addmod(struct list *list); 60static void arscp_clear(void); 61static int arscp_copy(int ifd, int ofd); 62static void arscp_create(char *in, char *out); 63static void arscp_delete(struct list *list); 64static void arscp_dir(char *archive, struct list *list, char *rlt); 65static void arscp_end(int eval); 66static void arscp_extract(struct list *list); 67static void arscp_free_argv(void); 68static void arscp_free_mlist(struct list *list); 69static void arscp_list(void); 70static struct list *arscp_mlist(struct list *list, char *str); 71static void arscp_mlist2argv(struct list *list); 72static int arscp_mlist_len(struct list *list); 73static void arscp_open(char *fname); 74static void arscp_prompt(void); 75static void arscp_replace(struct list *list); 76static void arscp_save(void); 77static int arscp_target_exist(void); 78 79extern int lineno; 80 81static struct bsdar *bsdar; 82static char *target; 83static char *tmpac; 84static int interactive; 85static 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 116begin 117 : { arscp_prompt(); } ar_script 118 ; 119 120ar_script 121 : cmd_list 122 | 123 ; 124 125mod_list 126 : FNAME { $$ = arscp_mlist(NULL, $1); } 127 | mod_list separator FNAME { $$ = arscp_mlist($1, $3); } 128 ; 129 130separator 131 : COMMA 132 | 133 ; 134 135cmd_list 136 : rawcmd 137 | cmd_list rawcmd 138 ; 139 140rawcmd 141 : cmd EOL { arscp_prompt(); } 142 ; 143 144cmd 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 163addlib_cmd 164 : ADDLIB FNAME LP mod_list RP { arscp_addlib($2, $4); } 165 | ADDLIB FNAME { arscp_addlib($2, NULL); } 166 ; 167 168addmod_cmd 169 : ADDMOD mod_list { arscp_addmod($2); } 170 ; 171 172clear_cmd 173 : CLEAR { arscp_clear(); } 174 ; 175 176create_cmd 177 : CREATE FNAME { arscp_create(NULL, $2); } 178 ; 179 180delete_cmd 181 : DELETE mod_list { arscp_delete($2); } 182 ; 183 184directory_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 190end_cmd 191 : END { arscp_end(EXIT_SUCCESS); } 192 ; 193 194extract_cmd 195 : EXTRACT mod_list { arscp_extract($2); } 196 ; 197 198list_cmd 199 : LIST { arscp_list(); } 200 ; 201 202open_cmd 203 : OPEN FNAME { arscp_open($2); } 204 ; 205 206replace_cmd 207 : REPLACE mod_list { arscp_replace($2); } 208 ; 209 210save_cmd 211 : SAVE { arscp_save(); } 212 ; 213 214verbose_cmd 215 : VERBOSE { verbose = !verbose; } 216 ; 217 218empty_cmd 219 : 220 ; 221 222invalid_cmd 223 : FNAME { yyerror(NULL); } 224 ; 225 226%% 227 228/* ARGSUSED */ 229static void 230yyerror(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 */ 242static void 243arscp_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 */ 268static void 269arscp_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. */ 324static int 325arscp_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 */ 360static void 361arscp_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. */ 374static void 375arscp_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. */ 387static void 388arscp_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. */ 400static void 401arscp_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) */ 413static void 414arscp_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) */ 428static void 429arscp_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. */ 465static void 466arscp_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. */ 478static void 479arscp_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 */ 507static void 508arscp_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 */ 524static void 525arscp_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 */ 544static int 545arscp_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. */ 556static struct list * 557arscp_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. */ 571static int 572arscp_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. */ 583static void 584arscp_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. */ 596static void 597arscp_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. */ 619static void 620arscp_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 */ 631static void 632arscp_prompt(void) 633{ 634 635 if (interactive) { 636 printf("AR >"); 637 fflush(stdout); 638 } 639} 640 641/* Main function for ar script mode. */ 642void 643ar_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