/* ar.c - Archive modify and extract.
- Copyright 1991, 92, 93, 94, 95, 96, 97, 98, 1999
+ Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
+ 2001, 2002
Free Software Foundation, Inc.
This file is part of GNU Binutils.
#include "aout/ar.h"
#include "libbfd.h"
#include "arsup.h"
+#include "filenames.h"
#include <sys/stat.h>
#ifdef __GO32___
static bfd **
get_pos_bfd PARAMS ((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 boolean counted_name_mode = 0;
+static int counted_name_counter = 0;
+
/* Whether to truncate names of files stored in the archive. */
static boolean 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 boolean full_pathname = false;
+
int interactive = 0;
static void
int count;
{
bfd *head;
+ int match_count;
if (count == 0)
{
}
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
{
boolean found = false;
+ match_count = 0;
for (head = arch->next; head; head = head->next)
{
PROGRESS (1);
bfd_stat_arch_elt (head, &buf);
}
if ((head->filename != NULL) &&
- (!strcmp (*files, head->filename)))
+ (!FILENAME_CMP (normalize (*files, arch), head->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);
}
if (! is_ranlib)
{
/* xgettext:c-format */
- fprintf (s, _("Usage: %s [-]{dmpqrstx}[abcilosSuvV] [member-name] archive-file file...\n"), program_name);
+ fprintf (s, _("Usage: %s [-X32_64] [-]{dmpqrstx}[abcfilNoPsSuvV] [member-name] [count] archive-file file...\n"),
+ program_name);
/* xgettext:c-format */
fprintf (s, _(" %s -M [<mri-script]\n"), program_name);
fprintf (s, _(" commands:\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"));
+ 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, _(" [u] - only replace files that are newer than current archive contents\n"));
fprintf (s, _(" generic modifiers:\n"));
fprintf (s, _(" [S] - do not build a symbol table\n"));
fprintf (s, _(" [v] - be verbose\n"));
fprintf (s, _(" [V] - display the version number\n"));
+ fprintf (s, _(" [-X32_64] - (ignored)\n"));
}
else
+ {
/* xgettext:c-format */
- fprintf (s, _("Usage: %s [-vV] archive\n"), program_name);
+ 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\
+ -h --help Print this help message\n\
+ -V --version Print version information\n"));
+ }
list_supported_targets (program_name, stderr);
if (help)
- fprintf (s, _("Report bugs to bug-gnu-utils@gnu.org\n"));
+ fprintf (s, _("Report bugs to %s\n"), REPORT_BUGS_TO);
xexit (help ? 0 : 1);
}
{
const char *filename;
+ if (full_pathname)
+ return file;
+
filename = strrchr (file, '/');
+#ifdef HAVE_DOS_BASED_FILE_SYSTEM
+ {
+ /* We could have foo/bar\\baz, or foo\\bar, or d:bar. */
+ char *bslash = strrchr (file, '\\');
+ if (filename == NULL || (bslash != NULL && bslash > filename))
+ filename = bslash;
+ if (filename == NULL && file[0] != '\0' && file[1] == ':')
+ filename = file + 1;
+ }
+#endif
if (filename != (char *) NULL)
filename++;
else
/* Remove any output file. This is only called via xatexit. */
-static char *output_filename = NULL;
+static const char *output_filename = NULL;
static FILE *output_file = NULL;
static bfd *output_bfd = NULL;
/* The option parsing should be in its own function.
It will be when I have getopt working. */
+int main PARAMS ((int, char **));
+
int
main (argc, argv)
int argc;
} operation = none;
int arg_index;
char **files;
+ int file_count;
char *inarch_filename;
int show_version;
#if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES)
setlocale (LC_MESSAGES, "");
+#endif
+#if defined (HAVE_SETLOCALE)
+ setlocale (LC_CTYPE, "");
#endif
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
char *temp;
temp = strrchr (program_name, '/');
+#ifdef HAVE_DOS_BASED_FILE_SYSTEM
+ {
+ /* We could have foo/bar\\baz, or foo\\bar, or d:bar. */
+ char *bslash = strrchr (program_name, '\\');
+ if (temp == NULL || (bslash != NULL && bslash > temp))
+ temp = bslash;
+ if (temp == NULL && program_name[0] != '\0' && program_name[1] == ':')
+ temp = program_name + 1;
+ }
+#endif
if (temp == NULL)
temp = program_name;
else
++temp;
if (strlen (temp) >= 6
- && strcmp (temp + strlen (temp) - 6, "ranlib") == 0)
+ && FILENAME_CMP (temp + strlen (temp) - 6, "ranlib") == 0)
is_ranlib = 1;
else
is_ranlib = 0;
xatexit (remove_output);
+ /* Ignored for (partial) AIX compatibility. On AIX,
+ the -X option can be used to ignore certain kinds
+ of object files in the archive (the 64-bit objects
+ or the 32-bit objects). GNU ar always looks at all
+ kinds of objects in an archive. */
+ while (argc > 1 && strcmp (argv[1], "-X32_64") == 0)
+ {
+ argv++;
+ argc--;
+ }
+
if (is_ranlib)
{
boolean touch = false;
- if (argc < 2 || strcmp (argv[1], "--help") == 0)
+ if (argc < 2
+ || strcmp (argv[1], "--help") == 0
+ || strcmp (argv[1], "-h") == 0
+ || strcmp (argv[1], "-H") == 0)
usage (0);
if (strcmp (argv[1], "-V") == 0
|| strcmp (argv[1], "-v") == 0
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;
default:
/* xgettext:c-format */
- fprintf (stderr, _("%s: illegal option -- %c\n"), program_name, c);
+ non_fatal (_("illegal option -- %c"), c);
usage (0);
}
}
if (postype != pos_default)
posname = argv[arg_index++];
+ if (counted_name_mode)
+ {
+ if (operation != extract && operation != delete)
+ fatal (_("`N' is only meaningful with the `x' and `d' options."));
+ counted_name_counter = atoi (argv[arg_index++]);
+ if (counted_name_counter <= 0)
+ fatal (_("Value for `N' must be positive."));
+ }
+
inarch_filename = argv[arg_index++];
files = arg_index < argc ? argv + arg_index : NULL;
+ file_count = argc - arg_index;
#if 0
/* We don't use do_quick_append any more. Too many systems
switch (operation)
{
case print_table:
- map_over_members (arch, print_descr, files, argc - 3);
+ map_over_members (arch, print_descr, files, file_count);
break;
case print_files:
- map_over_members (arch, print_contents, files, argc - 3);
+ map_over_members (arch, print_contents, files, file_count);
break;
case extract:
- map_over_members (arch, extract_file, files, argc - 3);
+ map_over_members (arch, extract_file, files, file_count);
break;
case delete:
if (files != NULL)
delete_members (arch, files);
+ else
+ output_filename = NULL;
break;
case move:
if (files != NULL)
move_members (arch, files);
+ else
+ output_filename = NULL;
break;
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 */
- fprintf (stderr, _("%s: internal error -- this option not implemented\n"),
- program_name);
- xexit (1);
+ fatal (_("internal error -- this option not implemented"));
}
}
if (stat (archive_filename, &sbuf) != 0)
{
-#ifndef __GO32__
+#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
- */
+ stat() is wrong ... think it's buried in GO32's IDT - Jax */
if (errno != ENOENT)
bfd_fatal (archive_filename);
#endif
|| ! bfd_set_format (arch, bfd_archive)
|| ! bfd_close (arch))
bfd_fatal (archive_filename);
+
+ /* If we die creating a new archive, don't leave it around. */
+ output_filename = archive_filename;
}
arch = bfd_openr (archive_filename, target);
/* xgettext:c-format */
printf (_("\n<member %s>\n\n"), bfd_get_filename (abfd));
- bfd_seek (abfd, 0, SEEK_SET);
+ bfd_seek (abfd, (file_ptr) 0, SEEK_SET);
size = buf.st_size;
while (ncopied < size)
if (tocopy > BUFSIZE)
tocopy = BUFSIZE;
- nread = bfd_read (cbuf, 1, tocopy, abfd); /* oops -- broke
- abstraction! */
+ nread = bfd_bread (cbuf, (bfd_size_type) tocopy, abfd);
if (nread != tocopy)
/* xgettext:c-format */
fatal (_("%s is not a valid archive"),
if (verbose)
printf ("x - %s\n", bfd_get_filename (abfd));
- bfd_seek (abfd, 0, SEEK_SET);
+ bfd_seek (abfd, (file_ptr) 0, SEEK_SET);
ostream = NULL;
if (size == 0)
if (tocopy > BUFSIZE)
tocopy = BUFSIZE;
- nread = bfd_read (cbuf, 1, tocopy, abfd);
+ nread = bfd_bread (cbuf, (bfd_size_type) tocopy, abfd);
if (nread != tocopy)
/* xgettext:c-format */
fatal (_("%s is not a valid archive"),
if (stat (archive_filename, &sbuf) != 0)
{
-#ifndef __GO32__
+#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.
+
+ (And yes, I know this is all unused, but somebody, someday,
+ might wish to resurrect this again... -- EZ. */
/* KLUDGE ALERT! Temporary fix until I figger why
- * stat() is wrong ... think it's buried in GO32's IDT
- * - Jax
- */
+ stat() is wrong ... think it's buried in GO32's IDT - Jax */
if (errno != ENOENT)
bfd_fatal (archive_filename);
fwrite (ARMAG, 1, SARMAG, ofile);
if (!silent_create)
/* xgettext:c-format */
- fprintf (stderr, _("%s: creating %s\n"),
- program_name, archive_filename);
+ non_fatal (_("creating %s"), archive_filename);
}
if (ar_truncate)
else
{
for (; *after_bfd; after_bfd = &(*after_bfd)->next)
- if (strcmp ((*after_bfd)->filename, realposname) == 0)
+ if (FILENAME_CMP ((*after_bfd)->filename, realposname) == 0)
{
if (realpos == pos_after)
after_bfd = &(*after_bfd)->next;
bfd **current_ptr_ptr;
boolean found;
boolean 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
}
found = false;
+ match_count = 0;
current_ptr_ptr = &(arch->next);
while (*current_ptr_ptr)
{
- if (strcmp (*files_to_delete, (*current_ptr_ptr)->filename) == 0)
- {
- found = true;
- something_changed = true;
- if (verbose)
- printf ("d - %s\n",
- *files_to_delete);
- *current_ptr_ptr = ((*current_ptr_ptr)->next);
- goto next_file;
- }
- else
+ if (FILENAME_CMP (normalize (*files_to_delete, arch),
+ (*current_ptr_ptr)->filename) == 0)
{
- current_ptr_ptr = &((*current_ptr_ptr)->next);
+ ++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)->next);
+ goto next_file;
+ }
}
+
+ current_ptr_ptr = &((*current_ptr_ptr)->next);
}
if (verbose && found == false)
}
if (something_changed == true)
- {
- write_archive (arch);
- }
+ write_archive (arch);
+ else
+ output_filename = NULL;
}
while (*current_ptr_ptr)
{
bfd *current_ptr = *current_ptr_ptr;
- if (strcmp (normalize (*files_to_move, arch),
- current_ptr->filename) == 0)
+ if (FILENAME_CMP (normalize (*files_to_move, arch),
+ current_ptr->filename) == 0)
{
/* Move this file to the end of the list - first cut from
where it is. */
current_ptr_ptr = &((*current_ptr_ptr)->next);
}
/* xgettext:c-format */
- fprintf (stderr, _("%s: no entry %s in archive %s!\n"),
- program_name, *files_to_move, arch->filename);
- xexit (1);
+ fatal (_("no entry %s in archive %s!"), *files_to_move, arch->filename);
+
next_file:;
}
/* For compatibility with existing ar programs, we
permit the same file to be added multiple times. */
- if (strcmp (normalize (*files_to_move, arch),
- normalize (current->filename, arch)) == 0
+ if (FILENAME_CMP (normalize (*files_to_move, arch),
+ normalize (current->filename, arch)) == 0
&& current->arelt_data != NULL)
{
if (newer_only)
if (changed)
write_archive (arch);
+ else
+ output_filename = NULL;
}
static void