| /* ar.c - Archive modify and extract. | |
| Copyright (C) 1991-2024 Free Software Foundation, Inc. | |
| This file is part of GNU Binutils. | |
| This program is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 3 of the License, or | |
| (at your option) any later version. | |
| This program is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with this program; if not, write to the Free Software | |
| Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, | |
| MA 02110-1301, USA. */ | |
| /* | |
| Bugs: GNU ar used to check file against filesystem in quick_update and | |
| replace operations (would check mtime). Doesn't warn when name truncated. | |
| No way to specify pos_end. Error messages should be more consistent. */ | |
| #include "sysdep.h" | |
| #include "bfd.h" | |
| #include "libiberty.h" | |
| #include "getopt.h" | |
| #include "aout/ar.h" | |
| #include "bucomm.h" | |
| #include "arsup.h" | |
| #include "filenames.h" | |
| #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. */ | |
| #else | |
| #define EXT_NAME_LEN 6 /* Ditto for *NIX. */ | |
| #endif | |
| /* Static declarations. */ | |
| static void mri_emul (void); | |
| static const char *normalize (const char *, bfd *); | |
| static void remove_output (void); | |
| static void map_over_members (bfd *, void (*)(bfd *), char **, int); | |
| static void print_contents (bfd * member); | |
| static void delete_members (bfd *, char **files_to_delete); | |
| static void move_members (bfd *, char **files_to_move); | |
| static void replace_members | |
| (bfd *, char **files_to_replace, bool quick); | |
| static void print_descr (bfd * abfd); | |
| static void write_archive (bfd *); | |
| static int ranlib_only (const char *archname); | |
| static int ranlib_touch (const char *archname); | |
| static void usage (int); | |
| /** Globals and flags. */ | |
| static int mri_mode; | |
| /* This flag distinguishes between ar and ranlib: | |
| 1 means this is 'ranlib'; 0 means this is 'ar'. | |
| -1 means if we should use argv[0] to decide. */ | |
| extern int is_ranlib; | |
| /* Nonzero means don't warn about creating the archive file if necessary. */ | |
| int silent_create = 0; | |
| /* Nonzero means describe each action performed. */ | |
| int verbose = 0; | |
| /* Nonzero means display offsets of files in the archive. */ | |
| int display_offsets = 0; | |
| /* Nonzero means preserve dates of members when extracting them. */ | |
| int preserve_dates = 0; | |
| /* Nonzero means don't replace existing members whose dates are more recent | |
| than the corresponding files. */ | |
| int newer_only = 0; | |
| /* Controls the writing of an archive symbol table (in BSD: a __.SYMDEF | |
| member). -1 means we've been explicitly asked to not write a symbol table; | |
| +1 means we've been explicitly asked to write it; | |
| 0 is the default. | |
| Traditionally, the default in BSD has been to not write the table. | |
| However, for POSIX.2 compliance the default is now to write a symbol table | |
| if any of the members are object files. */ | |
| int write_armap = 0; | |
| /* Operate in deterministic mode: write zero for timestamps, uids, | |
| and gids for archive members and the archive symbol table, and write | |
| consistent file modes. */ | |
| int deterministic = -1; /* Determinism indeterminate. */ | |
| /* Nonzero means it's the name of an existing member; position new or moved | |
| files with respect to this one. */ | |
| char *posname = NULL; | |
| /* Sez how to use `posname': pos_before means position before that member. | |
| pos_after means position after that member. pos_end means always at end. | |
| pos_default means default appropriately. For the latter two, `posname' | |
| should also be zero. */ | |
| enum pos | |
| { | |
| pos_default, pos_before, pos_after, pos_end | |
| } postype = pos_default; | |
| enum operations | |
| { | |
| none = 0, del, replace, print_table, | |
| print_files, extract, move, quick_append | |
| } operation = none; | |
| static bfd ** | |
| get_pos_bfd (bfd **, enum pos, const char *); | |
| /* For extract/delete only. If COUNTED_NAME_MODE is TRUE, we only | |
| extract the COUNTED_NAME_COUNTER instance of that name. */ | |
| static bool counted_name_mode = 0; | |
| static int counted_name_counter = 0; | |
| /* Whether to truncate names of files stored in the archive. */ | |
| static bool ar_truncate = false; | |
| /* Whether to use a full file name match when searching an archive. | |
| This is convenient for archives created by the Microsoft lib | |
| program. */ | |
| static bool full_pathname = false; | |
| /* Whether to create a "thin" archive (symbol index only -- no files). */ | |
| static bool make_thin_archive = false; | |
| #define LIBDEPS "__.LIBDEP" | |
| /* Text to store in the __.LIBDEP archive element for the linker to use. */ | |
| static char * libdeps = NULL; | |
| static bfd * libdeps_bfd = NULL; | |
| static int show_version = 0; | |
| static int show_help = 0; | |
| #if BFD_SUPPORTS_PLUGINS | |
| static const char *plugin_target = "plugin"; | |
| #else | |
| static const char *plugin_target = NULL; | |
| #endif | |
| static const char *target = NULL; | |
| enum long_option_numbers | |
| { | |
| OPTION_PLUGIN = 201, | |
| OPTION_TARGET, | |
| OPTION_OUTPUT | |
| }; | |
| static const char * output_dir = NULL; | |
| static struct option long_options[] = | |
| { | |
| {"help", no_argument, &show_help, 1}, | |
| {"plugin", required_argument, NULL, OPTION_PLUGIN}, | |
| {"target", required_argument, NULL, OPTION_TARGET}, | |
| {"version", no_argument, &show_version, 1}, | |
| {"output", required_argument, NULL, OPTION_OUTPUT}, | |
| {"record-libdeps", required_argument, NULL, 'l'}, | |
| {"thin", no_argument, NULL, 'T'}, | |
| {NULL, no_argument, NULL, 0} | |
| }; | |
| int interactive = 0; | |
| static void | |
| mri_emul (void) | |
| { | |
| interactive = isatty (fileno (stdin)); | |
| yyparse (); | |
| } | |
| /* If COUNT is 0, then FUNCTION is called once on each entry. If nonzero, | |
| COUNT is the length of the FILES chain; FUNCTION is called on each entry | |
| whose name matches one in FILES. */ | |
| static void | |
| map_over_members (bfd *arch, void (*function)(bfd *), char **files, int count) | |
| { | |
| bfd *head; | |
| int match_count; | |
| if (count == 0) | |
| { | |
| for (head = arch->archive_next; head; head = head->archive_next) | |
| function (head); | |
| return; | |
| } | |
| /* This may appear to be a baroque way of accomplishing what we want. | |
| However we have to iterate over the filenames in order to notice where | |
| a filename is requested but does not exist in the archive. Ditto | |
| mapping over each file each time -- we want to hack multiple | |
| references. */ | |
| for (head = arch->archive_next; head; head = head->archive_next) | |
| head->archive_pass = 0; | |
| for (; count > 0; files++, count--) | |
| { | |
| bool found = false; | |
| match_count = 0; | |
| for (head = arch->archive_next; head; head = head->archive_next) | |
| { | |
| const char * filename; | |
| /* PR binutils/15796: Once an archive element has been matched | |
| do not match it again. If the user provides multiple same-named | |
| parameters on the command line their intent is to match multiple | |
| same-named entries in the archive, not the same entry multiple | |
| times. */ | |
| if (head->archive_pass) | |
| continue; | |
| filename = bfd_get_filename (head); | |
| if (filename == NULL) | |
| { | |
| /* Some archive formats don't get the filenames filled in | |
| until the elements are opened. */ | |
| struct stat buf; | |
| bfd_stat_arch_elt (head, &buf); | |
| } | |
| else if (bfd_is_thin_archive (arch)) | |
| { | |
| /* Thin archives store full pathnames. Need to normalize. */ | |
| filename = normalize (filename, arch); | |
| } | |
| if (filename != NULL | |
| && !FILENAME_CMP (normalize (*files, arch), filename)) | |
| { | |
| ++match_count; | |
| if (counted_name_mode | |
| && match_count != counted_name_counter) | |
| { | |
| /* Counting, and didn't match on count; go on to the | |
| next one. */ | |
| continue; | |
| } | |
| found = true; | |
| function (head); | |
| head->archive_pass = 1; | |
| /* PR binutils/15796: Once a file has been matched, do not | |
| match any more same-named files in the archive. If the | |
| user does want to match multiple same-name files in an | |
| archive they should provide multiple same-name parameters | |
| to the ar command. */ | |
| break; | |
| } | |
| } | |
| if (!found) | |
| /* xgettext:c-format */ | |
| fprintf (stderr, _("no entry %s in archive\n"), *files); | |
| } | |
| } | |
| bool operation_alters_arch = false; | |
| static void | |
| usage (int help) | |
| { | |
| FILE *s; | |
| #if BFD_SUPPORTS_PLUGINS | |
| /* xgettext:c-format */ | |
| const char *command_line | |
| = _("Usage: %s [emulation options] [-]{dmpqrstx}[abcDfilMNoOPsSTuvV]" | |
| " [--plugin <name>] [member-name] [count] archive-file file...\n"); | |
| #else | |
| /* xgettext:c-format */ | |
| const char *command_line | |
| = _("Usage: %s [emulation options] [-]{dmpqrstx}[abcDfilMNoOPsSTuvV]" | |
| " [member-name] [count] archive-file file...\n"); | |
| #endif | |
| s = help ? stdout : stderr; | |
| fprintf (s, command_line, program_name); | |
| /* xgettext:c-format */ | |
| fprintf (s, _(" %s -M [<mri-script]\n"), program_name); | |
| fprintf (s, _(" commands:\n")); | |
| fprintf (s, _(" d - delete file(s) from the archive\n")); | |
| fprintf (s, _(" m[ab] - move file(s) in the archive\n")); | |
| fprintf (s, _(" p - print file(s) found in the archive\n")); | |
| fprintf (s, _(" q[f] - quick append file(s) to the archive\n")); | |
| fprintf (s, _(" r[ab][f][u] - replace existing or insert new file(s) into the archive\n")); | |
| fprintf (s, _(" s - act as ranlib\n")); | |
| fprintf (s, _(" t[O][v] - display contents of the archive\n")); | |
| fprintf (s, _(" x[o] - extract file(s) from the archive\n")); | |
| fprintf (s, _(" command specific modifiers:\n")); | |
| fprintf (s, _(" [a] - put file(s) after [member-name]\n")); | |
| fprintf (s, _(" [b] - put file(s) before [member-name] (same as [i])\n")); | |
| if (DEFAULT_AR_DETERMINISTIC) | |
| { | |
| fprintf (s, _("\ | |
| [D] - use zero for timestamps and uids/gids (default)\n")); | |
| fprintf (s, _("\ | |
| [U] - use actual timestamps and uids/gids\n")); | |
| } | |
| else | |
| { | |
| fprintf (s, _("\ | |
| [D] - use zero for timestamps and uids/gids\n")); | |
| fprintf (s, _("\ | |
| [U] - use actual timestamps and uids/gids (default)\n")); | |
| } | |
| fprintf (s, _(" [N] - use instance [count] of name\n")); | |
| fprintf (s, _(" [f] - truncate inserted file names\n")); | |
| fprintf (s, _(" [P] - use full path names when matching\n")); | |
| fprintf (s, _(" [o] - preserve original dates\n")); | |
| fprintf (s, _(" [O] - display offsets of files in the archive\n")); | |
| fprintf (s, _(" [u] - only replace files that are newer than current archive contents\n")); | |
| fprintf (s, _(" generic modifiers:\n")); | |
| fprintf (s, _(" [c] - do not warn if the library had to be created\n")); | |
| fprintf (s, _(" [s] - create an archive index (cf. ranlib)\n")); | |
| fprintf (s, _(" [l <text> ] - specify the dependencies of this library\n")); | |
| fprintf (s, _(" [S] - do not build a symbol table\n")); | |
| fprintf (s, _(" [T] - deprecated, use --thin instead\n")); | |
| fprintf (s, _(" [v] - be verbose\n")); | |
| fprintf (s, _(" [V] - display the version number\n")); | |
| fprintf (s, _(" @<file> - read options from <file>\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")); | |
| fprintf (s, _(" --record-libdeps=<text> - specify the dependencies of this library\n")); | |
| fprintf (s, _(" --thin - make a thin archive\n")); | |
| #if BFD_SUPPORTS_PLUGINS | |
| fprintf (s, _(" optional:\n")); | |
| fprintf (s, _(" --plugin <p> - load the specified plugin\n")); | |
| #endif | |
| ar_emul_usage (s); | |
| list_supported_targets (program_name, s); | |
| if (REPORT_BUGS_TO[0] && help) | |
| fprintf (s, _("Report bugs to %s\n"), REPORT_BUGS_TO); | |
| xexit (help ? 0 : 1); | |
| } | |
| static void | |
| ranlib_usage (int help) | |
| { | |
| FILE *s; | |
| s = help ? stdout : stderr; | |
| /* xgettext:c-format */ | |
| fprintf (s, _("Usage: %s [options] archive\n"), program_name); | |
| fprintf (s, _(" Generate an index to speed access to archives\n")); | |
| fprintf (s, _(" The options are:\n\ | |
| @<file> Read options from <file>\n")); | |
| #if BFD_SUPPORTS_PLUGINS | |
| fprintf (s, _("\ | |
| --plugin <name> Load the specified plugin\n")); | |
| #endif | |
| if (DEFAULT_AR_DETERMINISTIC) | |
| fprintf (s, _("\ | |
| -D Use zero for symbol map timestamp (default)\n\ | |
| -U Use an actual symbol map timestamp\n")); | |
| else | |
| fprintf (s, _("\ | |
| -D Use zero for symbol map timestamp\n\ | |
| -U Use actual symbol map timestamp (default)\n")); | |
| fprintf (s, _("\ | |
| -t Update the archive's symbol map timestamp\n\ | |
| -h --help Print this help message\n\ | |
| -v --version Print version information\n")); | |
| list_supported_targets (program_name, s); | |
| if (REPORT_BUGS_TO[0] && help) | |
| fprintf (s, _("Report bugs to %s\n"), REPORT_BUGS_TO); | |
| xexit (help ? 0 : 1); | |
| } | |
| /* Normalize a file name specified on the command line into a file | |
| name which we will use in an archive. */ | |
| static const char * | |
| normalize (const char *file, bfd *abfd) | |
| { | |
| const char *filename; | |
| if (full_pathname) | |
| return file; | |
| filename = lbasename (file); | |
| if (ar_truncate | |
| && abfd != NULL | |
| && strlen (filename) > abfd->xvec->ar_max_namelen) | |
| { | |
| char *s; | |
| /* Space leak. */ | |
| s = (char *) xmalloc (abfd->xvec->ar_max_namelen + 1); | |
| memcpy (s, filename, abfd->xvec->ar_max_namelen); | |
| s[abfd->xvec->ar_max_namelen] = '\0'; | |
| filename = s; | |
| } | |
| return filename; | |
| } | |
| /* Remove any output file. This is only called via xatexit. */ | |
| static const char *output_filename = NULL; | |
| static FILE *output_file = NULL; | |
| static void | |
| remove_output (void) | |
| { | |
| if (output_filename != NULL) | |
| { | |
| if (output_file != NULL) | |
| fclose (output_file); | |
| unlink_if_ordinary (output_filename); | |
| } | |
| } | |
| static char ** | |
| decode_options (int argc, char **argv) | |
| { | |
| int c; | |
| /* Convert old-style ar call by exploding option element and rearranging | |
| options accordingly. */ | |
| restart: | |
| if (argc > 1 && argv[1][0] != '-') | |
| { | |
| int new_argc; /* argc value for rearranged arguments */ | |
| char **new_argv; /* argv value for rearranged arguments */ | |
| char *const *in; /* cursor into original argv */ | |
| char **out; /* cursor into rearranged argv */ | |
| const char *letter; /* cursor into old option letters */ | |
| char buffer[3]; /* constructed option buffer */ | |
| /* Initialize a constructed option. */ | |
| buffer[0] = '-'; | |
| buffer[2] = '\0'; | |
| /* Allocate a new argument array, and copy program name in it. */ | |
| new_argc = argc - 1 + strlen (argv[1]); | |
| new_argv = xmalloc ((new_argc + 1) * sizeof (*argv)); | |
| in = argv; | |
| out = new_argv; | |
| *out++ = *in++; | |
| /* Copy each old letter option as a separate option. */ | |
| for (letter = *in++; *letter; letter++) | |
| { | |
| buffer[1] = *letter; | |
| *out++ = xstrdup (buffer); | |
| } | |
| /* Copy all remaining options. */ | |
| while (in < argv + argc) | |
| *out++ = *in++; | |
| *out = NULL; | |
| /* Replace the old option list by the new one. */ | |
| argc = new_argc; | |
| argv = new_argv; | |
| } | |
| while ((c = getopt_long (argc, argv, "hdmpqrtxl:coOVsSuvabiMNfPTDU", | |
| long_options, NULL)) != EOF) | |
| { | |
| switch (c) | |
| { | |
| case 'd': | |
| case 'm': | |
| case 'p': | |
| case 'q': | |
| case 'r': | |
| case 't': | |
| case 'x': | |
| if (operation != none) | |
| fatal (_("two different operation options specified")); | |
| break; | |
| } | |
| switch (c) | |
| { | |
| case 'h': | |
| show_help = 1; | |
| break; | |
| case 'd': | |
| operation = del; | |
| operation_alters_arch = true; | |
| break; | |
| case 'm': | |
| operation = move; | |
| operation_alters_arch = true; | |
| break; | |
| case 'p': | |
| operation = print_files; | |
| break; | |
| case 'q': | |
| operation = quick_append; | |
| operation_alters_arch = true; | |
| break; | |
| case 'r': | |
| operation = replace; | |
| operation_alters_arch = true; | |
| break; | |
| case 't': | |
| operation = print_table; | |
| break; | |
| case 'x': | |
| operation = extract; | |
| break; | |
| case 'l': | |
| if (libdeps != NULL) | |
| fatal (_("libdeps specified more than once")); | |
| libdeps = optarg; | |
| break; | |
| case 'c': | |
| silent_create = 1; | |
| break; | |
| case 'o': | |
| preserve_dates = 1; | |
| break; | |
| case 'O': | |
| display_offsets = 1; | |
| break; | |
| case 'V': | |
| show_version = true; | |
| break; | |
| case 's': | |
| write_armap = 1; | |
| break; | |
| case 'S': | |
| write_armap = -1; | |
| break; | |
| case 'u': | |
| newer_only = 1; | |
| break; | |
| case 'v': | |
| verbose = 1; | |
| break; | |
| case 'a': | |
| postype = pos_after; | |
| break; | |
| case 'b': | |
| postype = pos_before; | |
| break; | |
| case 'i': | |
| postype = pos_before; | |
| break; | |
| case 'M': | |
| mri_mode = 1; | |
| break; | |
| case 'N': | |
| counted_name_mode = true; | |
| break; | |
| case 'f': | |
| ar_truncate = true; | |
| break; | |
| case 'P': | |
| full_pathname = true; | |
| break; | |
| case 'T': | |
| make_thin_archive = true; | |
| break; | |
| case 'D': | |
| deterministic = true; | |
| break; | |
| case 'U': | |
| deterministic = false; | |
| break; | |
| case OPTION_PLUGIN: | |
| #if BFD_SUPPORTS_PLUGINS | |
| bfd_plugin_set_plugin (optarg); | |
| #else | |
| fprintf (stderr, _("sorry - this program has been built without plugin support\n")); | |
| xexit (1); | |
| #endif | |
| break; | |
| 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: | |
| usage (0); | |
| } | |
| } | |
| /* PR 13256: Allow for the possibility that the first command line option | |
| started with a dash (eg --plugin) but then the following option(s) are | |
| old style, non-dash-prefixed versions. */ | |
| if (operation == none && write_armap != 1 && !mri_mode | |
| && optind > 0 && optind < argc) | |
| { | |
| argv += (optind - 1); | |
| argc -= (optind - 1); | |
| optind = 0; | |
| goto restart; | |
| } | |
| return &argv[optind]; | |
| } | |
| /* If neither -D nor -U was specified explicitly, | |
| then use the configured default. */ | |
| static void | |
| default_deterministic (void) | |
| { | |
| if (deterministic < 0) | |
| deterministic = DEFAULT_AR_DETERMINISTIC; | |
| } | |
| static void | |
| ranlib_main (int argc, char **argv) | |
| { | |
| int arg_index, status = 0; | |
| bool touch = false; | |
| int c; | |
| while ((c = getopt_long (argc, argv, "DhHUvVt", long_options, NULL)) != EOF) | |
| { | |
| switch (c) | |
| { | |
| case 'D': | |
| deterministic = true; | |
| break; | |
| case 'U': | |
| deterministic = false; | |
| break; | |
| case 'h': | |
| case 'H': | |
| show_help = 1; | |
| break; | |
| case 't': | |
| touch = true; | |
| break; | |
| case 'v': | |
| case 'V': | |
| show_version = 1; | |
| break; | |
| /* PR binutils/13493: Support plugins. */ | |
| case OPTION_PLUGIN: | |
| #if BFD_SUPPORTS_PLUGINS | |
| bfd_plugin_set_plugin (optarg); | |
| #else | |
| fprintf (stderr, _("sorry - this program has been built without plugin support\n")); | |
| xexit (1); | |
| #endif | |
| break; | |
| } | |
| } | |
| if (argc < 2) | |
| ranlib_usage (0); | |
| if (show_help) | |
| ranlib_usage (1); | |
| if (show_version) | |
| print_version ("ranlib"); | |
| default_deterministic (); | |
| arg_index = optind; | |
| while (arg_index < argc) | |
| { | |
| if (! touch) | |
| status |= ranlib_only (argv[arg_index]); | |
| else | |
| status |= ranlib_touch (argv[arg_index]); | |
| ++arg_index; | |
| } | |
| xexit (status); | |
| } | |
| int main (int, char **); | |
| int | |
| main (int argc, char **argv) | |
| { | |
| int arg_index; | |
| char **files; | |
| int file_count; | |
| char *inarch_filename; | |
| int i; | |
| #ifdef HAVE_LC_MESSAGES | |
| setlocale (LC_MESSAGES, ""); | |
| #endif | |
| setlocale (LC_CTYPE, ""); | |
| bindtextdomain (PACKAGE, LOCALEDIR); | |
| textdomain (PACKAGE); | |
| program_name = argv[0]; | |
| xmalloc_set_program_name (program_name); | |
| bfd_set_error_program_name (program_name); | |
| #if BFD_SUPPORTS_PLUGINS | |
| bfd_plugin_set_program_name (program_name); | |
| #endif | |
| expandargv (&argc, &argv); | |
| if (is_ranlib < 0) | |
| { | |
| const char *temp = lbasename (program_name); | |
| if (strlen (temp) >= 6 | |
| && FILENAME_CMP (temp + strlen (temp) - 6, "ranlib") == 0) | |
| is_ranlib = 1; | |
| else | |
| is_ranlib = 0; | |
| } | |
| if (bfd_init () != BFD_INIT_MAGIC) | |
| fatal (_("fatal error: libbfd ABI mismatch")); | |
| set_default_bfd_target (); | |
| xatexit (remove_output); | |
| for (i = 1; i < argc; i++) | |
| if (! ar_emul_parse_arg (argv[i])) | |
| break; | |
| argv += (i - 1); | |
| argc -= (i - 1); | |
| if (is_ranlib) | |
| ranlib_main (argc, argv); | |
| if (argc < 2) | |
| usage (0); | |
| argv = decode_options (argc, argv); | |
| if (show_help) | |
| usage (1); | |
| if (show_version) | |
| print_version ("ar"); | |
| arg_index = 0; | |
| if (mri_mode) | |
| { | |
| default_deterministic (); | |
| mri_emul (); | |
| } | |
| else | |
| { | |
| bfd *arch; | |
| /* Fail if no files are specified on the command line. | |
| (But not for MRI mode which allows for reading arguments | |
| and filenames from stdin). */ | |
| if (argv[arg_index] == NULL) | |
| usage (0); | |
| /* We don't use do_quick_append any more. Too many systems | |
| expect ar to always rebuild the symbol table even when q is | |
| used. */ | |
| /* We can't write an armap when using ar q, so just do ar r | |
| instead. */ | |
| if (operation == quick_append && write_armap) | |
| operation = replace; | |
| if ((operation == none || operation == print_table) | |
| && write_armap == 1) | |
| xexit (ranlib_only (argv[arg_index])); | |
| if (operation == none) | |
| fatal (_("no operation specified")); | |
| if (newer_only && operation != replace) | |
| fatal (_("`u' is only meaningful with the `r' option.")); | |
| if (newer_only && deterministic > 0) | |
| non_fatal (_("`u' is not meaningful with the `D' option - replacement will always happen.")); | |
| if (newer_only && deterministic < 0 && DEFAULT_AR_DETERMINISTIC) | |
| non_fatal (_("\ | |
| `u' modifier ignored since `D' is the default (see `U')")); | |
| default_deterministic (); | |
| if (postype != pos_default) | |
| { | |
| posname = argv[arg_index++]; | |
| if (posname == NULL) | |
| fatal (_("missing position arg.")); | |
| } | |
| if (counted_name_mode) | |
| { | |
| if (operation != extract && operation != del) | |
| fatal (_("`N' is only meaningful with the `x' and `d' options.")); | |
| if (argv[arg_index] == NULL) | |
| fatal (_("`N' missing value.")); | |
| counted_name_counter = atoi (argv[arg_index++]); | |
| if (counted_name_counter <= 0) | |
| fatal (_("Value for `N' must be positive.")); | |
| } | |
| inarch_filename = argv[arg_index++]; | |
| if (inarch_filename == NULL) | |
| usage (0); | |
| for (file_count = 0; argv[arg_index + file_count] != NULL; file_count++) | |
| continue; | |
| files = (file_count > 0) ? argv + arg_index : NULL; | |
| arch = open_inarch (inarch_filename, | |
| files == NULL ? (char *) NULL : files[0]); | |
| if (operation == extract && bfd_is_thin_archive (arch)) | |
| fatal (_("`x' cannot be used on thin archives.")); | |
| if (libdeps != NULL) | |
| { | |
| char **new_files; | |
| bfd_size_type reclen = strlen (libdeps) + 1; | |
| /* Create a bfd to contain the dependencies. | |
| It inherits its type from arch, but we must set the type to | |
| "binary" otherwise bfd_write() will fail. After writing, we | |
| must set the type back to default otherwise adding it to the | |
| archive will fail. */ | |
| libdeps_bfd = bfd_create (LIBDEPS, arch); | |
| if (libdeps_bfd == NULL) | |
| fatal (_("Cannot create libdeps record.")); | |
| if (bfd_find_target ("binary", libdeps_bfd) == NULL) | |
| fatal (_("Cannot set libdeps record type to binary.")); | |
| if (! bfd_set_format (libdeps_bfd, bfd_object)) | |
| fatal (_("Cannot set libdeps object format.")); | |
| if (! bfd_make_writable (libdeps_bfd)) | |
| fatal (_("Cannot make libdeps object writable.")); | |
| if (bfd_write (libdeps, reclen, libdeps_bfd) != reclen) | |
| fatal (_("Cannot write libdeps record.")); | |
| if (! bfd_make_readable (libdeps_bfd)) | |
| fatal (_("Cannot make libdeps object readable.")); | |
| if (bfd_find_target (plugin_target, libdeps_bfd) == NULL) | |
| fatal (_("Cannot reset libdeps record type.")); | |
| /* Insert our libdeps record in 2nd slot of the list of files | |
| being operated on. We shouldn't use 1st slot, but we want | |
| to avoid having to search all the way to the end of an | |
| archive with a large number of members at link time. */ | |
| new_files = xmalloc ((file_count + 2) * sizeof (*new_files)); | |
| if (file_count) | |
| { | |
| new_files[0] = files[0]; | |
| memcpy (new_files + 1, files, file_count * sizeof (*files)); | |
| } | |
| new_files[file_count != 0] = LIBDEPS; | |
| file_count++; | |
| new_files[file_count] = NULL; | |
| files = new_files; | |
| } | |
| switch (operation) | |
| { | |
| case print_table: | |
| map_over_members (arch, print_descr, files, file_count); | |
| break; | |
| case print_files: | |
| map_over_members (arch, print_contents, files, file_count); | |
| break; | |
| case extract: | |
| map_over_members (arch, extract_file, files, file_count); | |
| break; | |
| case del: | |
| if (files != NULL) | |
| delete_members (arch, files); | |
| else | |
| output_filename = NULL; | |
| break; | |
| case move: | |
| /* PR 12558: Creating and moving at the same time does | |
| not make sense. Just create the archive instead. */ | |
| if (! silent_create) | |
| { | |
| if (files != NULL) | |
| move_members (arch, files); | |
| else | |
| output_filename = NULL; | |
| break; | |
| } | |
| /* Fall through. */ | |
| case replace: | |
| case quick_append: | |
| if (files != NULL || write_armap > 0) | |
| replace_members (arch, files, operation == quick_append); | |
| else | |
| output_filename = NULL; | |
| break; | |
| /* Shouldn't happen! */ | |
| default: | |
| /* xgettext:c-format */ | |
| fatal (_("internal error -- this option not implemented")); | |
| } | |
| } | |
| xexit (0); | |
| return 0; | |
| } | |
| bfd * | |
| open_inarch (const char *archive_filename, const char *file) | |
| { | |
| bfd **last_one; | |
| bfd *next_one; | |
| struct stat sbuf; | |
| bfd *arch; | |
| char **matching; | |
| bfd_set_error (bfd_error_no_error); | |
| if (target == NULL) | |
| target = plugin_target; | |
| if (stat (archive_filename, &sbuf) != 0) | |
| { | |
| #if !defined(__GO32__) || defined(__DJGPP__) | |
| /* FIXME: I don't understand why this fragment was ifndef'ed | |
| away for __GO32__; perhaps it was in the days of DJGPP v1.x. | |
| stat() works just fine in v2.x, so I think this should be | |
| removed. For now, I enable it for DJGPP v2. -- EZ. */ | |
| /* KLUDGE ALERT! Temporary fix until I figger why | |
| stat() is wrong ... think it's buried in GO32's IDT - Jax */ | |
| if (errno != ENOENT) | |
| bfd_fatal (archive_filename); | |
| #endif | |
| if (!operation_alters_arch) | |
| { | |
| fprintf (stderr, "%s: ", program_name); | |
| perror (archive_filename); | |
| maybequit (); | |
| return NULL; | |
| } | |
| /* If the target isn't set, try to figure out the target to use | |
| for the archive from the first object on the list. */ | |
| if (target == NULL && file != NULL) | |
| { | |
| bfd *obj; | |
| obj = bfd_openr (file, target); | |
| if (obj != NULL) | |
| { | |
| if (bfd_check_format (obj, bfd_object)) | |
| target = bfd_get_target (obj); | |
| (void) bfd_close (obj); | |
| } | |
| } | |
| /* Create an empty archive. */ | |
| arch = bfd_openw (archive_filename, target); | |
| if (arch == NULL | |
| || ! bfd_set_format (arch, bfd_archive) | |
| || ! bfd_close (arch)) | |
| bfd_fatal (archive_filename); | |
| else if (!silent_create) | |
| non_fatal (_("creating %s"), archive_filename); | |
| /* If we die creating a new archive, don't leave it around. */ | |
| output_filename = archive_filename; | |
| } | |
| arch = bfd_openr (archive_filename, target); | |
| if (arch == NULL) | |
| { | |
| bloser: | |
| bfd_fatal (archive_filename); | |
| } | |
| if (! bfd_check_format_matches (arch, bfd_archive, &matching)) | |
| { | |
| bfd_nonfatal (archive_filename); | |
| if (bfd_get_error () == bfd_error_file_ambiguously_recognized) | |
| list_matching_formats (matching); | |
| xexit (1); | |
| } | |
| if ((operation == replace || operation == quick_append) | |
| && bfd_openr_next_archived_file (arch, NULL) != NULL) | |
| { | |
| /* PR 15140: Catch attempts to convert a normal | |
| archive into a thin archive or vice versa. */ | |
| if (make_thin_archive && ! bfd_is_thin_archive (arch)) | |
| { | |
| fatal (_("Cannot convert existing library %s to thin format"), | |
| bfd_get_filename (arch)); | |
| goto bloser; | |
| } | |
| else if (! make_thin_archive && bfd_is_thin_archive (arch)) | |
| { | |
| fatal (_("Cannot convert existing thin library %s to normal format"), | |
| bfd_get_filename (arch)); | |
| goto bloser; | |
| } | |
| } | |
| last_one = &(arch->archive_next); | |
| /* Read all the contents right away, regardless. */ | |
| for (next_one = bfd_openr_next_archived_file (arch, NULL); | |
| next_one; | |
| next_one = bfd_openr_next_archived_file (arch, next_one)) | |
| { | |
| *last_one = next_one; | |
| last_one = &next_one->archive_next; | |
| } | |
| *last_one = (bfd *) NULL; | |
| if (bfd_get_error () != bfd_error_no_more_archived_files) | |
| goto bloser; | |
| return arch; | |
| } | |
| static void | |
| print_contents (bfd *abfd) | |
| { | |
| bfd_size_type ncopied = 0; | |
| bfd_size_type size; | |
| char *cbuf = (char *) xmalloc (BUFSIZE); | |
| struct stat buf; | |
| if (bfd_stat_arch_elt (abfd, &buf) != 0) | |
| /* xgettext:c-format */ | |
| fatal (_("internal stat error on %s"), bfd_get_filename (abfd)); | |
| if (verbose) | |
| printf ("\n<%s>\n\n", bfd_get_filename (abfd)); | |
| if (bfd_seek (abfd, 0, SEEK_SET) != 0) | |
| bfd_fatal (bfd_get_filename (abfd)); | |
| size = buf.st_size; | |
| while (ncopied < size) | |
| { | |
| bfd_size_type nread; | |
| bfd_size_type tocopy = size - ncopied; | |
| if (tocopy > BUFSIZE) | |
| tocopy = BUFSIZE; | |
| nread = bfd_read (cbuf, tocopy, abfd); | |
| if (nread != tocopy) | |
| /* xgettext:c-format */ | |
| fatal (_("%s is not a valid archive"), | |
| bfd_get_filename (abfd->my_archive)); | |
| /* 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, stdout) != nread) | |
| fatal ("stdout: %s", strerror (errno)); | |
| ncopied += tocopy; | |
| } | |
| free (cbuf); | |
| } | |
| static FILE * open_output_file (bfd *) ATTRIBUTE_RETURNS_NONNULL; | |
| static FILE * | |
| open_output_file (bfd * abfd) | |
| { | |
| output_filename = bfd_get_filename (abfd); | |
| /* PR binutils/17533: Do not allow directory traversal | |
| outside of the current directory tree - unless the | |
| user has explicitly specified an output directory. */ | |
| 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; | |
| } | |
| 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); | |
| } | |
| } | |
| if (verbose) | |
| printf ("x - %s\n", output_filename); | |
| 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 | |
| old one, since we know we have just read the archive header for the old | |
| one. Since most members are shorter than BUFSIZ, this means we will read | |
| the old header, read the old data, write a new inode for the new file, and | |
| write the new data, and be done. This 'optimization' is what comes from | |
| sitting next to a bare disk and hearing it every time it seeks. -- Gnu | |
| Gilmore */ | |
| void | |
| extract_file (bfd *abfd) | |
| { | |
| bfd_size_type size; | |
| struct stat buf; | |
| if (preserve_dates) | |
| memset (&buf, 0, sizeof (buf)); | |
| if (bfd_stat_arch_elt (abfd, &buf) != 0) | |
| /* xgettext:c-format */ | |
| fatal (_("internal stat error on %s"), bfd_get_filename (abfd)); | |
| size = buf.st_size; | |
| if (bfd_seek (abfd, 0, SEEK_SET) != 0) | |
| bfd_fatal (bfd_get_filename (abfd)); | |
| output_file = NULL; | |
| if (size == 0) | |
| { | |
| output_file = open_output_file (abfd); | |
| } | |
| else | |
| { | |
| bfd_size_type ncopied = 0; | |
| char *cbuf = (char *) xmalloc (BUFSIZE); | |
| while (ncopied < size) | |
| { | |
| bfd_size_type nread, tocopy; | |
| tocopy = size - ncopied; | |
| if (tocopy > BUFSIZE) | |
| tocopy = BUFSIZE; | |
| nread = bfd_read (cbuf, tocopy, abfd); | |
| if (nread != tocopy) | |
| /* xgettext:c-format */ | |
| fatal (_("%s is not a valid archive"), | |
| bfd_get_filename (abfd->my_archive)); | |
| /* See comment above; this saves disk arm motion. */ | |
| if (output_file == NULL) | |
| output_file = open_output_file (abfd); | |
| /* 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)); | |
| ncopied += tocopy; | |
| } | |
| free (cbuf); | |
| } | |
| fclose (output_file); | |
| output_file = NULL; | |
| 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 (output_filename, &buf); | |
| } | |
| output_filename = NULL; | |
| } | |
| static void | |
| write_archive (bfd *iarch) | |
| { | |
| bfd *obfd; | |
| char *old_name, *new_name; | |
| bfd *contents_head = iarch->archive_next; | |
| int tmpfd = -1; | |
| old_name = xstrdup (bfd_get_filename (iarch)); | |
| new_name = make_tempname (old_name, &tmpfd); | |
| if (new_name == NULL) | |
| bfd_fatal (_("could not create temporary file whilst writing archive")); | |
| output_filename = new_name; | |
| obfd = bfd_fdopenw (new_name, bfd_get_target (iarch), tmpfd); | |
| if (obfd == NULL) | |
| { | |
| close (tmpfd); | |
| bfd_fatal (old_name); | |
| } | |
| bfd_set_format (obfd, bfd_archive); | |
| /* Request writing the archive symbol table unless we've | |
| been explicitly requested not to. */ | |
| obfd->has_armap = write_armap >= 0; | |
| if (ar_truncate) | |
| { | |
| /* This should really use bfd_set_file_flags, but that rejects | |
| archives. */ | |
| obfd->flags |= BFD_TRADITIONAL_FORMAT; | |
| } | |
| if (deterministic) | |
| obfd->flags |= BFD_DETERMINISTIC_OUTPUT; | |
| if (full_pathname) | |
| obfd->flags |= BFD_ARCHIVE_FULL_PATH; | |
| if (make_thin_archive || bfd_is_thin_archive (iarch)) | |
| bfd_set_thin_archive (obfd, true); | |
| if (!bfd_set_archive_head (obfd, contents_head)) | |
| bfd_fatal (old_name); | |
| tmpfd = dup (tmpfd); | |
| if (!bfd_close (obfd)) | |
| bfd_fatal (old_name); | |
| output_filename = NULL; | |
| /* We don't care if this fails; we might be creating the archive. */ | |
| bfd_close (iarch); | |
| if (smart_rename (new_name, old_name, tmpfd, NULL, false) != 0) | |
| xexit (1); | |
| free (old_name); | |
| free (new_name); | |
| } | |
| /* Return a pointer to the pointer to the entry which should be rplacd'd | |
| into when altering. DEFAULT_POS should be how to interpret pos_default, | |
| and should be a pos value. */ | |
| static bfd ** | |
| get_pos_bfd (bfd **contents, enum pos default_pos, const char *default_posname) | |
| { | |
| bfd **after_bfd = contents; | |
| enum pos realpos; | |
| const char *realposname; | |
| if (postype == pos_default) | |
| { | |
| realpos = default_pos; | |
| realposname = default_posname; | |
| } | |
| else | |
| { | |
| realpos = postype; | |
| realposname = posname; | |
| } | |
| if (realpos == pos_end) | |
| { | |
| while (*after_bfd) | |
| after_bfd = &((*after_bfd)->archive_next); | |
| } | |
| else | |
| { | |
| for (; *after_bfd; after_bfd = &(*after_bfd)->archive_next) | |
| if (FILENAME_CMP (bfd_get_filename (*after_bfd), realposname) == 0) | |
| { | |
| if (realpos == pos_after) | |
| after_bfd = &(*after_bfd)->archive_next; | |
| break; | |
| } | |
| } | |
| return after_bfd; | |
| } | |
| static void | |
| delete_members (bfd *arch, char **files_to_delete) | |
| { | |
| bfd **current_ptr_ptr; | |
| bool found; | |
| bool something_changed = false; | |
| int match_count; | |
| for (; *files_to_delete != NULL; ++files_to_delete) | |
| { | |
| /* In a.out systems, the armap is optional. It's also called | |
| __.SYMDEF. So if the user asked to delete it, we should remember | |
| that fact. This isn't quite right for COFF systems (where | |
| __.SYMDEF might be regular member), but it's very unlikely | |
| to be a problem. FIXME */ | |
| if (!strcmp (*files_to_delete, "__.SYMDEF")) | |
| { | |
| arch->has_armap = false; | |
| write_armap = -1; | |
| continue; | |
| } | |
| found = false; | |
| match_count = 0; | |
| current_ptr_ptr = &(arch->archive_next); | |
| while (*current_ptr_ptr) | |
| { | |
| if (FILENAME_CMP (normalize (*files_to_delete, arch), | |
| bfd_get_filename (*current_ptr_ptr)) == 0) | |
| { | |
| ++match_count; | |
| if (counted_name_mode | |
| && match_count != counted_name_counter) | |
| { | |
| /* Counting, and didn't match on count; go on to the | |
| next one. */ | |
| } | |
| else | |
| { | |
| found = true; | |
| something_changed = true; | |
| if (verbose) | |
| printf ("d - %s\n", | |
| *files_to_delete); | |
| *current_ptr_ptr = ((*current_ptr_ptr)->archive_next); | |
| goto next_file; | |
| } | |
| } | |
| current_ptr_ptr = &((*current_ptr_ptr)->archive_next); | |
| } | |
| if (verbose && !found) | |
| { | |
| /* xgettext:c-format */ | |
| printf (_("No member named `%s'\n"), *files_to_delete); | |
| } | |
| next_file: | |
| ; | |
| } | |
| if (something_changed) | |
| write_archive (arch); | |
| else | |
| output_filename = NULL; | |
| } | |
| /* Reposition existing members within an archive */ | |
| static void | |
| move_members (bfd *arch, char **files_to_move) | |
| { | |
| bfd **after_bfd; /* New entries go after this one */ | |
| bfd **current_ptr_ptr; /* cdr pointer into contents */ | |
| for (; *files_to_move; ++files_to_move) | |
| { | |
| current_ptr_ptr = &(arch->archive_next); | |
| while (*current_ptr_ptr) | |
| { | |
| bfd *current_ptr = *current_ptr_ptr; | |
| if (FILENAME_CMP (normalize (*files_to_move, arch), | |
| bfd_get_filename (current_ptr)) == 0) | |
| { | |
| /* Move this file to the end of the list - first cut from | |
| where it is. */ | |
| bfd *link_bfd; | |
| *current_ptr_ptr = current_ptr->archive_next; | |
| /* Now glue to end */ | |
| after_bfd = get_pos_bfd (&arch->archive_next, pos_end, NULL); | |
| link_bfd = *after_bfd; | |
| *after_bfd = current_ptr; | |
| current_ptr->archive_next = link_bfd; | |
| if (verbose) | |
| printf ("m - %s\n", *files_to_move); | |
| goto next_file; | |
| } | |
| current_ptr_ptr = &((*current_ptr_ptr)->archive_next); | |
| } | |
| /* xgettext:c-format */ | |
| fatal (_("no entry %s in archive %s!"), *files_to_move, | |
| bfd_get_filename (arch)); | |
| next_file:; | |
| } | |
| write_archive (arch); | |
| } | |
| /* Ought to default to replacing in place, but this is existing practice! */ | |
| static void | |
| replace_members (bfd *arch, char **files_to_move, bool quick) | |
| { | |
| bool changed = false; | |
| bfd **after_bfd; /* New entries go after this one. */ | |
| bfd *current; | |
| bfd **current_ptr; | |
| while (files_to_move && *files_to_move) | |
| { | |
| if (! quick) | |
| { | |
| current_ptr = &arch->archive_next; | |
| while (*current_ptr) | |
| { | |
| current = *current_ptr; | |
| /* For compatibility with existing ar programs, we | |
| permit the same file to be added multiple times. */ | |
| if (FILENAME_CMP (normalize (*files_to_move, arch), | |
| normalize (bfd_get_filename (current), arch)) == 0 | |
| && current->arelt_data != NULL) | |
| { | |
| bool replaced; | |
| if (newer_only) | |
| { | |
| struct stat fsbuf, asbuf; | |
| if (stat (*files_to_move, &fsbuf) != 0) | |
| { | |
| if (errno != ENOENT) | |
| bfd_fatal (*files_to_move); | |
| goto next_file; | |
| } | |
| if (bfd_stat_arch_elt (current, &asbuf) != 0) | |
| /* xgettext:c-format */ | |
| fatal (_("internal stat error on %s"), | |
| bfd_get_filename (current)); | |
| if (fsbuf.st_mtime <= asbuf.st_mtime) | |
| /* A note about deterministic timestamps: In an | |
| archive created in a determistic manner the | |
| individual elements will either have a timestamp | |
| of 0 or SOURCE_DATE_EPOCH, depending upon the | |
| method used. This will be the value retrieved | |
| by bfd_stat_arch_elt(). | |
| The timestamp in fsbuf.st_mtime however will | |
| definitely be greater than 0, and it is unlikely | |
| to be less than SOURCE_DATE_EPOCH. (FIXME: | |
| should we test for this case case and issue an | |
| error message ?) | |
| So in either case fsbuf.st_mtime > asbuf.st_time | |
| and hence the incoming file will replace the | |
| current file. Which is what should be expected to | |
| happen. Deterministic archives have no real sense | |
| of the time/date when their elements were created, | |
| and so any updates to the archive should always | |
| result in replaced files. */ | |
| goto next_file; | |
| } | |
| after_bfd = get_pos_bfd (&arch->archive_next, pos_after, | |
| bfd_get_filename (current)); | |
| if (libdeps_bfd != NULL | |
| && FILENAME_CMP (normalize (*files_to_move, arch), | |
| LIBDEPS) == 0) | |
| { | |
| replaced = ar_emul_replace_bfd (after_bfd, libdeps_bfd, | |
| verbose); | |
| } | |
| else | |
| { | |
| replaced = ar_emul_replace (after_bfd, *files_to_move, | |
| target, verbose); | |
| } | |
| if (replaced) | |
| { | |
| /* Snip out this entry from the chain. */ | |
| *current_ptr = (*current_ptr)->archive_next; | |
| changed = true; | |
| } | |
| goto next_file; | |
| } | |
| current_ptr = &(current->archive_next); | |
| } | |
| } | |
| /* Add to the end of the archive. */ | |
| after_bfd = get_pos_bfd (&arch->archive_next, pos_end, NULL); | |
| if (libdeps_bfd != NULL | |
| && FILENAME_CMP (normalize (*files_to_move, arch), LIBDEPS) == 0) | |
| { | |
| changed |= ar_emul_append_bfd (after_bfd, libdeps_bfd, | |
| verbose, make_thin_archive); | |
| } | |
| else | |
| { | |
| changed |= ar_emul_append (after_bfd, *files_to_move, target, | |
| verbose, make_thin_archive); | |
| } | |
| next_file:; | |
| files_to_move++; | |
| } | |
| if (changed) | |
| write_archive (arch); | |
| else | |
| output_filename = NULL; | |
| } | |
| static int | |
| ranlib_only (const char *archname) | |
| { | |
| bfd *arch; | |
| if (get_file_size (archname) < 1) | |
| return 1; | |
| write_armap = 1; | |
| arch = open_inarch (archname, (char *) NULL); | |
| if (arch == NULL) | |
| xexit (1); | |
| write_archive (arch); | |
| return 0; | |
| } | |
| /* Update the timestamp of the symbol map of an archive. */ | |
| static int | |
| ranlib_touch (const char *archname) | |
| { | |
| #ifdef __GO32__ | |
| /* I don't think updating works on go32. */ | |
| ranlib_only (archname); | |
| #else | |
| int f; | |
| bfd *arch; | |
| char **matching; | |
| if (get_file_size (archname) < 1) | |
| return 1; | |
| f = open (archname, O_RDWR | O_BINARY, 0); | |
| if (f < 0) | |
| { | |
| bfd_set_error (bfd_error_system_call); | |
| bfd_fatal (archname); | |
| } | |
| arch = bfd_fdopenr (archname, (const char *) NULL, f); | |
| if (arch == NULL) | |
| bfd_fatal (archname); | |
| if (! bfd_check_format_matches (arch, bfd_archive, &matching)) | |
| { | |
| bfd_nonfatal (archname); | |
| if (bfd_get_error () == bfd_error_file_ambiguously_recognized) | |
| list_matching_formats (matching); | |
| xexit (1); | |
| } | |
| if (! bfd_has_map (arch)) | |
| /* xgettext:c-format */ | |
| fatal (_("%s: no archive map to update"), archname); | |
| if (deterministic) | |
| arch->flags |= BFD_DETERMINISTIC_OUTPUT; | |
| bfd_update_armap_timestamp (arch); | |
| if (! bfd_close (arch)) | |
| bfd_fatal (archname); | |
| #endif | |
| return 0; | |
| } | |
| /* Things which are interesting to map over all or some of the files: */ | |
| static void | |
| print_descr (bfd *abfd) | |
| { | |
| print_arelt_descr (stdout, abfd, verbose, display_offsets); | |
| } |