1286180Sscottl%{ 2290091Sbapt/*- 3286180Sscottl * Copyright (c) 2008 Kai Wang 4286180Sscottl * All rights reserved. 5286180Sscottl * 6286180Sscottl * Redistribution and use in source and binary forms, with or without 7286180Sscottl * modification, are permitted provided that the following conditions 8286180Sscottl * are met: 9286180Sscottl * 1. Redistributions of source code must retain the above copyright 10286180Sscottl * notice, this list of conditions and the following disclaimer 11286180Sscottl * in this position and unchanged. 12290091Sbapt * 2. Redistributions in binary form must reproduce the above copyright 13286180Sscottl * notice, this list of conditions and the following disclaimer in the 14286180Sscottl * documentation and/or other materials provided with the distribution. 15286180Sscottl * 16286180Sscottl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 17286180Sscottl * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18286180Sscottl * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19286180Sscottl * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 20286180Sscottl * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21286180Sscottl * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22286180Sscottl * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23286180Sscottl * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24286180Sscottl * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25286180Sscottl * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26286180Sscottl */ 27291002Sbapt 28290091Sbapt#include <sys/cdefs.h> 29286180Sscottl__FBSDID("$FreeBSD$"); 30286180Sscottl 31290091Sbapt#include <sys/mman.h> 32290091Sbapt#include <sys/param.h> 33290091Sbapt#include <sys/queue.h> 34286180Sscottl#include <sys/stat.h> 35286180Sscottl#include <archive.h> 36286180Sscottl#include <archive_entry.h> 37286180Sscottl#include <dirent.h> 38286180Sscottl#include <errno.h> 39286180Sscottl#include <fcntl.h> 40286180Sscottl#include <stdio.h> 41286180Sscottl#include <stdlib.h> 42290091Sbapt#include <string.h> 43286180Sscottl#include <sysexits.h> 44286180Sscottl#include <unistd.h> 45290091Sbapt 46286180Sscottl#include "ar.h" 47286180Sscottl 48290091Sbapt#define TEMPLATE "arscp.XXXXXXXX" 49290091Sbapt 50290091Sbaptstruct list { 51286180Sscottl char *str; 52286180Sscottl struct list *next; 53290091Sbapt}; 54286180Sscottl 55286180Sscottl 56290091Sbaptextern int yylex(void); 57286180Sscottl 58286180Sscottlstatic void yyerror(const char *); 59290091Sbaptstatic void arscp_addlib(char *archive, struct list *list); 60286180Sscottlstatic void arscp_addmod(struct list *list); 61286180Sscottlstatic void arscp_clear(void); 62290091Sbaptstatic int arscp_copy(int ifd, int ofd); 63291002Sbaptstatic void arscp_create(char *in, char *out); 64291002Sbaptstatic void arscp_delete(struct list *list); 65291002Sbaptstatic void arscp_dir(char *archive, struct list *list, char *rlt); 66291002Sbaptstatic void arscp_end(int eval); 67291002Sbaptstatic void arscp_extract(struct list *list); 68291002Sbaptstatic void arscp_free_argv(void); 69291002Sbaptstatic void arscp_free_mlist(struct list *list); 70291002Sbaptstatic void arscp_list(void); 71291002Sbaptstatic struct list *arscp_mlist(struct list *list, char *str); 72291002Sbaptstatic void arscp_mlist2argv(struct list *list); 73286180Sscottlstatic int arscp_mlist_len(struct list *list); 74286180Sscottlstatic void arscp_open(char *fname); 75286180Sscottlstatic void arscp_prompt(void); 76286180Sscottlstatic void arscp_replace(struct list *list); 77290091Sbaptstatic void arscp_save(void); 78290091Sbaptstatic int arscp_target_exist(void); 79290091Sbapt 80290091Sbaptextern int lineno; 81290091Sbapt 82290091Sbaptstatic struct bsdar *bsdar; 83290091Sbaptstatic char *target; 84290091Sbaptstatic char *tmpac; 85290091Sbaptstatic int interactive; 86290091Sbaptstatic int verbose; 87290091Sbapt 88290091Sbapt%} 89290091Sbapt 90286180Sscottl%token ADDLIB 91286180Sscottl%token ADDMOD 92286180Sscottl%token CLEAR 93286180Sscottl%token CREATE 94286180Sscottl%token DELETE 95286180Sscottl%token DIRECTORY 96286180Sscottl%token END 97286180Sscottl%token EXTRACT 98286180Sscottl%token LIST 99286180Sscottl%token OPEN 100286180Sscottl%token REPLACE 101286180Sscottl%token VERBOSE 102286180Sscottl%token SAVE 103286180Sscottl%token LP 104286180Sscottl%token RP 105286180Sscottl%token COMMA 106286180Sscottl%token EOL 107291002Sbapt%token <str> FNAME 108291002Sbapt%type <list> mod_list 109291002Sbapt 110286180Sscottl%union { 111286180Sscottl char *str; 112286180Sscottl struct list *list; 113286180Sscottl} 114286180Sscottl 115286180Sscottl%% 116286180Sscottl 117290091Sbaptbegin 118290091Sbapt : { arscp_prompt(); } ar_script 119290091Sbapt ; 120290091Sbapt 121290091Sbaptar_script 122290091Sbapt : cmd_list 123290091Sbapt | 124290091Sbapt ; 125290091Sbapt 126290091Sbaptmod_list 127290091Sbapt : FNAME { $$ = arscp_mlist(NULL, $1); } 128290091Sbapt | mod_list separator FNAME { $$ = arscp_mlist($1, $3); } 129290091Sbapt ; 130290091Sbapt 131290091Sbaptseparator 132290091Sbapt : COMMA 133286180Sscottl | 134291002Sbapt ; 135291002Sbapt 136291002Sbaptcmd_list 137291002Sbapt : rawcmd 138291002Sbapt | cmd_list rawcmd 139291002Sbapt ; 140291002Sbapt 141291002Sbaptrawcmd 142291002Sbapt : cmd EOL { arscp_prompt(); } 143291002Sbapt ; 144291002Sbapt 145291002Sbaptcmd 146291002Sbapt : addlib_cmd 147291002Sbapt | addmod_cmd 148291002Sbapt | clear_cmd 149291002Sbapt | create_cmd 150291002Sbapt | delete_cmd 151291002Sbapt | directory_cmd 152291002Sbapt | end_cmd 153291002Sbapt | extract_cmd 154291002Sbapt | list_cmd 155291002Sbapt | open_cmd 156291002Sbapt | replace_cmd 157291002Sbapt | verbose_cmd 158286180Sscottl | save_cmd 159291002Sbapt | invalid_cmd 160290091Sbapt | empty_cmd 161286180Sscottl | error 162286180Sscottl ; 163286180Sscottl 164286180Sscottladdlib_cmd 165290091Sbapt : 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