summaryrefslogtreecommitdiff
path: root/arscan.c
diff options
context:
space:
mode:
Diffstat (limited to 'arscan.c')
-rw-r--r--arscan.c867
1 files changed, 867 insertions, 0 deletions
diff --git a/arscan.c b/arscan.c
new file mode 100644
index 0000000..4ef8375
--- /dev/null
+++ b/arscan.c
@@ -0,0 +1,867 @@
+/* Library function for scanning an archive file.
+Copyright (C) 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
+1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
+2010 Free Software Foundation, Inc.
+This file is part of GNU Make.
+
+GNU Make 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.
+
+GNU Make 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, see <http://www.gnu.org/licenses/>. */
+
+#include "make.h"
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#else
+#include <sys/file.h>
+#endif
+
+#ifndef NO_ARCHIVES
+
+#ifdef VMS
+#include <lbrdef.h>
+#include <mhddef.h>
+#include <credef.h>
+#include <descrip.h>
+#include <ctype.h>
+#if __DECC
+#include <unixlib.h>
+#include <lbr$routines.h>
+#endif
+
+static void *VMS_lib_idx;
+
+static char *VMS_saved_memname;
+
+static time_t VMS_member_date;
+
+static long int (*VMS_function) ();
+
+static int
+VMS_get_member_info (struct dsc$descriptor_s *module, unsigned long *rfa)
+{
+ int status, i;
+ long int fnval;
+
+ time_t val;
+
+ static struct dsc$descriptor_s bufdesc =
+ { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL };
+
+ struct mhddef *mhd;
+ char filename[128];
+
+ bufdesc.dsc$a_pointer = filename;
+ bufdesc.dsc$w_length = sizeof (filename);
+
+ status = lbr$set_module (&VMS_lib_idx, rfa, &bufdesc,
+ &bufdesc.dsc$w_length, 0);
+ if (! (status & 1))
+ {
+ error (NILF, _("lbr$set_module() failed to extract module info, status = %d"),
+ status);
+
+ lbr$close (&VMS_lib_idx);
+
+ return 0;
+ }
+
+ mhd = (struct mhddef *) filename;
+
+#ifdef __DECC
+ /* John Fowler <jfowler@nyx.net> writes this is needed in his environment,
+ * but that decc$fix_time() isn't documented to work this way. Let me
+ * know if this causes problems in other VMS environments.
+ */
+ {
+ /* Modified by M. Gehre at 11-JAN-2008 because old formula is wrong:
+ * val = decc$fix_time (&mhd->mhd$l_datim) + timezone - daylight*3600;
+ * a) daylight specifies, if the timezone has daylight saving enabled, not
+ * if it is active
+ * b) what we need is the information, if daylight saving was active, if
+ * the library module was replaced. This information we get using the
+ * localtime function
+ */
+
+ struct tm *tmp;
+
+ /* Conversion from VMS time to C time */
+ val = decc$fix_time (&mhd->mhd$l_datim);
+
+ /*
+ * Conversion from local time (stored in library) to GMT (needed for gmake)
+ * Note: The tm_gmtoff element is a VMS extension to the ANSI standard.
+ */
+ tmp = localtime (&val);
+ val -= tmp->tm_gmtoff;
+ }
+#endif
+
+ for (i = 0; i < module->dsc$w_length; i++)
+ filename[i] = _tolower ((unsigned char)module->dsc$a_pointer[i]);
+
+ filename[i] = '\0';
+
+ VMS_member_date = (time_t) -1;
+
+ fnval =
+ (*VMS_function) (-1, filename, 0, 0, 0, 0, val, 0, 0, 0,
+ VMS_saved_memname);
+
+ if (fnval)
+ {
+ VMS_member_date = fnval;
+ return 0;
+ }
+ else
+ return 1;
+}
+
+/* Takes three arguments ARCHIVE, FUNCTION and ARG.
+
+ Open the archive named ARCHIVE, find its members one by one,
+ and for each one call FUNCTION with the following arguments:
+ archive file descriptor for reading the data,
+ member name,
+ member name might be truncated flag,
+ member header position in file,
+ member data position in file,
+ member data size,
+ member date,
+ member uid,
+ member gid,
+ member protection mode,
+ ARG.
+
+ NOTE: on VMS systems, only name, date, and arg are meaningful!
+
+ The descriptor is poised to read the data of the member
+ when FUNCTION is called. It does not matter how much
+ data FUNCTION reads.
+
+ If FUNCTION returns nonzero, we immediately return
+ what FUNCTION returned.
+
+ Returns -1 if archive does not exist,
+ Returns -2 if archive has invalid format.
+ Returns 0 if have scanned successfully. */
+
+long int
+ar_scan (const char *archive, ar_member_func_t function, const void *arg)
+{
+ char *p;
+
+ static struct dsc$descriptor_s libdesc =
+ { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL };
+
+ unsigned long func = LBR$C_READ;
+ unsigned long type = LBR$C_TYP_UNK;
+ unsigned long index = 1;
+
+ int status;
+
+ status = lbr$ini_control (&VMS_lib_idx, &func, &type, 0);
+
+ if (! (status & 1))
+ {
+ error (NILF, _("lbr$ini_control() failed with status = %d"), status);
+ return -2;
+ }
+
+ /* there is no such descriptor with "const char *dsc$a_pointer" */
+ libdesc.dsc$a_pointer = (char *)archive;
+ libdesc.dsc$w_length = strlen (archive);
+
+ status = lbr$open (&VMS_lib_idx, &libdesc, 0, 0, 0, 0, 0);
+
+ if (! (status & 1))
+ {
+ error (NILF, _("unable to open library `%s' to lookup member `%s'"),
+ archive, (char *)arg);
+ return -1;
+ }
+
+ VMS_saved_memname = (char *)arg;
+
+ /* For comparison, delete .obj from arg name. */
+
+ p = strrchr (VMS_saved_memname, '.');
+ if (p)
+ *p = '\0';
+
+ VMS_function = function;
+
+ VMS_member_date = (time_t) -1;
+ lbr$get_index (&VMS_lib_idx, &index, VMS_get_member_info, 0);
+
+ /* Undo the damage. */
+ if (p)
+ *p = '.';
+
+ lbr$close (&VMS_lib_idx);
+
+ return VMS_member_date > 0 ? VMS_member_date : 0;
+}
+
+#else /* !VMS */
+
+/* SCO Unix's compiler defines both of these. */
+#ifdef M_UNIX
+#undef M_XENIX
+#endif
+
+/* On the sun386i and in System V rel 3, ar.h defines two different archive
+ formats depending upon whether you have defined PORTAR (normal) or PORT5AR
+ (System V Release 1). There is no default, one or the other must be defined
+ to have a nonzero value. */
+
+#if (!defined (PORTAR) || PORTAR == 0) && (!defined (PORT5AR) || PORT5AR == 0)
+#undef PORTAR
+#ifdef M_XENIX
+/* According to Jim Sievert <jas1@rsvl.unisys.com>, for SCO XENIX defining
+ PORTAR to 1 gets the wrong archive format, and defining it to 0 gets the
+ right one. */
+#define PORTAR 0
+#else
+#define PORTAR 1
+#endif
+#endif
+
+/* On AIX, define these symbols to be sure to get both archive formats.
+ AIX 4.3 introduced the "big" archive format to support 64-bit object
+ files, so on AIX 4.3 systems we need to support both the "normal" and
+ "big" archive formats. An archive's format is indicated in the
+ "fl_magic" field of the "FL_HDR" structure. For a normal archive,
+ this field will be the string defined by the AIAMAG symbol. For a
+ "big" archive, it will be the string defined by the AIAMAGBIG symbol
+ (at least on AIX it works this way).
+
+ Note: we'll define these symbols regardless of which AIX version
+ we're compiling on, but this is okay since we'll use the new symbols
+ only if they're present. */
+#ifdef _AIX
+# define __AR_SMALL__
+# define __AR_BIG__
+#endif
+
+#ifndef WINDOWS32
+# ifndef __BEOS__
+# include <ar.h>
+# else
+ /* BeOS 5 doesn't have <ar.h> but has archives in the same format
+ * as many other Unices. This was taken from GNU binutils for BeOS.
+ */
+# define ARMAG "!<arch>\n" /* String that begins an archive file. */
+# define SARMAG 8 /* Size of that string. */
+# define ARFMAG "`\n" /* String in ar_fmag at end of each header. */
+struct ar_hdr
+ {
+ char ar_name[16]; /* Member file name, sometimes / terminated. */
+ char ar_date[12]; /* File date, decimal seconds since Epoch. */
+ char ar_uid[6], ar_gid[6]; /* User and group IDs, in ASCII decimal. */
+ char ar_mode[8]; /* File mode, in ASCII octal. */
+ char ar_size[10]; /* File size, in ASCII decimal. */
+ char ar_fmag[2]; /* Always contains ARFMAG. */
+ };
+# endif
+# define TOCHAR(_m) (_m)
+#else
+/* These should allow us to read Windows (VC++) libraries (according to Frank
+ * Libbrecht <frankl@abzx.belgium.hp.com>)
+ */
+# include <windows.h>
+# include <windef.h>
+# include <io.h>
+# define ARMAG IMAGE_ARCHIVE_START
+# define SARMAG IMAGE_ARCHIVE_START_SIZE
+# define ar_hdr _IMAGE_ARCHIVE_MEMBER_HEADER
+# define ar_name Name
+# define ar_mode Mode
+# define ar_size Size
+# define ar_date Date
+# define ar_uid UserID
+# define ar_gid GroupID
+/* In Windows the member names have type BYTE so we must cast them. */
+# define TOCHAR(_m) ((char *)(_m))
+#endif
+
+/* Cray's <ar.h> apparently defines this. */
+#ifndef AR_HDR_SIZE
+# define AR_HDR_SIZE (sizeof (struct ar_hdr))
+#endif
+
+/* Takes three arguments ARCHIVE, FUNCTION and ARG.
+
+ Open the archive named ARCHIVE, find its members one by one,
+ and for each one call FUNCTION with the following arguments:
+ archive file descriptor for reading the data,
+ member name,
+ member name might be truncated flag,
+ member header position in file,
+ member data position in file,
+ member data size,
+ member date,
+ member uid,
+ member gid,
+ member protection mode,
+ ARG.
+
+ The descriptor is poised to read the data of the member
+ when FUNCTION is called. It does not matter how much
+ data FUNCTION reads.
+
+ If FUNCTION returns nonzero, we immediately return
+ what FUNCTION returned.
+
+ Returns -1 if archive does not exist,
+ Returns -2 if archive has invalid format.
+ Returns 0 if have scanned successfully. */
+
+long int
+ar_scan (const char *archive, ar_member_func_t function, const void *arg)
+{
+#ifdef AIAMAG
+ FL_HDR fl_header;
+#ifdef AIAMAGBIG
+ int big_archive = 0;
+ FL_HDR_BIG fl_header_big;
+#endif
+#else
+ int long_name = 0;
+#endif
+ char *namemap = 0;
+ int desc = open (archive, O_RDONLY, 0);
+ if (desc < 0)
+ return -1;
+#ifdef SARMAG
+ {
+ char buf[SARMAG];
+ register int nread = read (desc, buf, SARMAG);
+ if (nread != SARMAG || memcmp (buf, ARMAG, SARMAG))
+ {
+ (void) close (desc);
+ return -2;
+ }
+ }
+#else
+#ifdef AIAMAG
+ {
+ register int nread = read (desc, &fl_header, FL_HSZ);
+
+ if (nread != FL_HSZ)
+ {
+ (void) close (desc);
+ return -2;
+ }
+#ifdef AIAMAGBIG
+ /* If this is a "big" archive, then set the flag and
+ re-read the header into the "big" structure. */
+ if (!memcmp (fl_header.fl_magic, AIAMAGBIG, SAIAMAG))
+ {
+ big_archive = 1;
+
+ /* seek back to beginning of archive */
+ if (lseek (desc, 0, 0) < 0)
+ {
+ (void) close (desc);
+ return -2;
+ }
+
+ /* re-read the header into the "big" structure */
+ nread = read (desc, &fl_header_big, FL_HSZ_BIG);
+ if (nread != FL_HSZ_BIG)
+ {
+ (void) close (desc);
+ return -2;
+ }
+ }
+ else
+#endif
+ /* Check to make sure this is a "normal" archive. */
+ if (memcmp (fl_header.fl_magic, AIAMAG, SAIAMAG))
+ {
+ (void) close (desc);
+ return -2;
+ }
+ }
+#else
+ {
+#ifndef M_XENIX
+ int buf;
+#else
+ unsigned short int buf;
+#endif
+ register int nread = read(desc, &buf, sizeof (buf));
+ if (nread != sizeof (buf) || buf != ARMAG)
+ {
+ (void) close (desc);
+ return -2;
+ }
+ }
+#endif
+#endif
+
+ /* Now find the members one by one. */
+ {
+#ifdef SARMAG
+ register long int member_offset = SARMAG;
+#else
+#ifdef AIAMAG
+ long int member_offset;
+ long int last_member_offset;
+
+#ifdef AIAMAGBIG
+ if ( big_archive )
+ {
+ sscanf (fl_header_big.fl_fstmoff, "%20ld", &member_offset);
+ sscanf (fl_header_big.fl_lstmoff, "%20ld", &last_member_offset);
+ }
+ else
+#endif
+ {
+ sscanf (fl_header.fl_fstmoff, "%12ld", &member_offset);
+ sscanf (fl_header.fl_lstmoff, "%12ld", &last_member_offset);
+ }
+
+ if (member_offset == 0)
+ {
+ /* Empty archive. */
+ close (desc);
+ return 0;
+ }
+#else
+#ifndef M_XENIX
+ register long int member_offset = sizeof (int);
+#else /* Xenix. */
+ register long int member_offset = sizeof (unsigned short int);
+#endif /* Not Xenix. */
+#endif
+#endif
+
+ while (1)
+ {
+ register int nread;
+ struct ar_hdr member_header;
+#ifdef AIAMAGBIG
+ struct ar_hdr_big member_header_big;
+#endif
+#ifdef AIAMAG
+ char name[256];
+ int name_len;
+ long int dateval;
+ int uidval, gidval;
+ long int data_offset;
+#else
+ char namebuf[sizeof member_header.ar_name + 1];
+ char *name;
+ int is_namemap; /* Nonzero if this entry maps long names. */
+#endif
+ long int eltsize;
+ int eltmode;
+ long int fnval;
+
+ if (lseek (desc, member_offset, 0) < 0)
+ {
+ (void) close (desc);
+ return -2;
+ }
+
+#ifdef AIAMAG
+#define AR_MEMHDR_SZ(x) (sizeof(x) - sizeof (x._ar_name))
+
+#ifdef AIAMAGBIG
+ if (big_archive)
+ {
+ nread = read (desc, &member_header_big,
+ AR_MEMHDR_SZ(member_header_big) );
+
+ if (nread != AR_MEMHDR_SZ(member_header_big))
+ {
+ (void) close (desc);
+ return -2;
+ }
+
+ sscanf (member_header_big.ar_namlen, "%4d", &name_len);
+ nread = read (desc, name, name_len);
+
+ if (nread != name_len)
+ {
+ (void) close (desc);
+ return -2;
+ }
+
+ name[name_len] = 0;
+
+ sscanf (member_header_big.ar_date, "%12ld", &dateval);
+ sscanf (member_header_big.ar_uid, "%12d", &uidval);
+ sscanf (member_header_big.ar_gid, "%12d", &gidval);
+ sscanf (member_header_big.ar_mode, "%12o", &eltmode);
+ sscanf (member_header_big.ar_size, "%20ld", &eltsize);
+
+ data_offset = (member_offset + AR_MEMHDR_SZ(member_header_big)
+ + name_len + 2);
+ }
+ else
+#endif
+ {
+ nread = read (desc, &member_header,
+ AR_MEMHDR_SZ(member_header) );
+
+ if (nread != AR_MEMHDR_SZ(member_header))
+ {
+ (void) close (desc);
+ return -2;
+ }
+
+ sscanf (member_header.ar_namlen, "%4d", &name_len);
+ nread = read (desc, name, name_len);
+
+ if (nread != name_len)
+ {
+ (void) close (desc);
+ return -2;
+ }
+
+ name[name_len] = 0;
+
+ sscanf (member_header.ar_date, "%12ld", &dateval);
+ sscanf (member_header.ar_uid, "%12d", &uidval);
+ sscanf (member_header.ar_gid, "%12d", &gidval);
+ sscanf (member_header.ar_mode, "%12o", &eltmode);
+ sscanf (member_header.ar_size, "%12ld", &eltsize);
+
+ data_offset = (member_offset + AR_MEMHDR_SZ(member_header)
+ + name_len + 2);
+ }
+ data_offset += data_offset % 2;
+
+ fnval =
+ (*function) (desc, name, 0,
+ member_offset, data_offset, eltsize,
+ dateval, uidval, gidval,
+ eltmode, arg);
+
+#else /* Not AIAMAG. */
+ nread = read (desc, &member_header, AR_HDR_SIZE);
+ if (nread == 0)
+ /* No data left means end of file; that is OK. */
+ break;
+
+ if (nread != AR_HDR_SIZE
+#if defined(ARFMAG) || defined(ARFZMAG)
+ || (
+# ifdef ARFMAG
+ memcmp (member_header.ar_fmag, ARFMAG, 2)
+# else
+ 1
+# endif
+ &&
+# ifdef ARFZMAG
+ memcmp (member_header.ar_fmag, ARFZMAG, 2)
+# else
+ 1
+# endif
+ )
+#endif
+ )
+ {
+ (void) close (desc);
+ return -2;
+ }
+
+ name = namebuf;
+ memcpy (name, member_header.ar_name, sizeof member_header.ar_name);
+ {
+ register char *p = name + sizeof member_header.ar_name;
+ do
+ *p = '\0';
+ while (p > name && *--p == ' ');
+
+#ifndef AIAMAG
+ /* If the member name is "//" or "ARFILENAMES/" this may be
+ a list of file name mappings. The maximum file name
+ length supported by the standard archive format is 14
+ characters. This member will actually always be the
+ first or second entry in the archive, but we don't check
+ that. */
+ is_namemap = (!strcmp (name, "//")
+ || !strcmp (name, "ARFILENAMES/"));
+#endif /* Not AIAMAG. */
+ /* On some systems, there is a slash after each member name. */
+ if (*p == '/')
+ *p = '\0';
+
+#ifndef AIAMAG
+ /* If the member name starts with a space or a slash, this
+ is an index into the file name mappings (used by GNU ar).
+ Otherwise if the member name looks like #1/NUMBER the
+ real member name appears in the element data (used by
+ 4.4BSD). */
+ if (! is_namemap
+ && (name[0] == ' ' || name[0] == '/')
+ && namemap != 0)
+ {
+ name = namemap + atoi (name + 1);
+ long_name = 1;
+ }
+ else if (name[0] == '#'
+ && name[1] == '1'
+ && name[2] == '/')
+ {
+ int namesize = atoi (name + 3);
+
+ name = alloca (namesize + 1);
+ nread = read (desc, name, namesize);
+ if (nread != namesize)
+ {
+ close (desc);
+ return -2;
+ }
+ name[namesize] = '\0';
+
+ long_name = 1;
+ }
+#endif /* Not AIAMAG. */
+ }
+
+#ifndef M_XENIX
+ sscanf (TOCHAR (member_header.ar_mode), "%o", &eltmode);
+ eltsize = atol (TOCHAR (member_header.ar_size));
+#else /* Xenix. */
+ eltmode = (unsigned short int) member_header.ar_mode;
+ eltsize = member_header.ar_size;
+#endif /* Not Xenix. */
+
+ fnval =
+ (*function) (desc, name, ! long_name, member_offset,
+ member_offset + AR_HDR_SIZE, eltsize,
+#ifndef M_XENIX
+ atol (TOCHAR (member_header.ar_date)),
+ atoi (TOCHAR (member_header.ar_uid)),
+ atoi (TOCHAR (member_header.ar_gid)),
+#else /* Xenix. */
+ member_header.ar_date,
+ member_header.ar_uid,
+ member_header.ar_gid,
+#endif /* Not Xenix. */
+ eltmode, arg);
+
+#endif /* AIAMAG. */
+
+ if (fnval)
+ {
+ (void) close (desc);
+ return fnval;
+ }
+
+#ifdef AIAMAG
+ if (member_offset == last_member_offset)
+ /* End of the chain. */
+ break;
+
+#ifdef AIAMAGBIG
+ if (big_archive)
+ sscanf (member_header_big.ar_nxtmem, "%20ld", &member_offset);
+ else
+#endif
+ sscanf (member_header.ar_nxtmem, "%12ld", &member_offset);
+
+ if (lseek (desc, member_offset, 0) != member_offset)
+ {
+ (void) close (desc);
+ return -2;
+ }
+#else
+
+ /* If this member maps archive names, we must read it in. The
+ name map will always precede any members whose names must
+ be mapped. */
+ if (is_namemap)
+ {
+ char *clear;
+ char *limit;
+
+ namemap = alloca (eltsize);
+ nread = read (desc, namemap, eltsize);
+ if (nread != eltsize)
+ {
+ (void) close (desc);
+ return -2;
+ }
+
+ /* The names are separated by newlines. Some formats have
+ a trailing slash. Null terminate the strings for
+ convenience. */
+ limit = namemap + eltsize;
+ for (clear = namemap; clear < limit; clear++)
+ {
+ if (*clear == '\n')
+ {
+ *clear = '\0';
+ if (clear[-1] == '/')
+ clear[-1] = '\0';
+ }
+ }
+
+ is_namemap = 0;
+ }
+
+ member_offset += AR_HDR_SIZE + eltsize;
+ if (member_offset % 2 != 0)
+ member_offset++;
+#endif
+ }
+ }
+
+ close (desc);
+ return 0;
+}
+#endif /* !VMS */
+
+/* Return nonzero iff NAME matches MEM.
+ If TRUNCATED is nonzero, MEM may be truncated to
+ sizeof (struct ar_hdr.ar_name) - 1. */
+
+int
+ar_name_equal (const char *name, const char *mem, int truncated)
+{
+ const char *p;
+
+ p = strrchr (name, '/');
+ if (p != 0)
+ name = p + 1;
+
+#ifndef VMS
+ if (truncated)
+ {
+#ifdef AIAMAG
+ /* TRUNCATED should never be set on this system. */
+ abort ();
+#else
+ struct ar_hdr hdr;
+#if !defined (__hpux) && !defined (cray)
+ return strneq (name, mem, sizeof(hdr.ar_name) - 1);
+#else
+ return strneq (name, mem, sizeof(hdr.ar_name) - 2);
+#endif /* !__hpux && !cray */
+#endif /* !AIAMAG */
+ }
+#endif /* !VMS */
+
+ return !strcmp (name, mem);
+}
+
+#ifndef VMS
+/* ARGSUSED */
+static long int
+ar_member_pos (int desc UNUSED, const char *mem, int truncated,
+ long int hdrpos, long int datapos UNUSED, long int size UNUSED,
+ long int date UNUSED, int uid UNUSED, int gid UNUSED,
+ int mode UNUSED, const void *name)
+{
+ if (!ar_name_equal (name, mem, truncated))
+ return 0;
+ return hdrpos;
+}
+
+/* Set date of member MEMNAME in archive ARNAME to current time.
+ Returns 0 if successful,
+ -1 if file ARNAME does not exist,
+ -2 if not a valid archive,
+ -3 if other random system call error (including file read-only),
+ 1 if valid but member MEMNAME does not exist. */
+
+int
+ar_member_touch (const char *arname, const char *memname)
+{
+ long int pos = ar_scan (arname, ar_member_pos, memname);
+ int fd;
+ struct ar_hdr ar_hdr;
+ int i;
+ unsigned int ui;
+ struct stat statbuf;
+
+ if (pos < 0)
+ return (int) pos;
+ if (!pos)
+ return 1;
+
+ fd = open (arname, O_RDWR, 0666);
+ if (fd < 0)
+ return -3;
+ /* Read in this member's header */
+ if (lseek (fd, pos, 0) < 0)
+ goto lose;
+ if (AR_HDR_SIZE != read (fd, &ar_hdr, AR_HDR_SIZE))
+ goto lose;
+ /* Write back the header, thus touching the archive file. */
+ if (lseek (fd, pos, 0) < 0)
+ goto lose;
+ if (AR_HDR_SIZE != write (fd, &ar_hdr, AR_HDR_SIZE))
+ goto lose;
+ /* The file's mtime is the time we we want. */
+ EINTRLOOP (i, fstat (fd, &statbuf));
+ if (i < 0)
+ goto lose;
+#if defined(ARFMAG) || defined(ARFZMAG) || defined(AIAMAG) || defined(WINDOWS32)
+ /* Advance member's time to that time */
+ for (ui = 0; ui < sizeof ar_hdr.ar_date; ui++)
+ ar_hdr.ar_date[ui] = ' ';
+ sprintf (TOCHAR (ar_hdr.ar_date), "%ld", (long int) statbuf.st_mtime);
+#ifdef AIAMAG
+ ar_hdr.ar_date[strlen(ar_hdr.ar_date)] = ' ';
+#endif
+#else
+ ar_hdr.ar_date = statbuf.st_mtime;
+#endif
+ /* Write back this member's header */
+ if (lseek (fd, pos, 0) < 0)
+ goto lose;
+ if (AR_HDR_SIZE != write (fd, &ar_hdr, AR_HDR_SIZE))
+ goto lose;
+ close (fd);
+ return 0;
+
+ lose:
+ i = errno;
+ close (fd);
+ errno = i;
+ return -3;
+}
+#endif
+
+#ifdef TEST
+
+long int
+describe_member (int desc, const char *name, int truncated,
+ long int hdrpos, long int datapos, long int size,
+ long int date, int uid, int gid, int mode, const void *arg)
+{
+ extern char *ctime ();
+
+ printf (_("Member `%s'%s: %ld bytes at %ld (%ld).\n"),
+ name, truncated ? _(" (name might be truncated)") : "",
+ size, hdrpos, datapos);
+ printf (_(" Date %s"), ctime (&date));
+ printf (_(" uid = %d, gid = %d, mode = 0%o.\n"), uid, gid, mode);
+
+ return 0;
+}
+
+int
+main (int argc, char **argv)
+{
+ ar_scan (argv[1], describe_member, NULL);
+ return 0;
+}
+
+#endif /* TEST. */
+#endif /* NO_ARCHIVES. */