1183218Skaiw%{ 2183218Skaiw/*- 3183218Skaiw * Copyright (c) 2008 Kai Wang 4183218Skaiw * All rights reserved. 5183218Skaiw * 6183218Skaiw * Redistribution and use in source and binary forms, with or without 7183218Skaiw * modification, are permitted provided that the following conditions 8183218Skaiw * are met: 9183218Skaiw * 1. Redistributions of source code must retain the above copyright 10183218Skaiw * notice, this list of conditions and the following disclaimer 11183218Skaiw * in this position and unchanged. 12183218Skaiw * 2. Redistributions in binary form must reproduce the above copyright 13183218Skaiw * notice, this list of conditions and the following disclaimer in the 14183218Skaiw * documentation and/or other materials provided with the distribution. 15183218Skaiw * 16183218Skaiw * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 17183218Skaiw * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18183218Skaiw * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19183218Skaiw * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 20183218Skaiw * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21183218Skaiw * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22183218Skaiw * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23183218Skaiw * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24183218Skaiw * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25183218Skaiw * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26183218Skaiw */ 27183218Skaiw 28183218Skaiw#include <sys/cdefs.h> 29183218Skaiw__FBSDID("$FreeBSD$"); 30183218Skaiw 31183218Skaiw#include <sys/mman.h> 32183218Skaiw#include <sys/param.h> 33183218Skaiw#include <sys/queue.h> 34183218Skaiw#include <sys/stat.h> 35183218Skaiw#include <archive.h> 36183218Skaiw#include <archive_entry.h> 37183218Skaiw#include <dirent.h> 38183218Skaiw#include <errno.h> 39183218Skaiw#include <fcntl.h> 40183218Skaiw#include <stdio.h> 41183218Skaiw#include <stdlib.h> 42183218Skaiw#include <string.h> 43183218Skaiw#include <sysexits.h> 44183218Skaiw#include <unistd.h> 45183218Skaiw 46183218Skaiw#include "ar.h" 47183218Skaiw 48183218Skaiw#define TEMPLATE "arscp.XXXXXXXX" 49183218Skaiw 50183218Skaiwstruct list { 51183218Skaiw char *str; 52183218Skaiw struct list *next; 53183218Skaiw}; 54183218Skaiw 55183218Skaiw 56183218Skaiwextern int yylex(void); 57183218Skaiw 58183218Skaiwstatic void yyerror(const char *); 59183218Skaiwstatic void arscp_addlib(char *archive, struct list *list); 60183218Skaiwstatic void arscp_addmod(struct list *list); 61183218Skaiwstatic void arscp_clear(void); 62183218Skaiwstatic int arscp_copy(int ifd, int ofd); 63183218Skaiwstatic void arscp_create(char *in, char *out); 64183218Skaiwstatic void arscp_delete(struct list *list); 65183218Skaiwstatic void arscp_dir(char *archive, struct list *list, char *rlt); 66183218Skaiwstatic void arscp_end(int eval); 67183218Skaiwstatic void arscp_extract(struct list *list); 68183218Skaiwstatic void arscp_free_argv(void); 69183218Skaiwstatic void arscp_free_mlist(struct list *list); 70183218Skaiwstatic void arscp_list(void); 71183218Skaiwstatic struct list *arscp_mlist(struct list *list, char *str); 72183218Skaiwstatic void arscp_mlist2argv(struct list *list); 73183218Skaiwstatic int arscp_mlist_len(struct list *list); 74183218Skaiwstatic void arscp_open(char *fname); 75183218Skaiwstatic void arscp_prompt(void); 76183218Skaiwstatic void arscp_replace(struct list *list); 77183218Skaiwstatic void arscp_save(void); 78183218Skaiwstatic int arscp_target_exist(void); 79183218Skaiw 80183218Skaiwextern int lineno; 81183218Skaiw 82183218Skaiwstatic struct bsdar *bsdar; 83183218Skaiwstatic char *target; 84183218Skaiwstatic char *tmpac; 85183218Skaiwstatic int interactive; 86183218Skaiwstatic int verbose; 87183218Skaiw 88183218Skaiw%} 89183218Skaiw 90183218Skaiw%token ADDLIB 91183218Skaiw%token ADDMOD 92183218Skaiw%token CLEAR 93183218Skaiw%token CREATE 94183218Skaiw%token DELETE 95183218Skaiw%token DIRECTORY 96183218Skaiw%token END 97183218Skaiw%token EXTRACT 98183218Skaiw%token LIST 99183218Skaiw%token OPEN 100183218Skaiw%token REPLACE 101183218Skaiw%token VERBOSE 102183218Skaiw%token SAVE 103183218Skaiw%token LP 104183218Skaiw%token RP 105183218Skaiw%token COMMA 106183218Skaiw%token EOL 107183218Skaiw%token <str> FNAME 108183218Skaiw%type <list> mod_list 109183218Skaiw 110183218Skaiw%union { 111183218Skaiw char *str; 112183218Skaiw struct list *list; 113183218Skaiw} 114183218Skaiw 115183218Skaiw%% 116183218Skaiw 117183218Skaiwbegin 118183218Skaiw : { arscp_prompt(); } ar_script 119183218Skaiw ; 120183218Skaiw 121183218Skaiwar_script 122183218Skaiw : cmd_list 123183218Skaiw | 124183218Skaiw ; 125183218Skaiw 126183218Skaiwmod_list 127183218Skaiw : FNAME { $$ = arscp_mlist(NULL, $1); } 128183218Skaiw | mod_list separator FNAME { $$ = arscp_mlist($1, $3); } 129183218Skaiw ; 130183218Skaiw 131183218Skaiwseparator 132183218Skaiw : COMMA 133183218Skaiw | 134183218Skaiw ; 135183218Skaiw 136183218Skaiwcmd_list 137183218Skaiw : rawcmd 138183218Skaiw | cmd_list rawcmd 139183218Skaiw ; 140183218Skaiw 141183218Skaiwrawcmd 142183218Skaiw : cmd EOL { arscp_prompt(); } 143183218Skaiw ; 144183218Skaiw 145183218Skaiwcmd 146183218Skaiw : addlib_cmd 147183218Skaiw | addmod_cmd 148183218Skaiw | clear_cmd 149183218Skaiw | create_cmd 150183218Skaiw | delete_cmd 151183218Skaiw | directory_cmd 152183218Skaiw | end_cmd 153183218Skaiw | extract_cmd 154183218Skaiw | list_cmd 155183218Skaiw | open_cmd 156183218Skaiw | replace_cmd 157183218Skaiw | verbose_cmd 158183218Skaiw | save_cmd 159183218Skaiw | invalid_cmd 160183218Skaiw | empty_cmd 161183218Skaiw | error 162183218Skaiw ; 163183218Skaiw 164183218Skaiwaddlib_cmd 165183218Skaiw : ADDLIB FNAME LP mod_list RP { arscp_addlib($2, $4); } 166183218Skaiw | ADDLIB FNAME { arscp_addlib($2, NULL); } 167183218Skaiw ; 168183218Skaiw 169183218Skaiwaddmod_cmd 170183218Skaiw : ADDMOD mod_list { arscp_addmod($2); } 171183218Skaiw ; 172183218Skaiw 173183218Skaiwclear_cmd 174183218Skaiw : CLEAR { arscp_clear(); } 175183218Skaiw ; 176183218Skaiw 177183218Skaiwcreate_cmd 178183218Skaiw : CREATE FNAME { arscp_create(NULL, $2); } 179183218Skaiw ; 180183218Skaiw 181183218Skaiwdelete_cmd 182183218Skaiw : DELETE mod_list { arscp_delete($2); } 183183218Skaiw ; 184183218Skaiw 185183218Skaiwdirectory_cmd 186183218Skaiw : DIRECTORY FNAME { arscp_dir($2, NULL, NULL); } 187183218Skaiw | DIRECTORY FNAME LP mod_list RP { arscp_dir($2, $4, NULL); } 188183218Skaiw | DIRECTORY FNAME LP mod_list RP FNAME { arscp_dir($2, $4, $6); } 189183218Skaiw ; 190183218Skaiw 191183218Skaiwend_cmd 192183218Skaiw : END { arscp_end(EX_OK); } 193183218Skaiw ; 194183218Skaiw 195183218Skaiwextract_cmd 196183218Skaiw : EXTRACT mod_list { arscp_extract($2); } 197183218Skaiw ; 198183218Skaiw 199183218Skaiwlist_cmd 200183218Skaiw : LIST { arscp_list(); } 201183218Skaiw ; 202183218Skaiw 203183218Skaiwopen_cmd 204183218Skaiw : OPEN FNAME { arscp_open($2); } 205183218Skaiw ; 206183218Skaiw 207183218Skaiwreplace_cmd 208183218Skaiw : REPLACE mod_list { arscp_replace($2); } 209183218Skaiw ; 210183218Skaiw 211183218Skaiwsave_cmd 212183218Skaiw : SAVE { arscp_save(); } 213183218Skaiw ; 214183218Skaiw 215183218Skaiwverbose_cmd 216183218Skaiw : VERBOSE { verbose = !verbose; } 217183218Skaiw ; 218183218Skaiw 219183218Skaiwempty_cmd 220183218Skaiw : 221183218Skaiw ; 222183218Skaiw 223183218Skaiwinvalid_cmd 224183218Skaiw : FNAME { yyerror(NULL); } 225183218Skaiw ; 226183218Skaiw 227183218Skaiw%% 228183218Skaiw 229183218Skaiw/* ARGSUSED */ 230183218Skaiwstatic void 231183218Skaiwyyerror(const char *s) 232183218Skaiw{ 233183218Skaiw 234183218Skaiw (void) s; 235183218Skaiw printf("Syntax error in archive script, line %d\n", lineno); 236183218Skaiw} 237183218Skaiw 238183218Skaiw/* 239183218Skaiw * arscp_open first open an archive and check its validity. If the archive 240183218Skaiw * format is valid, it calls arscp_create to create a temporary copy of 241183218Skaiw * the archive. 242183218Skaiw */ 243183218Skaiwstatic void 244183218Skaiwarscp_open(char *fname) 245183218Skaiw{ 246183218Skaiw struct archive *a; 247183218Skaiw struct archive_entry *entry; 248183218Skaiw int r; 249183218Skaiw 250183218Skaiw if ((a = archive_read_new()) == NULL) 251183218Skaiw bsdar_errc(bsdar, EX_SOFTWARE, 0, "archive_read_new failed"); 252201166Skientzle archive_read_support_format_ar(a); 253248612Smm AC(archive_read_open_filename(a, fname, DEF_BLKSZ)); 254183218Skaiw if ((r = archive_read_next_header(a, &entry))) 255183218Skaiw bsdar_warnc(bsdar, 0, "%s", archive_error_string(a)); 256183218Skaiw AC(archive_read_close(a)); 257248612Smm AC(archive_read_free(a)); 258183218Skaiw if (r != ARCHIVE_OK) 259183218Skaiw return; 260183218Skaiw arscp_create(fname, fname); 261183218Skaiw} 262183218Skaiw 263183218Skaiw/* 264183218Skaiw * Create archive. in != NULL indicate it's a OPEN cmd, and resulting 265183218Skaiw * archive is based on modification of an existing one. If in == NULL, 266183218Skaiw * we are in CREATE cmd and a new empty archive will be created. 267183218Skaiw */ 268183218Skaiwstatic void 269183218Skaiwarscp_create(char *in, char *out) 270183218Skaiw{ 271183218Skaiw struct archive *a; 272183218Skaiw int ifd, ofd; 273183218Skaiw 274183218Skaiw /* Delete previously created temporary archive, if any. */ 275183218Skaiw if (tmpac) { 276183218Skaiw if (unlink(tmpac) < 0) 277183218Skaiw bsdar_errc(bsdar, EX_IOERR, errno, "unlink failed"); 278183218Skaiw free(tmpac); 279183218Skaiw } 280183218Skaiw 281183218Skaiw tmpac = strdup(TEMPLATE); 282183218Skaiw if (tmpac == NULL) 283183218Skaiw bsdar_errc(bsdar, EX_SOFTWARE, errno, "strdup failed"); 284183218Skaiw if ((ofd = mkstemp(tmpac)) < 0) 285183218Skaiw bsdar_errc(bsdar, EX_IOERR, errno, "mkstemp failed"); 286183218Skaiw 287183218Skaiw if (in) { 288183218Skaiw /* 289183218Skaiw * Command OPEN creates a temporary copy of the 290183218Skaiw * input archive. 291183218Skaiw */ 292183218Skaiw if ((ifd = open(in, O_RDONLY)) < 0) { 293183218Skaiw bsdar_warnc(bsdar, errno, "open failed"); 294183218Skaiw return; 295183218Skaiw } 296183218Skaiw if (arscp_copy(ifd, ofd)) { 297183218Skaiw bsdar_warnc(bsdar, 0, "arscp_copy failed"); 298183218Skaiw return; 299183218Skaiw } 300183218Skaiw close(ifd); 301183218Skaiw close(ofd); 302183218Skaiw } else { 303183218Skaiw /* 304183218Skaiw * Command CREATE creates an "empty" archive. 305183218Skaiw * (archive with only global header) 306183218Skaiw */ 307183218Skaiw if ((a = archive_write_new()) == NULL) 308183218Skaiw bsdar_errc(bsdar, EX_SOFTWARE, 0, 309183218Skaiw "archive_write_new failed"); 310183218Skaiw archive_write_set_format_ar_svr4(a); 311183218Skaiw AC(archive_write_open_fd(a, ofd)); 312183218Skaiw AC(archive_write_close(a)); 313248612Smm AC(archive_write_free(a)); 314183218Skaiw } 315183218Skaiw 316183218Skaiw /* Override previous target, if any. */ 317183218Skaiw if (target) 318183218Skaiw free(target); 319183218Skaiw 320183218Skaiw target = out; 321183218Skaiw bsdar->filename = tmpac; 322183218Skaiw} 323183218Skaiw 324183218Skaiw/* A file copying implementation using mmap. */ 325183218Skaiwstatic int 326183218Skaiwarscp_copy(int ifd, int ofd) 327183218Skaiw{ 328183218Skaiw struct stat sb; 329183218Skaiw char *buf, *p; 330183218Skaiw ssize_t w; 331183218Skaiw size_t bytes; 332183218Skaiw 333183218Skaiw if (fstat(ifd, &sb) < 0) { 334183218Skaiw bsdar_warnc(bsdar, errno, "fstate failed"); 335183218Skaiw return (1); 336183218Skaiw } 337183218Skaiw if ((p = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, ifd, 338183218Skaiw (off_t)0)) == MAP_FAILED) { 339183218Skaiw bsdar_warnc(bsdar, errno, "mmap failed"); 340183218Skaiw return (1); 341183218Skaiw } 342183218Skaiw for (buf = p, bytes = sb.st_size; bytes > 0; bytes -= w) { 343183218Skaiw w = write(ofd, buf, bytes); 344183218Skaiw if (w <= 0) { 345183218Skaiw bsdar_warnc(bsdar, errno, "write failed"); 346183218Skaiw break; 347183218Skaiw } 348183218Skaiw } 349183218Skaiw if (munmap(p, sb.st_size) < 0) 350183218Skaiw bsdar_errc(bsdar, EX_SOFTWARE, errno, "munmap failed"); 351183218Skaiw if (bytes > 0) 352183218Skaiw return (1); 353183218Skaiw 354183218Skaiw return (0); 355183218Skaiw} 356183218Skaiw 357183218Skaiw/* 358183218Skaiw * Add all modules of archive to current archive, if list != NULL, 359222122Sbcr * only those modules specified in 'list' will be added. 360183218Skaiw */ 361183218Skaiwstatic void 362183218Skaiwarscp_addlib(char *archive, struct list *list) 363183218Skaiw{ 364183218Skaiw 365183218Skaiw if (!arscp_target_exist()) 366183218Skaiw return; 367183218Skaiw arscp_mlist2argv(list); 368183218Skaiw bsdar->addlib = archive; 369183218Skaiw ar_mode_A(bsdar); 370183218Skaiw arscp_free_argv(); 371183218Skaiw arscp_free_mlist(list); 372183218Skaiw} 373183218Skaiw 374183218Skaiw/* Add modules into current archive. */ 375183218Skaiwstatic void 376183218Skaiwarscp_addmod(struct list *list) 377183218Skaiw{ 378183218Skaiw 379183218Skaiw if (!arscp_target_exist()) 380183218Skaiw return; 381183218Skaiw arscp_mlist2argv(list); 382183218Skaiw ar_mode_q(bsdar); 383183218Skaiw arscp_free_argv(); 384183218Skaiw arscp_free_mlist(list); 385183218Skaiw} 386183218Skaiw 387183218Skaiw/* Delete modules from current archive. */ 388183218Skaiwstatic void 389183218Skaiwarscp_delete(struct list *list) 390183218Skaiw{ 391183218Skaiw 392183218Skaiw if (!arscp_target_exist()) 393183218Skaiw return; 394183218Skaiw arscp_mlist2argv(list); 395183218Skaiw ar_mode_d(bsdar); 396183218Skaiw arscp_free_argv(); 397183218Skaiw arscp_free_mlist(list); 398183218Skaiw} 399183218Skaiw 400183218Skaiw/* Extract modules from current archive. */ 401183218Skaiwstatic void 402183218Skaiwarscp_extract(struct list *list) 403183218Skaiw{ 404183218Skaiw 405183218Skaiw if (!arscp_target_exist()) 406183218Skaiw return; 407183218Skaiw arscp_mlist2argv(list); 408183218Skaiw ar_mode_x(bsdar); 409183218Skaiw arscp_free_argv(); 410183218Skaiw arscp_free_mlist(list); 411183218Skaiw} 412183218Skaiw 413183218Skaiw/* List modules of archive. (Simple Mode) */ 414183218Skaiwstatic void 415201382Sedarscp_list(void) 416183218Skaiw{ 417183218Skaiw 418183218Skaiw if (!arscp_target_exist()) 419183218Skaiw return; 420183218Skaiw bsdar->argc = 0; 421183218Skaiw bsdar->argv = NULL; 422183218Skaiw /* Always verbose. */ 423183218Skaiw bsdar->options |= AR_V; 424183218Skaiw ar_mode_t(bsdar); 425183218Skaiw bsdar->options &= ~AR_V; 426183218Skaiw} 427183218Skaiw 428183218Skaiw/* List modules of archive. (Advance Mode) */ 429183218Skaiwstatic void 430183218Skaiwarscp_dir(char *archive, struct list *list, char *rlt) 431183218Skaiw{ 432183218Skaiw FILE *out; 433183218Skaiw 434183218Skaiw /* If rlt != NULL, redirect output to it */ 435183218Skaiw out = NULL; 436183218Skaiw if (rlt) { 437183218Skaiw out = stdout; 438183218Skaiw if ((stdout = fopen(rlt, "w")) == NULL) 439183218Skaiw bsdar_errc(bsdar, EX_IOERR, errno, 440183218Skaiw "fopen %s failed", rlt); 441183218Skaiw } 442183218Skaiw 443183218Skaiw bsdar->filename = archive; 444183218Skaiw if (list) 445183218Skaiw arscp_mlist2argv(list); 446183218Skaiw else { 447183218Skaiw bsdar->argc = 0; 448183218Skaiw bsdar->argv = NULL; 449183218Skaiw } 450183218Skaiw if (verbose) 451183218Skaiw bsdar->options |= AR_V; 452183218Skaiw ar_mode_t(bsdar); 453183218Skaiw bsdar->options &= ~AR_V; 454183218Skaiw 455183218Skaiw if (rlt) { 456183218Skaiw if (fclose(stdout) == EOF) 457183218Skaiw bsdar_errc(bsdar, EX_IOERR, errno, 458183218Skaiw "fclose %s failed", rlt); 459183218Skaiw stdout = out; 460183218Skaiw free(rlt); 461183218Skaiw } 462183218Skaiw free(archive); 463183218Skaiw bsdar->filename = tmpac; 464183218Skaiw arscp_free_argv(); 465183218Skaiw arscp_free_mlist(list); 466183218Skaiw} 467183218Skaiw 468183218Skaiw 469183218Skaiw/* Replace modules of current archive. */ 470183218Skaiwstatic void 471183218Skaiwarscp_replace(struct list *list) 472183218Skaiw{ 473183218Skaiw 474183218Skaiw if (!arscp_target_exist()) 475183218Skaiw return; 476183218Skaiw arscp_mlist2argv(list); 477183218Skaiw ar_mode_r(bsdar); 478183218Skaiw arscp_free_argv(); 479183218Skaiw arscp_free_mlist(list); 480183218Skaiw} 481183218Skaiw 482183218Skaiw/* Rename the temporary archive to the target archive. */ 483183218Skaiwstatic void 484201382Sedarscp_save(void) 485183218Skaiw{ 486183218Skaiw mode_t mask; 487183218Skaiw 488183218Skaiw if (target) { 489183218Skaiw if (rename(tmpac, target) < 0) 490183218Skaiw bsdar_errc(bsdar, EX_IOERR, errno, "rename failed"); 491183218Skaiw /* 492183218Skaiw * mkstemp creates temp files with mode 0600, here we 493183218Skaiw * set target archive mode per process umask. 494183218Skaiw */ 495183218Skaiw mask = umask(0); 496183218Skaiw umask(mask); 497183218Skaiw if (chmod(target, 0666 & ~mask) < 0) 498183218Skaiw bsdar_errc(bsdar, EX_IOERR, errno, "chmod failed"); 499183218Skaiw free(tmpac); 500183218Skaiw free(target); 501183218Skaiw tmpac = NULL; 502183218Skaiw target= NULL; 503183218Skaiw bsdar->filename = NULL; 504183218Skaiw } else 505183218Skaiw bsdar_warnc(bsdar, 0, "no open output archive"); 506183218Skaiw} 507183218Skaiw 508183218Skaiw/* 509183218Skaiw * Discard all the contents of current archive. This is achieved by 510183218Skaiw * invoking CREATE cmd on current archive. 511183218Skaiw */ 512183218Skaiwstatic void 513201382Sedarscp_clear(void) 514183218Skaiw{ 515183218Skaiw char *new_target; 516183218Skaiw 517183218Skaiw if (target) { 518183218Skaiw new_target = strdup(target); 519183218Skaiw if (new_target == NULL) 520183218Skaiw bsdar_errc(bsdar, EX_SOFTWARE, errno, "strdup failed"); 521183218Skaiw arscp_create(NULL, new_target); 522183218Skaiw } 523183218Skaiw} 524183218Skaiw 525183218Skaiw/* 526183218Skaiw * Quit ar(1). Note that END cmd will not SAVE current archive 527183218Skaiw * before exit. 528183218Skaiw */ 529183218Skaiwstatic void 530183218Skaiwarscp_end(int eval) 531183218Skaiw{ 532183218Skaiw 533183218Skaiw if (target) 534183218Skaiw free(target); 535183218Skaiw if (tmpac) { 536183218Skaiw if (unlink(tmpac) == -1) 537183218Skaiw bsdar_errc(bsdar, EX_IOERR, errno, "unlink %s failed", 538183218Skaiw tmpac); 539183218Skaiw free(tmpac); 540183218Skaiw } 541183218Skaiw 542183218Skaiw exit(eval); 543183218Skaiw} 544183218Skaiw 545183218Skaiw/* 546222122Sbcr * Check if target specified, i.e, whether OPEN or CREATE has been 547183218Skaiw * issued by user. 548183218Skaiw */ 549183218Skaiwstatic int 550201382Sedarscp_target_exist(void) 551183218Skaiw{ 552183218Skaiw 553183218Skaiw if (target) 554183218Skaiw return (1); 555183218Skaiw 556183218Skaiw bsdar_warnc(bsdar, 0, "no open output archive"); 557183218Skaiw return (0); 558183218Skaiw} 559183218Skaiw 560183218Skaiw/* Construct module list. */ 561183218Skaiwstatic struct list * 562183218Skaiwarscp_mlist(struct list *list, char *str) 563183218Skaiw{ 564183218Skaiw struct list *l; 565183218Skaiw 566183218Skaiw l = malloc(sizeof(*l)); 567183218Skaiw if (l == NULL) 568183218Skaiw bsdar_errc(bsdar, EX_SOFTWARE, errno, "malloc failed"); 569183218Skaiw l->str = str; 570183218Skaiw l->next = list; 571183218Skaiw 572183218Skaiw return (l); 573183218Skaiw} 574183218Skaiw 575183218Skaiw/* Calculate the length of a mlist. */ 576183218Skaiwstatic int 577183218Skaiwarscp_mlist_len(struct list *list) 578183218Skaiw{ 579183218Skaiw int len; 580183218Skaiw 581183218Skaiw for(len = 0; list; list = list->next) 582183218Skaiw len++; 583183218Skaiw 584183218Skaiw return (len); 585183218Skaiw} 586183218Skaiw 587183218Skaiw/* Free the space allocated for mod_list. */ 588183218Skaiwstatic void 589183218Skaiwarscp_free_mlist(struct list *list) 590183218Skaiw{ 591183218Skaiw struct list *l; 592183218Skaiw 593183218Skaiw /* Note that list->str was freed in arscp_free_argv. */ 594183218Skaiw for(; list; list = l) { 595183218Skaiw l = list->next; 596183218Skaiw free(list); 597183218Skaiw } 598183218Skaiw} 599183218Skaiw 600183218Skaiw/* Convert mlist to argv array. */ 601183218Skaiwstatic void 602183218Skaiwarscp_mlist2argv(struct list *list) 603183218Skaiw{ 604183218Skaiw char **argv; 605183218Skaiw int i, n; 606183218Skaiw 607183218Skaiw n = arscp_mlist_len(list); 608183218Skaiw argv = malloc(n * sizeof(*argv)); 609183218Skaiw if (argv == NULL) 610183218Skaiw bsdar_errc(bsdar, EX_SOFTWARE, errno, "malloc failed"); 611183218Skaiw 612183218Skaiw /* Note that module names are stored in reverse order in mlist. */ 613183218Skaiw for(i = n - 1; i >= 0; i--, list = list->next) { 614183218Skaiw if (list == NULL) 615183218Skaiw bsdar_errc(bsdar, EX_SOFTWARE, errno, "invalid mlist"); 616183218Skaiw argv[i] = list->str; 617183218Skaiw } 618183218Skaiw 619183218Skaiw bsdar->argc = n; 620183218Skaiw bsdar->argv = argv; 621183218Skaiw} 622183218Skaiw 623183218Skaiw/* Free space allocated for argv array and its elements. */ 624183218Skaiwstatic void 625201382Sedarscp_free_argv(void) 626183218Skaiw{ 627183218Skaiw int i; 628183218Skaiw 629183218Skaiw for(i = 0; i < bsdar->argc; i++) 630183218Skaiw free(bsdar->argv[i]); 631183218Skaiw 632183218Skaiw free(bsdar->argv); 633183218Skaiw} 634183218Skaiw 635183218Skaiw/* Show a prompt if we are in interactive mode */ 636183218Skaiwstatic void 637201382Sedarscp_prompt(void) 638183218Skaiw{ 639183218Skaiw 640183218Skaiw if (interactive) { 641183218Skaiw printf("AR >"); 642183218Skaiw fflush(stdout); 643183218Skaiw } 644183218Skaiw} 645183218Skaiw 646183218Skaiw/* Main function for ar script mode. */ 647183218Skaiwvoid 648183218Skaiwar_mode_script(struct bsdar *ar) 649183218Skaiw{ 650183218Skaiw 651183218Skaiw bsdar = ar; 652183218Skaiw interactive = isatty(fileno(stdin)); 653183218Skaiw while(yyparse()) { 654183218Skaiw if (!interactive) 655183218Skaiw arscp_end(1); 656183218Skaiw } 657183218Skaiw 658183218Skaiw /* Script ends without END */ 659183218Skaiw arscp_end(EX_OK); 660183218Skaiw} 661