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