summaryrefslogtreecommitdiffstats
path: root/usr.bin/ar
diff options
context:
space:
mode:
authorkaiw <kaiw@FreeBSD.org>2008-09-20 22:10:10 +0000
committerkaiw <kaiw@FreeBSD.org>2008-09-20 22:10:10 +0000
commit96088079e9423afdfe848a84a64b47524a619d1a (patch)
treeaa14ca07aff07354774c652e9d3151ae72a67b98 /usr.bin/ar
parentfb5e3c134323e57c3edf3ab69f73ddf6edd4168e (diff)
downloadFreeBSD-src-96088079e9423afdfe848a84a64b47524a619d1a.zip
FreeBSD-src-96088079e9423afdfe848a84a64b47524a619d1a.tar.gz
Add support for option "-M", which is used to operate ar(1) in a
script mode like the MRI(Microtec Research Inc.) "librarian" program. Originally this option is provided by Binutils ar(1) to ease the transition for developers who are used to writing "librarian" scripts. We added this option to BSD ar(1) because: 1. Further improve the compatibility with Binutils ar(1). 2. There are still a few software using this -M option. (at least one in our ports collection) Suggested by: rink & erwin
Diffstat (limited to 'usr.bin/ar')
-rw-r--r--usr.bin/ar/Makefile2
-rw-r--r--usr.bin/ar/acplex.l78
-rw-r--r--usr.bin/ar/acpyacc.y662
-rw-r--r--usr.bin/ar/ar.c12
-rw-r--r--usr.bin/ar/ar.h9
-rw-r--r--usr.bin/ar/write.c148
6 files changed, 864 insertions, 47 deletions
diff --git a/usr.bin/ar/Makefile b/usr.bin/ar/Makefile
index 1639ed5..a02c128 100644
--- a/usr.bin/ar/Makefile
+++ b/usr.bin/ar/Makefile
@@ -1,7 +1,7 @@
# $FreeBSD$
PROG= ar
-SRCS= ar.c read.c util.c write.c
+SRCS= ar.c acplex.l acpyacc.y read.c util.c write.c y.tab.h
WARNS?= 5
diff --git a/usr.bin/ar/acplex.l b/usr.bin/ar/acplex.l
new file mode 100644
index 0000000..3186d17
--- /dev/null
+++ b/usr.bin/ar/acplex.l
@@ -0,0 +1,78 @@
+%{
+/*-
+ * Copyright (c) 2008 Kai Wang
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <sysexits.h>
+
+#include "y.tab.h"
+
+#define YY_NO_UNPUT
+int lineno = 1;
+
+int yylex(void);
+
+%}
+
+%option noyywrap
+
+%%
+
+ADDLIB|addlib { return (ADDLIB); }
+ADDMOD|addmod { return (ADDMOD); }
+CLEAR|clear { return (CLEAR); }
+CREATE|create { return (CREATE); }
+DELETE|delete { return (DELETE); }
+DIRECTORY|directory { return (DIRECTORY); }
+END|end { return (END); }
+EXTRACT|extract { return (EXTRACT); }
+LIST|list { return (LIST); }
+OPEN|open { return (OPEN); }
+REPLACE|replace { return (REPLACE); }
+VERBOSE|verbose { return (VERBOSE); }
+SAVE|save { return (SAVE); }
+"(" { return (LP); }
+")" { return (RP); }
+"," { return (COMMA); }
+
+[-_A-Za-z0-9/:$.\\]+ {
+ yylval.str = strdup(yytext);
+ if (yylval.str == NULL)
+ errc(EX_SOFTWARE, errno, "strdup failed");
+ return (FNAME);
+}
+
+[ \t] /* whitespace */
+"*".* /* comment */
+";".* /* comment */
+"+\n" { lineno++; /* '+' is line continuation char */ }
+"\n" { lineno++; return (EOL); }
diff --git a/usr.bin/ar/acpyacc.y b/usr.bin/ar/acpyacc.y
new file mode 100644
index 0000000..bc25b62
--- /dev/null
+++ b/usr.bin/ar/acpyacc.y
@@ -0,0 +1,662 @@
+%{
+/*-
+ * Copyright (c) 2008 Kai Wang
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <archive.h>
+#include <archive_entry.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "ar.h"
+
+#define TEMPLATE "arscp.XXXXXXXX"
+
+struct list {
+ char *str;
+ struct list *next;
+};
+
+
+extern int yylex(void);
+extern int yyparse(void);
+
+static void yyerror(const char *);
+static void arscp_addlib(char *archive, struct list *list);
+static void arscp_addmod(struct list *list);
+static void arscp_clear(void);
+static int arscp_copy(int ifd, int ofd);
+static void arscp_create(char *in, char *out);
+static void arscp_delete(struct list *list);
+static void arscp_dir(char *archive, struct list *list, char *rlt);
+static void arscp_end(int eval);
+static void arscp_extract(struct list *list);
+static void arscp_free_argv(void);
+static void arscp_free_mlist(struct list *list);
+static void arscp_list(void);
+static struct list *arscp_mlist(struct list *list, char *str);
+static void arscp_mlist2argv(struct list *list);
+static int arscp_mlist_len(struct list *list);
+static void arscp_open(char *fname);
+static void arscp_prompt(void);
+static void arscp_replace(struct list *list);
+static void arscp_save(void);
+static int arscp_target_exist(void);
+
+extern int lineno;
+
+static struct bsdar *bsdar;
+static char *target;
+static char *tmpac;
+static int interactive;
+static int verbose;
+
+%}
+
+%token ADDLIB
+%token ADDMOD
+%token CLEAR
+%token CREATE
+%token DELETE
+%token DIRECTORY
+%token END
+%token EXTRACT
+%token LIST
+%token OPEN
+%token REPLACE
+%token VERBOSE
+%token SAVE
+%token LP
+%token RP
+%token COMMA
+%token EOL
+%token <str> FNAME
+%type <list> mod_list
+
+%union {
+ char *str;
+ struct list *list;
+}
+
+%%
+
+begin
+ : { arscp_prompt(); } ar_script
+ ;
+
+ar_script
+ : cmd_list
+ |
+ ;
+
+mod_list
+ : FNAME { $$ = arscp_mlist(NULL, $1); }
+ | mod_list separator FNAME { $$ = arscp_mlist($1, $3); }
+ ;
+
+separator
+ : COMMA
+ |
+ ;
+
+cmd_list
+ : rawcmd
+ | cmd_list rawcmd
+ ;
+
+rawcmd
+ : cmd EOL { arscp_prompt(); }
+ ;
+
+cmd
+ : addlib_cmd
+ | addmod_cmd
+ | clear_cmd
+ | create_cmd
+ | delete_cmd
+ | directory_cmd
+ | end_cmd
+ | extract_cmd
+ | list_cmd
+ | open_cmd
+ | replace_cmd
+ | verbose_cmd
+ | save_cmd
+ | invalid_cmd
+ | empty_cmd
+ | error
+ ;
+
+addlib_cmd
+ : ADDLIB FNAME LP mod_list RP { arscp_addlib($2, $4); }
+ | ADDLIB FNAME { arscp_addlib($2, NULL); }
+ ;
+
+addmod_cmd
+ : ADDMOD mod_list { arscp_addmod($2); }
+ ;
+
+clear_cmd
+ : CLEAR { arscp_clear(); }
+ ;
+
+create_cmd
+ : CREATE FNAME { arscp_create(NULL, $2); }
+ ;
+
+delete_cmd
+ : DELETE mod_list { arscp_delete($2); }
+ ;
+
+directory_cmd
+ : DIRECTORY FNAME { arscp_dir($2, NULL, NULL); }
+ | DIRECTORY FNAME LP mod_list RP { arscp_dir($2, $4, NULL); }
+ | DIRECTORY FNAME LP mod_list RP FNAME { arscp_dir($2, $4, $6); }
+ ;
+
+end_cmd
+ : END { arscp_end(EX_OK); }
+ ;
+
+extract_cmd
+ : EXTRACT mod_list { arscp_extract($2); }
+ ;
+
+list_cmd
+ : LIST { arscp_list(); }
+ ;
+
+open_cmd
+ : OPEN FNAME { arscp_open($2); }
+ ;
+
+replace_cmd
+ : REPLACE mod_list { arscp_replace($2); }
+ ;
+
+save_cmd
+ : SAVE { arscp_save(); }
+ ;
+
+verbose_cmd
+ : VERBOSE { verbose = !verbose; }
+ ;
+
+empty_cmd
+ :
+ ;
+
+invalid_cmd
+ : FNAME { yyerror(NULL); }
+ ;
+
+%%
+
+/* ARGSUSED */
+static void
+yyerror(const char *s)
+{
+
+ (void) s;
+ printf("Syntax error in archive script, line %d\n", lineno);
+}
+
+/*
+ * arscp_open first open an archive and check its validity. If the archive
+ * format is valid, it calls arscp_create to create a temporary copy of
+ * the archive.
+ */
+static void
+arscp_open(char *fname)
+{
+ struct archive *a;
+ struct archive_entry *entry;
+ int r;
+
+ if ((a = archive_read_new()) == NULL)
+ bsdar_errc(bsdar, EX_SOFTWARE, 0, "archive_read_new failed");
+ archive_read_support_compression_all(a);
+ archive_read_support_format_all(a);
+ AC(archive_read_open_file(a, fname, DEF_BLKSZ));
+ if ((r = archive_read_next_header(a, &entry)))
+ bsdar_warnc(bsdar, 0, "%s", archive_error_string(a));
+ AC(archive_read_close(a));
+ AC(archive_read_finish(a));
+ if (r != ARCHIVE_OK)
+ return;
+ arscp_create(fname, fname);
+}
+
+/*
+ * Create archive. in != NULL indicate it's a OPEN cmd, and resulting
+ * archive is based on modification of an existing one. If in == NULL,
+ * we are in CREATE cmd and a new empty archive will be created.
+ */
+static void
+arscp_create(char *in, char *out)
+{
+ struct archive *a;
+ int ifd, ofd;
+
+ /* Delete previously created temporary archive, if any. */
+ if (tmpac) {
+ if (unlink(tmpac) < 0)
+ bsdar_errc(bsdar, EX_IOERR, errno, "unlink failed");
+ free(tmpac);
+ }
+
+ tmpac = strdup(TEMPLATE);
+ if (tmpac == NULL)
+ bsdar_errc(bsdar, EX_SOFTWARE, errno, "strdup failed");
+ if ((ofd = mkstemp(tmpac)) < 0)
+ bsdar_errc(bsdar, EX_IOERR, errno, "mkstemp failed");
+
+ if (in) {
+ /*
+ * Command OPEN creates a temporary copy of the
+ * input archive.
+ */
+ if ((ifd = open(in, O_RDONLY)) < 0) {
+ bsdar_warnc(bsdar, errno, "open failed");
+ return;
+ }
+ if (arscp_copy(ifd, ofd)) {
+ bsdar_warnc(bsdar, 0, "arscp_copy failed");
+ return;
+ }
+ close(ifd);
+ close(ofd);
+ } else {
+ /*
+ * Command CREATE creates an "empty" archive.
+ * (archive with only global header)
+ */
+ if ((a = archive_write_new()) == NULL)
+ bsdar_errc(bsdar, EX_SOFTWARE, 0,
+ "archive_write_new failed");
+ archive_write_set_format_ar_svr4(a);
+ AC(archive_write_open_fd(a, ofd));
+ AC(archive_write_close(a));
+ AC(archive_write_finish(a));
+ }
+
+ /* Override previous target, if any. */
+ if (target)
+ free(target);
+
+ target = out;
+ bsdar->filename = tmpac;
+}
+
+/* A file copying implementation using mmap. */
+static int
+arscp_copy(int ifd, int ofd)
+{
+ struct stat sb;
+ char *buf, *p;
+ ssize_t w;
+ size_t bytes;
+
+ if (fstat(ifd, &sb) < 0) {
+ bsdar_warnc(bsdar, errno, "fstate failed");
+ return (1);
+ }
+ if ((p = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, ifd,
+ (off_t)0)) == MAP_FAILED) {
+ bsdar_warnc(bsdar, errno, "mmap failed");
+ return (1);
+ }
+ for (buf = p, bytes = sb.st_size; bytes > 0; bytes -= w) {
+ w = write(ofd, buf, bytes);
+ if (w <= 0) {
+ bsdar_warnc(bsdar, errno, "write failed");
+ break;
+ }
+ }
+ if (munmap(p, sb.st_size) < 0)
+ bsdar_errc(bsdar, EX_SOFTWARE, errno, "munmap failed");
+ if (bytes > 0)
+ return (1);
+
+ return (0);
+}
+
+/*
+ * Add all modules of archive to current archive, if list != NULL,
+ * only those modules speicifed in 'list' will be added.
+ */
+static void
+arscp_addlib(char *archive, struct list *list)
+{
+
+ if (!arscp_target_exist())
+ return;
+ arscp_mlist2argv(list);
+ bsdar->addlib = archive;
+ ar_mode_A(bsdar);
+ arscp_free_argv();
+ arscp_free_mlist(list);
+}
+
+/* Add modules into current archive. */
+static void
+arscp_addmod(struct list *list)
+{
+
+ if (!arscp_target_exist())
+ return;
+ arscp_mlist2argv(list);
+ ar_mode_q(bsdar);
+ arscp_free_argv();
+ arscp_free_mlist(list);
+}
+
+/* Delete modules from current archive. */
+static void
+arscp_delete(struct list *list)
+{
+
+ if (!arscp_target_exist())
+ return;
+ arscp_mlist2argv(list);
+ ar_mode_d(bsdar);
+ arscp_free_argv();
+ arscp_free_mlist(list);
+}
+
+/* Extract modules from current archive. */
+static void
+arscp_extract(struct list *list)
+{
+
+ if (!arscp_target_exist())
+ return;
+ arscp_mlist2argv(list);
+ ar_mode_x(bsdar);
+ arscp_free_argv();
+ arscp_free_mlist(list);
+}
+
+/* List modules of archive. (Simple Mode) */
+static void
+arscp_list()
+{
+
+ if (!arscp_target_exist())
+ return;
+ bsdar->argc = 0;
+ bsdar->argv = NULL;
+ /* Always verbose. */
+ bsdar->options |= AR_V;
+ ar_mode_t(bsdar);
+ bsdar->options &= ~AR_V;
+}
+
+/* List modules of archive. (Advance Mode) */
+static void
+arscp_dir(char *archive, struct list *list, char *rlt)
+{
+ FILE *out;
+
+ /* If rlt != NULL, redirect output to it */
+ out = NULL;
+ if (rlt) {
+ out = stdout;
+ if ((stdout = fopen(rlt, "w")) == NULL)
+ bsdar_errc(bsdar, EX_IOERR, errno,
+ "fopen %s failed", rlt);
+ }
+
+ bsdar->filename = archive;
+ if (list)
+ arscp_mlist2argv(list);
+ else {
+ bsdar->argc = 0;
+ bsdar->argv = NULL;
+ }
+ if (verbose)
+ bsdar->options |= AR_V;
+ ar_mode_t(bsdar);
+ bsdar->options &= ~AR_V;
+
+ if (rlt) {
+ if (fclose(stdout) == EOF)
+ bsdar_errc(bsdar, EX_IOERR, errno,
+ "fclose %s failed", rlt);
+ stdout = out;
+ free(rlt);
+ }
+ free(archive);
+ bsdar->filename = tmpac;
+ arscp_free_argv();
+ arscp_free_mlist(list);
+}
+
+
+/* Replace modules of current archive. */
+static void
+arscp_replace(struct list *list)
+{
+
+ if (!arscp_target_exist())
+ return;
+ arscp_mlist2argv(list);
+ ar_mode_r(bsdar);
+ arscp_free_argv();
+ arscp_free_mlist(list);
+}
+
+/* Rename the temporary archive to the target archive. */
+static void
+arscp_save()
+{
+ mode_t mask;
+
+ if (target) {
+ if (rename(tmpac, target) < 0)
+ bsdar_errc(bsdar, EX_IOERR, errno, "rename failed");
+ /*
+ * mkstemp creates temp files with mode 0600, here we
+ * set target archive mode per process umask.
+ */
+ mask = umask(0);
+ umask(mask);
+ if (chmod(target, 0666 & ~mask) < 0)
+ bsdar_errc(bsdar, EX_IOERR, errno, "chmod failed");
+ free(tmpac);
+ free(target);
+ tmpac = NULL;
+ target= NULL;
+ bsdar->filename = NULL;
+ } else
+ bsdar_warnc(bsdar, 0, "no open output archive");
+}
+
+/*
+ * Discard all the contents of current archive. This is achieved by
+ * invoking CREATE cmd on current archive.
+ */
+static void
+arscp_clear()
+{
+ char *new_target;
+
+ if (target) {
+ new_target = strdup(target);
+ if (new_target == NULL)
+ bsdar_errc(bsdar, EX_SOFTWARE, errno, "strdup failed");
+ arscp_create(NULL, new_target);
+ }
+}
+
+/*
+ * Quit ar(1). Note that END cmd will not SAVE current archive
+ * before exit.
+ */
+static void
+arscp_end(int eval)
+{
+
+ if (target)
+ free(target);
+ if (tmpac) {
+ if (unlink(tmpac) == -1)
+ bsdar_errc(bsdar, EX_IOERR, errno, "unlink %s failed",
+ tmpac);
+ free(tmpac);
+ }
+
+ exit(eval);
+}
+
+/*
+ * Check if target spcified, i.e, whether OPEN or CREATE has been
+ * issued by user.
+ */
+static int
+arscp_target_exist()
+{
+
+ if (target)
+ return (1);
+
+ bsdar_warnc(bsdar, 0, "no open output archive");
+ return (0);
+}
+
+/* Construct module list. */
+static struct list *
+arscp_mlist(struct list *list, char *str)
+{
+ struct list *l;
+
+ l = malloc(sizeof(*l));
+ if (l == NULL)
+ bsdar_errc(bsdar, EX_SOFTWARE, errno, "malloc failed");
+ l->str = str;
+ l->next = list;
+
+ return (l);
+}
+
+/* Calculate the length of a mlist. */
+static int
+arscp_mlist_len(struct list *list)
+{
+ int len;
+
+ for(len = 0; list; list = list->next)
+ len++;
+
+ return (len);
+}
+
+/* Free the space allocated for mod_list. */
+static void
+arscp_free_mlist(struct list *list)
+{
+ struct list *l;
+
+ /* Note that list->str was freed in arscp_free_argv. */
+ for(; list; list = l) {
+ l = list->next;
+ free(list);
+ }
+}
+
+/* Convert mlist to argv array. */
+static void
+arscp_mlist2argv(struct list *list)
+{
+ char **argv;
+ int i, n;
+
+ n = arscp_mlist_len(list);
+ argv = malloc(n * sizeof(*argv));
+ if (argv == NULL)
+ bsdar_errc(bsdar, EX_SOFTWARE, errno, "malloc failed");
+
+ /* Note that module names are stored in reverse order in mlist. */
+ for(i = n - 1; i >= 0; i--, list = list->next) {
+ if (list == NULL)
+ bsdar_errc(bsdar, EX_SOFTWARE, errno, "invalid mlist");
+ argv[i] = list->str;
+ }
+
+ bsdar->argc = n;
+ bsdar->argv = argv;
+}
+
+/* Free space allocated for argv array and its elements. */
+static void
+arscp_free_argv()
+{
+ int i;
+
+ for(i = 0; i < bsdar->argc; i++)
+ free(bsdar->argv[i]);
+
+ free(bsdar->argv);
+}
+
+/* Show a prompt if we are in interactive mode */
+static void
+arscp_prompt()
+{
+
+ if (interactive) {
+ printf("AR >");
+ fflush(stdout);
+ }
+}
+
+/* Main function for ar script mode. */
+void
+ar_mode_script(struct bsdar *ar)
+{
+
+ bsdar = ar;
+ interactive = isatty(fileno(stdin));
+ while(yyparse()) {
+ if (!interactive)
+ arscp_end(1);
+ }
+
+ /* Script ends without END */
+ arscp_end(EX_OK);
+}
diff --git a/usr.bin/ar/ar.c b/usr.bin/ar/ar.c
index fdb7957..e910e30 100644
--- a/usr.bin/ar/ar.c
+++ b/usr.bin/ar/ar.c
@@ -151,7 +151,7 @@ main(int argc, char **argv)
}
}
- while ((opt = getopt_long(argc, argv, "abCcdfijlmopqrSsTtuVvxz",
+ while ((opt = getopt_long(argc, argv, "abCcdfijlMmopqrSsTtuVvxz",
longopts, NULL)) != -1) {
switch(opt) {
case 'a':
@@ -180,6 +180,9 @@ main(int argc, char **argv)
case 'l':
/* ignored, for GNU ar comptibility */
break;
+ case 'M':
+ set_mode(bsdar, opt);
+ break;
case 'm':
set_mode(bsdar, opt);
break;
@@ -229,7 +232,7 @@ main(int argc, char **argv)
argv += optind;
argc -= optind;
- if (*argv == NULL)
+ if (*argv == NULL && bsdar->mode != 'M')
bsdar_usage();
if (bsdar->options & AR_A && bsdar->options & AR_B)
@@ -270,6 +273,11 @@ main(int argc, char **argv)
if (bsdar->options & AR_U)
only_mode(bsdar, "-u", "qrx");
+ if (bsdar->mode == 'M') {
+ ar_mode_script(bsdar);
+ exit(EX_OK);
+ }
+
if ((bsdar->filename = *argv) == NULL)
bsdar_usage();
diff --git a/usr.bin/ar/ar.h b/usr.bin/ar/ar.h
index e76fba0..111f944 100644
--- a/usr.bin/ar/ar.h
+++ b/usr.bin/ar/ar.h
@@ -26,7 +26,7 @@
* $FreeBSD$
*/
-#define BSDAR_VERSION "1.0.2"
+#define BSDAR_VERSION "1.1.0"
/*
* ar(1) options.
@@ -54,7 +54,7 @@
bsdar_errc(bsdar, EX_SOFTWARE, 0, "%s", \
archive_error_string(a)); \
} while (0)
-
+
/*
* In-memory representation of archive member(object).
*/
@@ -74,10 +74,11 @@ struct ar_obj {
};
/*
- * Structure encapsulates the "global" data for "ar" program.
+ * Structure encapsulates the "global" data for "ar" program.
*/
struct bsdar {
const char *filename; /* archive name. */
+ const char *addlib; /* target of ADDLIB. */
const char *posarg; /* position arg for modifiers -a, -b. */
char mode; /* program mode */
char compression; /* compression mode */
@@ -120,3 +121,5 @@ void ar_mode_r(struct bsdar *bsdar);
void ar_mode_s(struct bsdar *bsdar);
void ar_mode_t(struct bsdar *bsdar);
void ar_mode_x(struct bsdar *bsdar);
+void ar_mode_A(struct bsdar *bsdar);
+void ar_mode_script(struct bsdar *ar);
diff --git a/usr.bin/ar/write.c b/usr.bin/ar/write.c
index 4e6d0f6..3d4f93d 100644
--- a/usr.bin/ar/write.c
+++ b/usr.bin/ar/write.c
@@ -60,6 +60,8 @@ static void create_symtab_entry(struct bsdar *bsdar, void *maddr,
size_t size);
static void insert_obj(struct bsdar *bsdar, struct ar_obj *obj,
struct ar_obj *pos);
+static void read_objs(struct bsdar *bsdar, const char *archive,
+ int checkargv);
static void write_archive(struct bsdar *bsdar, char mode);
static void write_cleanup(struct bsdar *bsdar);
static void write_data(struct bsdar *bsdar, struct archive *a,
@@ -101,6 +103,13 @@ ar_mode_s(struct bsdar *bsdar)
write_archive(bsdar, 's');
}
+void
+ar_mode_A(struct bsdar *bsdar)
+{
+
+ write_archive(bsdar, 'A');
+}
+
/*
* Create object from file, return created obj upon success, or NULL
* when an error occurs or the member is not newer than existing
@@ -218,62 +227,29 @@ tail:
}
/*
- * Determine the constitution of resulting archive.
+ * Read objects from archive into v_obj list. Note that checkargv is
+ * set when read_objs is used to read objects from the target of
+ * ADDLIB command (ar script mode), in this case argv array possibly
+ * specifies the members ADDLIB want.
*/
static void
-write_archive(struct bsdar *bsdar, char mode)
+read_objs(struct bsdar *bsdar, const char *archive, int checkargv)
{
struct archive *a;
struct archive_entry *entry;
- struct ar_obj *nobj, *obj, *obj_temp, *pos;
- struct stat sb;
+ struct ar_obj *obj;
const char *name;
const char *bname;
char *buff;
char **av;
size_t size;
- int i, r;
-
- TAILQ_INIT(&bsdar->v_obj);
- nobj = NULL;
- pos = NULL;
- memset(&sb, 0, sizeof(sb));
-
- /* By default, no compression is assumed. */
- bsdar->compression = ARCHIVE_COMPRESSION_NONE;
-
- /*
- * Test if the specified archive exists, to figure out
- * whether we are creating one here.
- */
- if (stat(bsdar->filename, &sb) != 0) {
- if (errno != ENOENT) {
- bsdar_warnc(bsdar, 0, "stat %s failed",
- bsdar->filename);
- return;
- }
+ int i, r, find;
- /* We do not create archive in mode 'd', 'm' and 's'. */
- if (mode != 'r' && mode != 'q') {
- bsdar_warnc(bsdar, 0, "%s: no such file",
- bsdar->filename);
- return;
- }
-
- /* Issue a warning if -c is not specified when creating. */
- if (!(bsdar->options & AR_C))
- bsdar_warnc(bsdar, 0, "creating %s", bsdar->filename);
- goto new_archive;
- }
-
- /*
- * First read members from existing archive.
- */
if ((a = archive_read_new()) == NULL)
bsdar_errc(bsdar, EX_SOFTWARE, 0, "archive_read_new failed");
archive_read_support_compression_all(a);
archive_read_support_format_ar(a);
- AC(archive_read_open_filename(a, bsdar->filename, DEF_BLKSZ));
+ AC(archive_read_open_filename(a, archive, DEF_BLKSZ));
for (;;) {
r = archive_read_next_header(a, &entry);
if (r == ARCHIVE_FATAL)
@@ -303,6 +279,30 @@ write_archive(struct bsdar *bsdar, char mode)
if (strcmp(name, "/") == 0 || strcmp(name, "//") == 0)
continue;
+ /*
+ * If checkargv is set, only read those members specified
+ * in argv.
+ */
+ if (checkargv && bsdar->argc > 0) {
+ find = 0;
+ for(i = 0; i < bsdar->argc; i++) {
+ av = &bsdar->argv[i];
+ if (*av == NULL)
+ continue;
+ if ((bname = basename(*av)) == NULL)
+ bsdar_errc(bsdar, EX_SOFTWARE, errno,
+ "basename failed");
+ if (strcmp(bname, name) != 0)
+ continue;
+
+ *av = NULL;
+ find = 1;
+ break;
+ }
+ if (!find)
+ continue;
+ }
+
size = archive_entry_size(entry);
if (size > 0) {
@@ -341,6 +341,56 @@ write_archive(struct bsdar *bsdar, char mode)
}
AC(archive_read_close(a));
AC(archive_read_finish(a));
+}
+
+/*
+ * Determine the constitution of resulting archive.
+ */
+static void
+write_archive(struct bsdar *bsdar, char mode)
+{
+ struct ar_obj *nobj, *obj, *obj_temp, *pos;
+ struct stat sb;
+ const char *bname;
+ char **av;
+ int i;
+
+ TAILQ_INIT(&bsdar->v_obj);
+ nobj = NULL;
+ pos = NULL;
+ memset(&sb, 0, sizeof(sb));
+
+ /* By default, no compression is assumed. */
+ bsdar->compression = ARCHIVE_COMPRESSION_NONE;
+
+ /*
+ * Test if the specified archive exists, to figure out
+ * whether we are creating one here.
+ */
+ if (stat(bsdar->filename, &sb) != 0) {
+ if (errno != ENOENT) {
+ bsdar_warnc(bsdar, 0, "stat %s failed",
+ bsdar->filename);
+ return;
+ }
+
+ /* We do not create archive in mode 'd', 'm' and 's'. */
+ if (mode != 'r' && mode != 'q') {
+ bsdar_warnc(bsdar, 0, "%s: no such file",
+ bsdar->filename);
+ return;
+ }
+
+ /* Issue a warning if -c is not specified when creating. */
+ if (!(bsdar->options & AR_C))
+ bsdar_warnc(bsdar, 0, "creating %s", bsdar->filename);
+ goto new_archive;
+ }
+
+ /*
+ * First read members from existing archive.
+ */
+ read_objs(bsdar, bsdar->filename, 0);
/*
* For mode 's', no member will be moved, deleted or replaced.
@@ -357,6 +407,22 @@ write_archive(struct bsdar *bsdar, char mode)
goto new_archive;
/*
+ * Mode 'A' adds the contents of another archive to the tail of
+ * current archive. Note that mode 'A' is a special mode for the
+ * ADDLIB command of the ar script mode. Currently there is no
+ * access to this function from the ar command line mode.
+ */
+ if (mode == 'A') {
+ /*
+ * Read objects from the target archive of ADDLIB command.
+ * If there are members spcified in argv, read those members
+ * only, otherwise the entire archive will be read.
+ */
+ read_objs(bsdar, bsdar->addlib, 1);
+ goto write_objs;
+ }
+
+ /*
* Try to find the position member specified by user.
*/
if (bsdar->options & AR_A || bsdar->options & AR_B) {
OpenPOWER on IntegriCloud