From 197245e341aeb1b5da8ee5fbe5d6c49de650fb89 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Wed, 30 Oct 2019 10:50:23 +0000 Subject: [PATCH] Add a --output= option to ar to allow the specifying of an output directory. * ar.c (emum long option numbers): Declare. Use to provide numerical values for long options. (long_options): Add --output option. (usage): Mention the --output option. (open_output_file): New function. Create a filepath for an output file and open it. (extract_file): Use open_output_file(). (open_output_file): * testsuite/binutils-all/ar.exp: Add a test of the new feature. * doc/binutils.texi: Document the new feature. * NEWS: Mention the new feature. --- binutils/ChangeLog | 15 +++ binutils/NEWS | 3 + binutils/ar.c | 155 +++++++++++++++---------- binutils/doc/binutils.texi | 19 ++- binutils/testsuite/binutils-all/ar.exp | 45 +++++++ 5 files changed, 171 insertions(+), 66 deletions(-) diff --git a/binutils/ChangeLog b/binutils/ChangeLog index b723f56939..c85d93b9dc 100644 --- a/binutils/ChangeLog +++ b/binutils/ChangeLog @@ -1,3 +1,18 @@ +2019-10-30 Fangrui Song + Nick Clifton + + * ar.c (emum long option numbers): Declare. Use to provide + numerical values for long options. + (long_options): Add --output option. + (usage): Mention the --output option. + (open_output_file): New function. Create a filepath for an output + file and open it. + (extract_file): Use open_output_file(). + (open_output_file): + * testsuite/binutils-all/ar.exp: Add a test of the new feature. + * doc/binutils.texi: Document the new feature. + * NEWS: Mention the new feature. + 2019-10-26 Alan Modra * objcopy.c (sort_gnu_build_notes): Correct sort of deleted diff --git a/binutils/NEWS b/binutils/NEWS index e6afc4ba29..fd14d71ce9 100644 --- a/binutils/NEWS +++ b/binutils/NEWS @@ -1,5 +1,8 @@ -*- text -*- +* Add --output option to the "ar" program. This option can be used to specify + the output directory when extracting members from an archive. + Changes in 2.33: * Add --source-comment[=] option to objdump which if present, diff --git a/binutils/ar.c b/binutils/ar.c index 38c54c9fa8..0af7954a47 100644 --- a/binutils/ar.c +++ b/binutils/ar.c @@ -35,6 +35,7 @@ #include "binemul.h" #include "plugin-api.h" #include "plugin.h" +#include "ansidecl.h" #ifdef __GO32___ #define EXT_NAME_LEN 3 /* Bufflen of addition to name if it's MS-DOS. */ @@ -149,8 +150,14 @@ static const char *plugin_target = NULL; static const char *target = NULL; -#define OPTION_PLUGIN 201 -#define OPTION_TARGET 202 +enum long_option_numbers +{ + OPTION_PLUGIN = 201, + OPTION_TARGET, + OPTION_OUTPUT +}; + +static const char * output_dir = NULL; static struct option long_options[] = { @@ -158,6 +165,7 @@ static struct option long_options[] = {"plugin", required_argument, NULL, OPTION_PLUGIN}, {"target", required_argument, NULL, OPTION_TARGET}, {"version", no_argument, &show_version, 1}, + {"output", required_argument, NULL, OPTION_OUTPUT}, {NULL, no_argument, NULL, 0} }; @@ -327,6 +335,7 @@ usage (int help) fprintf (s, _(" [V] - display the version number\n")); fprintf (s, _(" @ - read options from \n")); fprintf (s, _(" --target=BFDNAME - specify the target object format as BFDNAME\n")); + fprintf (s, _(" --output=DIRNAME - specify the output directory for extraction operations\n")); #if BFD_SUPPORTS_PLUGINS fprintf (s, _(" optional:\n")); fprintf (s, _(" --plugin

- load the specified plugin\n")); @@ -592,6 +601,9 @@ decode_options (int argc, char **argv) case OPTION_TARGET: target = optarg; break; + case OPTION_OUTPUT: + output_dir = optarg; + break; case 0: /* A long option that just sets a flag. */ break; default: @@ -1050,6 +1062,49 @@ print_contents (bfd *abfd) free (cbuf); } + +static FILE * open_output_file (bfd *) ATTRIBUTE_RETURNS_NONNULL; + +static FILE * +open_output_file (bfd * abfd) +{ + output_filename = bfd_get_filename (abfd); + + if (output_dir) + { + size_t len = strlen (output_dir); + + if (len > 0) + { + /* FIXME: There is a memory leak here, but it is not serious. */ + if (IS_DIR_SEPARATOR (output_dir [len - 1])) + output_filename = concat (output_dir, output_filename, NULL); + else + output_filename = concat (output_dir, "/", output_filename, NULL); + } + } + + /* PR binutils/17533: Do not allow directory traversal + outside of the current directory tree. */ + if (! is_valid_archive_path (output_filename)) + { + char * base = (char *) lbasename (output_filename); + + non_fatal (_("illegal output pathname for archive member: %s, using '%s' instead"), + output_filename, base); + output_filename = base; + } + + FILE * ostream = fopen (output_filename, FOPEN_WB); + if (ostream == NULL) + { + perror (output_filename); + xexit (1); + } + + return ostream; +} + /* Extract a member of the archive into its own file. We defer opening the new file until after we have read a BUFSIZ chunk of the @@ -1063,23 +1118,9 @@ print_contents (bfd *abfd) void extract_file (bfd *abfd) { - FILE *ostream; - char *cbuf = (char *) xmalloc (BUFSIZE); - bfd_size_type nread, tocopy; - bfd_size_type ncopied = 0; bfd_size_type size; struct stat buf; - /* PR binutils/17533: Do not allow directory traversal - outside of the current directory tree. */ - if (! is_valid_archive_path (bfd_get_filename (abfd))) - { - non_fatal (_("illegal pathname found in archive member: %s"), - bfd_get_filename (abfd)); - free (cbuf); - return; - } - if (bfd_stat_arch_elt (abfd, &buf) != 0) /* xgettext:c-format */ fatal (_("internal stat error on %s"), bfd_get_filename (abfd)); @@ -1090,75 +1131,61 @@ extract_file (bfd *abfd) bfd_seek (abfd, (file_ptr) 0, SEEK_SET); - ostream = NULL; + output_file = NULL; if (size == 0) { - /* Seems like an abstraction violation, eh? Well it's OK! */ - output_filename = bfd_get_filename (abfd); + output_file = open_output_file (abfd); + } + else + { + bfd_size_type ncopied = 0; + char *cbuf = (char *) xmalloc (BUFSIZE); - ostream = fopen (bfd_get_filename (abfd), FOPEN_WB); - if (ostream == NULL) + while (ncopied < size) { - perror (bfd_get_filename (abfd)); - xexit (1); - } + bfd_size_type nread, tocopy; - output_file = ostream; - } - else - while (ncopied < size) - { - tocopy = size - ncopied; - if (tocopy > BUFSIZE) - tocopy = BUFSIZE; - - nread = bfd_bread (cbuf, tocopy, abfd); - if (nread != tocopy) - /* xgettext:c-format */ - fatal (_("%s is not a valid archive"), - bfd_get_filename (abfd->my_archive)); + tocopy = size - ncopied; + if (tocopy > BUFSIZE) + tocopy = BUFSIZE; - /* See comment above; this saves disk arm motion */ - if (ostream == NULL) - { - /* Seems like an abstraction violation, eh? Well it's OK! */ - output_filename = bfd_get_filename (abfd); + nread = bfd_bread (cbuf, tocopy, abfd); + if (nread != tocopy) + /* xgettext:c-format */ + fatal (_("%s is not a valid archive"), + bfd_get_filename (abfd->my_archive)); - ostream = fopen (bfd_get_filename (abfd), FOPEN_WB); - if (ostream == NULL) - { - perror (bfd_get_filename (abfd)); - xexit (1); - } + /* See comment above; this saves disk arm motion. */ + if (output_file == NULL) + output_file = open_output_file (abfd); - output_file = ostream; - } + /* fwrite in mingw32 may return int instead of bfd_size_type. Cast + the return value to bfd_size_type to avoid comparison between + signed and unsigned values. */ + if ((bfd_size_type) fwrite (cbuf, 1, nread, output_file) != nread) + fatal ("%s: %s", output_filename, strerror (errno)); - /* fwrite in mingw32 may return int instead of bfd_size_type. Cast - the return value to bfd_size_type to avoid comparison between - signed and unsigned values. */ - if ((bfd_size_type) fwrite (cbuf, 1, nread, ostream) != nread) - fatal ("%s: %s", output_filename, strerror (errno)); - ncopied += tocopy; - } + ncopied += tocopy; + } - if (ostream != NULL) - fclose (ostream); + free (cbuf); + } + + fclose (output_file); output_file = NULL; - output_filename = NULL; - chmod (bfd_get_filename (abfd), buf.st_mode); + chmod (output_filename, buf.st_mode); if (preserve_dates) { /* Set access time to modification time. Only st_mtime is initialized by bfd_stat_arch_elt. */ buf.st_atime = buf.st_mtime; - set_times (bfd_get_filename (abfd), &buf); + set_times (output_filename, &buf); } - free (cbuf); + output_filename = NULL; } static void diff --git a/binutils/doc/binutils.texi b/binutils/doc/binutils.texi index 2edd7e1aa1..97abf980ba 100644 --- a/binutils/doc/binutils.texi +++ b/binutils/doc/binutils.texi @@ -169,7 +169,7 @@ in the section entitled ``GNU Free Documentation License''. @c man title ar create, modify, and extract from archives @smallexample -ar [-]@var{p}[@var{mod}] [@option{--plugin} @var{name}] [@option{--target} @var{bfdname}] [@var{relpos}] [@var{count}] @var{archive} [@var{member}@dots{}] +ar [-]@var{p}[@var{mod}] [@option{--plugin} @var{name}] [@option{--target} @var{bfdname}] [@option{--output} @var{dirname}] [@var{relpos}] [@var{count}] @var{archive} [@var{member}@dots{}] ar -M [