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 50struct list { 51 char *str; 52 struct list *next; 53}; 54 55 56extern int yylex(void); 57 58static void yyerror(const char *); 59static void arscp_addlib(char *archive, struct list *list); 60static void arscp_addmod(struct list *list); 61static void arscp_clear(void); 62static int arscp_copy(int ifd, int ofd); 63static void arscp_create(char *in, char *out); 64static void arscp_delete(struct list *list); 65static void arscp_dir(char *archive, struct list *list, char *rlt); 66static void arscp_end(int eval); 67static void arscp_extract(struct list *list); 68static void arscp_free_argv(void); 69static void arscp_free_mlist(struct list *list); 70static void arscp_list(void); 71static struct list *arscp_mlist(struct list *list, char *str); 72static void arscp_mlist2argv(struct list *list); 73static int arscp_mlist_len(struct list *list); 74static void arscp_open(char *fname); 75static void arscp_prompt(void); 76static void arscp_replace(struct list *list); 77static void arscp_save(void); 78static int arscp_target_exist(void); 79 80extern int lineno; 81 82static struct bsdar *bsdar; 83static char *target; 84static char *tmpac; 85static int interactive; 86static 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 117begin 118 : { arscp_prompt(); } ar_script 119 ; 120 121ar_script 122 : cmd_list 123 | 124 ; 125 126mod_list 127 : FNAME { $$ = arscp_mlist(NULL, $1); } 128 | mod_list separator FNAME { $$ = arscp_mlist($1, $3); } 129 ; 130 131separator 132 : COMMA 133 | 134 ; 135 136cmd_list 137 : rawcmd 138 | cmd_list rawcmd 139 ; 140 141rawcmd 142 : cmd EOL { arscp_prompt(); } 143 ; 144 145cmd 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 164addlib_cmd 165 : ADDLIB FNAME LP mod_list RP { arscp_addlib($2, $4); } 166 | ADDLIB FNAME { arscp_addlib($2, NULL); } 167 ; 168 169addmod_cmd 170 : ADDMOD mod_list { arscp_addmod($2); } 171 ; 172 173clear_cmd 174 : CLEAR { arscp_clear(); } 175 ; 176 177create_cmd 178 : CREATE FNAME { arscp_create(NULL, $2); } 179 ; 180 181delete_cmd 182 : DELETE mod_list { arscp_delete($2); } 183 ; 184 185directory_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 191end_cmd 192 : END { arscp_end(EX_OK); } 193 ; 194 195extract_cmd 196 : EXTRACT mod_list { arscp_extract($2); } 197 ; 198 199list_cmd 200 : LIST { arscp_list(); } 201 ; 202 203open_cmd 204 : OPEN FNAME { arscp_open($2); } 205 ; 206 207replace_cmd 208 : REPLACE mod_list { arscp_replace($2); } 209 ; 210 211save_cmd 212 : SAVE { arscp_save(); } 213 ; 214 215verbose_cmd 216 : VERBOSE { verbose = !verbose; } 217 ; 218 219empty_cmd 220 : 221 ; 222 223invalid_cmd 224 : FNAME { yyerror(NULL); } 225 ; 226 227%% 228 229/* ARGSUSED */ 230static void 231yyerror(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 */ 243static void 244arscp_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 */ 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, 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. */ 325static int 326arscp_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 */ 361static void 362arscp_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. */ 375static void 376arscp_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. */ 388static void 389arscp_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. */ 401static void 402arscp_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) */ 414static void 415arscp_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) */ 429static void 430arscp_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. */ 470static void 471arscp_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. */ 483static void 484arscp_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 */ 512static void 513arscp_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 */ 529static void 530arscp_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 */ 549static int 550arscp_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. */ 561static struct list * 562arscp_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. */ 576static int 577arscp_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. */ 588static void 589arscp_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. */ 601static void 602arscp_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. */ 624static void 625arscp_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 */ 636static void 637arscp_prompt(void) 638{ 639 640 if (interactive) { 641 printf("AR >"); 642 fflush(stdout); 643 } 644} 645 646/* Main function for ar script mode. */ 647void 648ar_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