/* ar.c - Archive modify and extract.
- Copyright 1991, 92, 93, 94 Free Software Foundation, Inc.
+ Copyright 1991, 92, 93, 94, 95, 1996 Free Software Foundation, Inc.
This file is part of GNU Binutils.
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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
\f
/*
Bugs: should use getopt the way tar does (complete w/optional -) and
more consistant.
*/
#include "bfd.h"
-#include "sysdep.h"
#include "libiberty.h"
+#include "progress.h"
#include "bucomm.h"
#include "aout/ar.h"
#include "libbfd.h"
#include "arsup.h"
-#include <stdio.h>
-#ifdef POSIX_UTIME
+#include <sys/stat.h>
+
+#ifdef HAVE_GOOD_UTIME_H
#include <utime.h>
-#else /* ! POSIX_UTIME */
-#ifdef USE_UTIME
-#include <time.h>
-#else /* ! USE_UTIME */
+#else /* ! HAVE_GOOD_UTIME_H */
+#ifdef HAVE_UTIMES
#include <sys/time.h>
-#endif /* ! USE_UTIME */
-#endif /* ! POSIX_UTIME */
-#include <errno.h>
-#ifndef errno
-extern int errno;
-#endif
-#define BUFSIZE 8192
+#endif /* HAVE_UTIMES */
+#endif /* ! HAVE_GOOD_UTIME_H */
#ifdef __GO32___
#define EXT_NAME_LEN 3 /* bufflen of addition to name if it's MS-DOS */
#define EXT_NAME_LEN 6 /* ditto for *NIX */
#endif
+#define BUFSIZE 8192
+
/* Kludge declaration from BFD! This is ugly! FIXME! XXX */
struct ar_hdr *
/* Forward declarations */
+static const char *
+normalize PARAMS ((const char *, bfd *));
+
+static void
+remove_output PARAMS ((void));
+
static void
map_over_members PARAMS ((bfd *, void (*)(bfd *), char **, int));
static void
delete_members PARAMS ((bfd *, char **files_to_delete));
+#if 0
static void
do_quick_append PARAMS ((const char *archive_filename,
char **files_to_append));
+#endif
static void
move_members PARAMS ((bfd *, char **files_to_move));
static void
-replace_members PARAMS ((bfd *, char **files_to_replace));
+replace_members PARAMS ((bfd *, char **files_to_replace, boolean quick));
static void
print_descr PARAMS ((bfd * abfd));
pos_default, pos_before, pos_after, pos_end
} postype = pos_default;
+/* Whether to truncate names of files stored in the archive. */
+static boolean ar_truncate = false;
+
int interactive = 0;
void
if (count == 0)
{
for (head = arch->next; head; head = head->next)
- function (head);
+ {
+ PROGRESS (1);
+ function (head);
+ }
return;
}
/* This may appear to be a baroque way of accomplishing what we want.
for (; count > 0; files++, count--)
{
boolean found = false;
+
for (head = arch->next; head; head = head->next)
{
+ PROGRESS (1);
if (head->filename == NULL)
{
/* Some archive formats don't get the filenames filled in
\f
boolean operation_alters_arch = false;
-extern char *program_version;
-
void
-do_show_version ()
+usage (help)
+ int help;
{
- printf ("GNU %s version %s\n", program_name, program_version);
- exit (0);
-}
+ FILE *s;
-void
-usage ()
-{
- if (is_ranlib == 0)
- fprintf (stderr, "\
+ s = help ? stdout : stderr;
+ if (! is_ranlib)
+ fprintf (s, "\
Usage: %s [-]{dmpqrtx}[abcilosuvV] [member-name] archive-file file...\n\
%s -M [<mri-script]\n",
program_name, program_name);
else
- fprintf (stderr, "\
+ fprintf (s, "\
Usage: %s [-vV] archive\n", program_name);
- exit (1);
+
+ list_supported_targets (program_name, stderr);
+
+ if (help)
+ fprintf (s, "Report bugs to bug-gnu-utils@prep.ai.mit.edu\n");
+
+ 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 (file, abfd)
+ const char *file;
+ bfd *abfd;
+{
+ const char *filename;
+
+ filename = strrchr (file, '/');
+ if (filename != (char *) NULL)
+ filename++;
+ else
+ filename = 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 char *output_filename = NULL;
+static FILE *output_file = NULL;
+static bfd *output_bfd = NULL;
+
+static void
+remove_output ()
+{
+ if (output_filename != NULL)
+ {
+ if (output_bfd != NULL && output_bfd->iostream != NULL)
+ fclose ((FILE *) (output_bfd->iostream));
+ if (output_file != NULL)
+ fclose (output_file);
+ unlink (output_filename);
+ }
}
/* The option parsing should be in its own function.
int arg_index;
char **files;
char *inarch_filename;
- char *temp;
int show_version;
program_name = argv[0];
xmalloc_set_program_name (program_name);
+ if (is_ranlib < 0)
+ {
+ char *temp;
+
+ temp = strrchr (program_name, '/');
+ if (temp == NULL)
+ temp = program_name;
+ else
+ ++temp;
+ if (strlen (temp) >= 6
+ && strcmp (temp + strlen (temp) - 6, "ranlib") == 0)
+ is_ranlib = 1;
+ else
+ is_ranlib = 0;
+ }
+
+ if (argc > 1 && argv[1][0] == '-')
+ {
+ if (strcmp (argv[1], "--help") == 0)
+ usage (1);
+ else if (strcmp (argv[1], "--version") == 0)
+ {
+ if (is_ranlib)
+ print_version ("ranlib");
+ else
+ print_version ("ar");
+ }
+ }
+
+ START_PROGRESS (program_name, 0);
+
bfd_init ();
show_version = 0;
- temp = strrchr (program_name, '/');
- if (temp == (char *) NULL)
- temp = program_name; /* shouldn't happen, but... */
- else
- ++temp;
- if (is_ranlib > 0 || (is_ranlib < 0 && strcmp (temp, "ranlib") == 0))
+ xatexit (remove_output);
+
+ if (is_ranlib)
{
boolean touch = false;
- is_ranlib = 1;
- if (argc < 2)
+ if (argc < 2 || strcmp (argv[1], "--help") == 0)
usage ();
if (strcmp (argv[1], "-V") == 0
|| strcmp (argv[1], "-v") == 0
|| strncmp (argv[1], "--v", 3) == 0)
- do_show_version ();
+ print_version ("ranlib");
arg_index = 1;
if (strcmp (argv[1], "-t") == 0)
{
ranlib_touch (argv[arg_index]);
++arg_index;
}
- exit (0);
+ xexit (0);
}
- else
- is_ranlib = 0;
if (argc == 2 && strcmp (argv[1], "-M") == 0)
{
mri_emul ();
- exit (0);
+ xexit (0);
}
if (argc < 2)
case 'M':
mri_mode = 1;
break;
+ case 'f':
+ ar_truncate = true;
+ break;
default:
fprintf (stderr, "%s: illegal option -- %c\n", program_name, c);
usage ();
}
if (show_version)
- do_show_version ();
+ print_version ("ar");
if (argc < 3)
usage ();
{
bfd *arch;
+ /* 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)
{
ranlib_only (argv[2]);
- exit (0);
+ xexit (0);
}
if (operation == none)
files = arg_index < argc ? argv + arg_index : NULL;
+#if 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 do a quick append if we need to construct an
+ extended name table, because do_quick_append won't be able to
+ rebuild the name table. Unfortunately, at this point we
+ don't actually know the maximum name length permitted by this
+ object file format. So, we guess. FIXME. */
+ if (operation == quick_append && ! ar_truncate)
+ {
+ char **chk;
+
+ for (chk = files; chk != NULL && *chk != '\0'; chk++)
+ {
+ if (strlen (normalize (*chk, (bfd *) NULL)) > 14)
+ {
+ operation = replace;
+ break;
+ }
+ }
+ }
+
if (operation == quick_append)
{
/* Note that quick appending to a non-existent archive creates it,
even if there are no files to append. */
do_quick_append (inarch_filename, files);
- exit (0);
+ xexit (0);
}
+#endif
- arch = open_inarch (inarch_filename);
+ arch = open_inarch (inarch_filename,
+ files == NULL ? (char *) NULL : files[0]);
switch (operation)
{
break;
case replace:
+ case quick_append:
if (files != NULL || write_armap > 0)
- replace_members (arch, files);
+ replace_members (arch, files, operation == quick_append);
break;
/* Shouldn't happen! */
default:
fprintf (stderr, "%s: internal error -- this option not implemented\n",
program_name);
- exit (1);
+ xexit (1);
}
}
- return 0;
-}
-static char *
-normalize (file)
- char *file;
-{
- char *filename = strrchr (file, '/');
- if (filename != (char *) NULL)
- {
- filename++;
- }
- else
- {
- filename = file;
- }
- return filename;
+ END_PROGRESS (program_name);
+
+ xexit (0);
+ return 0;
}
bfd *
-open_inarch (archive_filename)
+open_inarch (archive_filename, file)
const char *archive_filename;
+ const char *file;
{
+ const char *target;
bfd **last_one;
bfd *next_one;
struct stat sbuf;
bfd *arch;
+ char **matching;
bfd_set_error (bfd_error_no_error);
+ target = NULL;
+
if (stat (archive_filename, &sbuf) != 0)
{
+ bfd *obj;
#ifndef __GO32__
return NULL;
}
- /* This routine is one way to forcibly create the archive. */
+ /* Try to figure out the target to use for the archive from the
+ first object on the list. */
+ obj = bfd_openr (file, NULL);
+ if (obj != NULL)
+ {
+ if (bfd_check_format (obj, bfd_object))
+ target = bfd_get_target (obj);
+ (void) bfd_close (obj);
+ }
- do_quick_append (archive_filename, 0);
+ /* 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);
}
- arch = bfd_openr (archive_filename, NULL);
+ arch = bfd_openr (archive_filename, target);
if (arch == NULL)
{
bloser:
bfd_fatal (archive_filename);
}
- if (bfd_check_format (arch, bfd_archive) != true)
- fatal ("%s is not an archive", 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);
+ free (matching);
+ }
+ xexit (1);
+ }
+
last_one = &(arch->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))
{
+ PROGRESS (1);
*last_one = next_one;
last_one = &next_one->next;
}
bfd *abfd;
{
int ncopied = 0;
+ char *cbuf = xmalloc (BUFSIZE);
struct stat buf;
long size;
if (bfd_stat_arch_elt (abfd, &buf) != 0)
size = buf.st_size;
while (ncopied < size)
{
- char cbuf[BUFSIZE];
+
int nread;
int tocopy = size - ncopied;
if (tocopy > BUFSIZE)
fwrite (cbuf, 1, nread, stdout);
ncopied += tocopy;
}
+ free (cbuf);
}
/* Extract a member of the archive into its own file.
bfd *abfd;
{
FILE *ostream;
- char cbuf[BUFSIZE];
+ char *cbuf = xmalloc (BUFSIZE);
int nread, tocopy;
int ncopied = 0;
long size;
if (size == 0)
{
/* Seems like an abstraction violation, eh? Well it's OK! */
+ output_filename = bfd_get_filename (abfd);
+
ostream = fopen (bfd_get_filename (abfd), FOPEN_WB);
if (!ostream)
{
perror (bfd_get_filename (abfd));
- exit (1);
+ xexit (1);
}
+
+ output_file = ostream;
}
else
while (ncopied < size)
if (!ostream)
{
/* Seems like an abstraction violation, eh? Well it's OK! */
+ output_filename = bfd_get_filename (abfd);
+
ostream = fopen (bfd_get_filename (abfd), FOPEN_WB);
if (!ostream)
{
perror (bfd_get_filename (abfd));
- exit (1);
+ xexit (1);
}
+
+ output_file = ostream;
}
fwrite (cbuf, 1, nread, ostream);
ncopied += tocopy;
}
fclose (ostream);
+
+ output_file = NULL;
+ output_filename = NULL;
+
chmod (bfd_get_filename (abfd), buf.st_mode);
if (preserve_dates)
{
-#ifdef POSIX_UTIME
+#ifdef HAVE_GOOD_UTIME_H
struct utimbuf tb;
tb.actime = buf.st_mtime;
tb.modtime = buf.st_mtime;
utime (bfd_get_filename (abfd), &tb); /* FIXME check result */
-#else /* ! POSIX_UTIME */
-#ifdef USE_UTIME
+#else /* ! HAVE_GOOD_UTIME_H */
+#ifndef HAVE_UTIMES
long tb[2];
tb[0] = buf.st_mtime;
tb[1] = buf.st_mtime;
utime (bfd_get_filename (abfd), tb); /* FIXME check result */
-#else /* ! USE_UTIME */
+#else /* HAVE_UTIMES */
struct timeval tv[2];
tv[0].tv_sec = buf.st_mtime;
tv[0].tv_usec = 0;
tv[1].tv_sec = buf.st_mtime;
tv[1].tv_usec = 0;
utimes (bfd_get_filename (abfd), tv); /* FIXME check result */
-#endif /* ! USE_UTIME */
-#endif /* ! POSIX_UTIME */
+#endif /* HAVE_UTIMES */
+#endif /* ! HAVE_GOOD_UTIME_H */
}
+free (cbuf);
}
+#if 0
+
+/* We don't use this anymore. Too many systems expect ar to rebuild
+ the symbol table even when q is used. */
+
/* Just do it quickly; don't worry about dups, armap, or anything like that */
static void
char **files_to_append;
{
FILE *ofile, *ifile;
- char buf[BUFSIZE];
+ char *buf = xmalloc (BUFSIZE);
long tocopy, thistime;
bfd *temp;
struct stat sbuf;
if (ofile == NULL)
{
perror (program_name);
- exit (1);
+ xexit (1);
}
temp = bfd_openr (archive_filename, NULL);
program_name, archive_filename);
}
+ if (ar_truncate)
+ temp->flags |= BFD_TRADITIONAL_FORMAT;
+
/* assume it's an achive, go straight to the end, sans $200 */
fseek (ofile, 0, 2);
}
fclose (ofile);
bfd_close (temp);
+ free (buf);
}
+#endif /* 0 */
static void
write_archive (iarch)
bfd *iarch;
{
bfd *obfd;
- int namelen = strlen (bfd_get_filename (iarch));
- char *old_name = xmalloc (namelen + 1);
- char *new_name = xmalloc (namelen + EXT_NAME_LEN);
+ char *old_name, *new_name;
bfd *contents_head = iarch->next;
+ old_name = xmalloc (strlen (bfd_get_filename (iarch)) + 1);
strcpy (old_name, bfd_get_filename (iarch));
- strcpy (new_name, bfd_get_filename (iarch));
+ new_name = make_tempname (old_name);
-#ifdef __GO32__ /* avoid long .extensions for MS-DOS */
- strcpy (new_name + namelen, "-a");
-#else
- strcpy (new_name + namelen, "-art");
-#endif
+ output_filename = new_name;
obfd = bfd_openw (new_name, bfd_get_target (iarch));
if (obfd == NULL)
bfd_fatal (old_name);
+ output_bfd = obfd;
+
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 (bfd_set_archive_head (obfd, contents_head) != true)
bfd_fatal (old_name);
if (!bfd_close (obfd))
bfd_fatal (old_name);
+ output_bfd = NULL;
+ output_filename = NULL;
+
/* We don't care if this fails; we might be creating the archive. */
bfd_close (iarch);
unlink (old_name);
while (*current_ptr_ptr)
{
bfd *current_ptr = *current_ptr_ptr;
- if (strcmp (normalize (*files_to_move), current_ptr->filename) == 0)
+ if (strcmp (normalize (*files_to_move, arch),
+ current_ptr->filename) == 0)
{
/* Move this file to the end of the list - first cut from
where it is. */
}
fprintf (stderr, "%s: no entry %s in archive %s!\n",
program_name, *files_to_move, arch->filename);
- exit (1);
+ xexit (1);
next_file:;
}
/* Ought to default to replacing in place, but this is existing practice! */
static void
-replace_members (arch, files_to_move)
+replace_members (arch, files_to_move, quick)
bfd *arch;
char **files_to_move;
+ boolean quick;
{
+ boolean changed = false;
bfd **after_bfd; /* New entries go after this one */
bfd *current;
bfd **current_ptr;
while (files_to_move && *files_to_move)
{
- current_ptr = &arch->next;
- while (*current_ptr)
+ if (! quick)
{
- current = *current_ptr;
-
- if (!strcmp (normalize (*files_to_move), current->filename))
+ current_ptr = &arch->next;
+ while (*current_ptr)
{
- if (newer_only)
- {
- struct stat fsbuf, asbuf;
+ current = *current_ptr;
- if (current->arelt_data == NULL)
+ /* 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
+ && current->arelt_data != NULL)
+ {
+ if (newer_only)
{
- /* This can only happen if you specify a file on the
- command line more than once. */
- fprintf (stderr,
- "%s: duplicate file specified: %s -- skipping\n",
- program_name, *files_to_move);
- goto next_file;
+ 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)
+ fatal ("internal stat error on %s", current->filename);
+
+ if (fsbuf.st_mtime <= asbuf.st_mtime)
+ goto next_file;
}
- if (stat (*files_to_move, &fsbuf) != 0)
+ /* snip out this entry from the chain */
+ *current_ptr = current->next;
+
+ after_bfd = get_pos_bfd (&arch->next, pos_end);
+ temp = *after_bfd;
+ *after_bfd = bfd_openr (*files_to_move, NULL);
+ if (*after_bfd == (bfd *) NULL)
{
- if (errno != ENOENT)
- bfd_fatal (*files_to_move);
- goto next_file;
+ bfd_fatal (*files_to_move);
}
- if (bfd_stat_arch_elt (current, &asbuf) != 0)
- fatal ("internal stat error on %s", current->filename);
-
- if (fsbuf.st_mtime <= asbuf.st_mtime)
- goto next_file;
- }
+ (*after_bfd)->next = temp;
- /* snip out this entry from the chain */
- *current_ptr = current->next;
+ if (verbose)
+ {
+ printf ("r - %s\n", *files_to_move);
+ }
- after_bfd = get_pos_bfd (&arch->next, pos_end);
- temp = *after_bfd;
- *after_bfd = bfd_openr (*files_to_move, NULL);
- if (*after_bfd == (bfd *) NULL)
- {
- bfd_fatal (*files_to_move);
- }
- (*after_bfd)->next = temp;
+ changed = true;
- if (verbose)
- {
- printf ("%c - %s\n", (postype == pos_after ? 'r' : 'a'),
- *files_to_move);
+ goto next_file;
}
- goto next_file;
+ current_ptr = &(current->next);
}
- current_ptr = &(current->next);
}
- /* It isn't in there, so add to end */
+ /* Add to the end of the archive. */
after_bfd = get_pos_bfd (&arch->next, pos_end);
temp = *after_bfd;
}
if (verbose)
{
- printf ("c - %s\n", *files_to_move);
+ printf ("a - %s\n", *files_to_move);
}
(*after_bfd)->next = temp;
+ changed = true;
+
next_file:;
files_to_move++;
}
- write_archive (arch);
+ if (changed)
+ write_archive (arch);
}
static void
bfd *arch;
write_armap = 1;
- arch = open_inarch (archname);
+ arch = open_inarch (archname, (char *) NULL);
if (arch == NULL)
- exit (1);
+ xexit (1);
write_archive (arch);
}
#else
int f;
bfd *arch;
+ char **matching;
f = open (archname, O_RDWR, 0);
if (f < 0)
}
arch = bfd_fdopenr (archname, (const char *) NULL, f);
- if (arch == NULL
- || ! bfd_check_format (arch, bfd_archive))
+ 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);
+ free (matching);
+ }
+ xexit (1);
+ }
+
+ if (! bfd_has_map (arch))
+ fatal ("%s: no archive map to update", archname);
bfd_update_armap_timestamp (arch);