1/* Library function for scanning an archive file.
2Copyright (C) 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
31998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation,
4Inc.
5This file is part of GNU Make.
6
7GNU Make is free software; you can redistribute it and/or modify it under the
8terms of the GNU General Public License as published by the Free Software
9Foundation; either version 2, or (at your option) any later version.
10
11GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
12WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License along with
16GNU Make; see the file COPYING.  If not, write to the Free Software
17Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.  */
18
19#include "make.h"
20
21#ifdef HAVE_FCNTL_H
22#include <fcntl.h>
23#else
24#include <sys/file.h>
25#endif
26
27#ifndef	NO_ARCHIVES
28
29#ifdef VMS
30#include <lbrdef.h>
31#include <mhddef.h>
32#include <credef.h>
33#include <descrip.h>
34#include <ctype.h>
35#if __DECC
36#include <unixlib.h>
37#include <lbr$routines.h>
38#endif
39
40static void *VMS_lib_idx;
41
42static char *VMS_saved_memname;
43
44static time_t VMS_member_date;
45
46static long int (*VMS_function) ();
47
48static int
49VMS_get_member_info (struct dsc$descriptor_s *module, unsigned long *rfa)
50{
51  int status, i;
52  long int fnval;
53
54  time_t val;
55
56  static struct dsc$descriptor_s bufdesc =
57    { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL };
58
59  struct mhddef *mhd;
60  char filename[128];
61
62  bufdesc.dsc$a_pointer = filename;
63  bufdesc.dsc$w_length = sizeof (filename);
64
65  status = lbr$set_module (&VMS_lib_idx, rfa, &bufdesc,
66			   &bufdesc.dsc$w_length, 0);
67  if (! (status & 1))
68    {
69      error (NILF, _("lbr$set_module failed to extract module info, status = %d"),
70	     status);
71
72      lbr$close (&VMS_lib_idx);
73
74      return 0;
75    }
76
77  mhd = (struct mhddef *) filename;
78
79#ifdef __DECC
80  /* John Fowler <jfowler@nyx.net> writes this is needed in his environment,
81   * but that decc$fix_time() isn't documented to work this way.  Let me
82   * know if this causes problems in other VMS environments.
83   */
84  val = decc$fix_time (&mhd->mhd$l_datim) + timezone - daylight*3600;
85#endif
86
87  for (i = 0; i < module->dsc$w_length; i++)
88    filename[i] = _tolower ((unsigned char)module->dsc$a_pointer[i]);
89
90  filename[i] = '\0';
91
92  VMS_member_date = (time_t) -1;
93
94  fnval =
95    (*VMS_function) (-1, filename, 0, 0, 0, 0, val, 0, 0, 0,
96		     VMS_saved_memname);
97
98  if (fnval)
99    {
100      VMS_member_date = fnval;
101      return 0;
102    }
103  else
104    return 1;
105}
106
107/* Takes three arguments ARCHIVE, FUNCTION and ARG.
108
109   Open the archive named ARCHIVE, find its members one by one,
110   and for each one call FUNCTION with the following arguments:
111     archive file descriptor for reading the data,
112     member name,
113     member name might be truncated flag,
114     member header position in file,
115     member data position in file,
116     member data size,
117     member date,
118     member uid,
119     member gid,
120     member protection mode,
121     ARG.
122
123   NOTE: on VMS systems, only name, date, and arg are meaningful!
124
125   The descriptor is poised to read the data of the member
126   when FUNCTION is called.  It does not matter how much
127   data FUNCTION reads.
128
129   If FUNCTION returns nonzero, we immediately return
130   what FUNCTION returned.
131
132   Returns -1 if archive does not exist,
133   Returns -2 if archive has invalid format.
134   Returns 0 if have scanned successfully.  */
135
136long int
137ar_scan (char *archive, long int (*function) PARAMS ((void)), long int arg)
138{
139  char *p;
140
141  static struct dsc$descriptor_s libdesc =
142    { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL };
143
144  unsigned long func = LBR$C_READ;
145  unsigned long type = LBR$C_TYP_UNK;
146  unsigned long index = 1;
147
148  int status;
149
150  status = lbr$ini_control (&VMS_lib_idx, &func, &type, 0);
151
152  if (! (status & 1))
153    {
154      error (NILF, _("lbr$ini_control failed with status = %d"),status);
155      return -2;
156    }
157
158  libdesc.dsc$a_pointer = archive;
159  libdesc.dsc$w_length = strlen (archive);
160
161  status = lbr$open (&VMS_lib_idx, &libdesc, 0, 0, 0, 0, 0);
162
163  if (! (status & 1))
164    {
165      error (NILF, _("unable to open library `%s' to lookup member `%s'"),
166	     archive, (char *)arg);
167      return -1;
168    }
169
170  VMS_saved_memname = (char *)arg;
171
172  /* For comparison, delete .obj from arg name.  */
173
174  p = strrchr (VMS_saved_memname, '.');
175  if (p)
176    *p = '\0';
177
178  VMS_function = function;
179
180  VMS_member_date = (time_t) -1;
181  lbr$get_index (&VMS_lib_idx, &index, VMS_get_member_info, 0);
182
183  /* Undo the damage.  */
184  if (p)
185    *p = '.';
186
187  lbr$close (&VMS_lib_idx);
188
189  return VMS_member_date > 0 ? VMS_member_date : 0;
190}
191
192#else /* !VMS */
193
194/* SCO Unix's compiler defines both of these.  */
195#ifdef	M_UNIX
196#undef	M_XENIX
197#endif
198
199/* On the sun386i and in System V rel 3, ar.h defines two different archive
200   formats depending upon whether you have defined PORTAR (normal) or PORT5AR
201   (System V Release 1).  There is no default, one or the other must be defined
202   to have a nonzero value.  */
203
204#if (!defined (PORTAR) || PORTAR == 0) && (!defined (PORT5AR) || PORT5AR == 0)
205#undef	PORTAR
206#ifdef M_XENIX
207/* According to Jim Sievert <jas1@rsvl.unisys.com>, for SCO XENIX defining
208   PORTAR to 1 gets the wrong archive format, and defining it to 0 gets the
209   right one.  */
210#define PORTAR 0
211#else
212#define PORTAR 1
213#endif
214#endif
215
216/* On AIX, define these symbols to be sure to get both archive formats.
217   AIX 4.3 introduced the "big" archive format to support 64-bit object
218   files, so on AIX 4.3 systems we need to support both the "normal" and
219   "big" archive formats.  An archive's format is indicated in the
220   "fl_magic" field of the "FL_HDR" structure.  For a normal archive,
221   this field will be the string defined by the AIAMAG symbol.  For a
222   "big" archive, it will be the string defined by the AIAMAGBIG symbol
223   (at least on AIX it works this way).
224
225   Note: we'll define these symbols regardless of which AIX version
226   we're compiling on, but this is okay since we'll use the new symbols
227   only if they're present.  */
228#ifdef _AIX
229# define __AR_SMALL__
230# define __AR_BIG__
231#endif
232
233#ifndef WINDOWS32
234# ifndef __BEOS__
235#  include <ar.h>
236# else
237   /* BeOS 5 doesn't have <ar.h> but has archives in the same format
238    * as many other Unices.  This was taken from GNU binutils for BeOS.
239    */
240#  define ARMAG	"!<arch>\n"	/* String that begins an archive file.  */
241#  define SARMAG 8		/* Size of that string.  */
242#  define ARFMAG "`\n"		/* String in ar_fmag at end of each header.  */
243struct ar_hdr
244  {
245    char ar_name[16];		/* Member file name, sometimes / terminated. */
246    char ar_date[12];		/* File date, decimal seconds since Epoch.  */
247    char ar_uid[6], ar_gid[6];	/* User and group IDs, in ASCII decimal.  */
248    char ar_mode[8];		/* File mode, in ASCII octal.  */
249    char ar_size[10];		/* File size, in ASCII decimal.  */
250    char ar_fmag[2];		/* Always contains ARFMAG.  */
251  };
252# endif
253#else
254/* These should allow us to read Windows (VC++) libraries (according to Frank
255 * Libbrecht <frankl@abzx.belgium.hp.com>)
256 */
257# include <windows.h>
258# include <windef.h>
259# include <io.h>
260# define ARMAG      IMAGE_ARCHIVE_START
261# define SARMAG     IMAGE_ARCHIVE_START_SIZE
262# define ar_hdr     _IMAGE_ARCHIVE_MEMBER_HEADER
263# define ar_name    Name
264# define ar_mode    Mode
265# define ar_size    Size
266# define ar_date    Date
267# define ar_uid     UserID
268# define ar_gid     GroupID
269#endif
270
271/* Cray's <ar.h> apparently defines this.  */
272#ifndef	AR_HDR_SIZE
273# define   AR_HDR_SIZE	(sizeof (struct ar_hdr))
274#endif
275
276/* Takes three arguments ARCHIVE, FUNCTION and ARG.
277
278   Open the archive named ARCHIVE, find its members one by one,
279   and for each one call FUNCTION with the following arguments:
280     archive file descriptor for reading the data,
281     member name,
282     member name might be truncated flag,
283     member header position in file,
284     member data position in file,
285     member data size,
286     member date,
287     member uid,
288     member gid,
289     member protection mode,
290     ARG.
291
292   The descriptor is poised to read the data of the member
293   when FUNCTION is called.  It does not matter how much
294   data FUNCTION reads.
295
296   If FUNCTION returns nonzero, we immediately return
297   what FUNCTION returned.
298
299   Returns -1 if archive does not exist,
300   Returns -2 if archive has invalid format.
301   Returns 0 if have scanned successfully.  */
302
303long int
304ar_scan (char *archive, long int (*function)(), long int arg)
305{
306#ifdef AIAMAG
307  FL_HDR fl_header;
308#ifdef AIAMAGBIG
309  int big_archive = 0;
310  FL_HDR_BIG fl_header_big;
311#endif
312#else
313  int long_name = 0;
314#endif
315  char *namemap = 0;
316  register int desc = open (archive, O_RDONLY, 0);
317  if (desc < 0)
318    return -1;
319#ifdef SARMAG
320  {
321    char buf[SARMAG];
322    register int nread = read (desc, buf, SARMAG);
323    if (nread != SARMAG || bcmp (buf, ARMAG, SARMAG))
324      {
325	(void) close (desc);
326	return -2;
327      }
328  }
329#else
330#ifdef AIAMAG
331  {
332    register int nread = read (desc, (char *) &fl_header, FL_HSZ);
333
334    if (nread != FL_HSZ)
335      {
336	(void) close (desc);
337	return -2;
338      }
339#ifdef AIAMAGBIG
340    /* If this is a "big" archive, then set the flag and
341       re-read the header into the "big" structure. */
342    if (!bcmp (fl_header.fl_magic, AIAMAGBIG, SAIAMAG))
343      {
344	big_archive = 1;
345
346	/* seek back to beginning of archive */
347	if (lseek (desc, 0, 0) < 0)
348	  {
349	    (void) close (desc);
350	    return -2;
351	  }
352
353	/* re-read the header into the "big" structure */
354	nread = read (desc, (char *) &fl_header_big, FL_HSZ_BIG);
355	if (nread != FL_HSZ_BIG)
356	  {
357	    (void) close (desc);
358	    return -2;
359	  }
360      }
361    else
362#endif
363       /* Check to make sure this is a "normal" archive. */
364      if (bcmp (fl_header.fl_magic, AIAMAG, SAIAMAG))
365	{
366          (void) close (desc);
367          return -2;
368	}
369  }
370#else
371  {
372#ifndef M_XENIX
373    int buf;
374#else
375    unsigned short int buf;
376#endif
377    register int nread = read(desc, &buf, sizeof (buf));
378    if (nread != sizeof (buf) || buf != ARMAG)
379      {
380	(void) close (desc);
381	return -2;
382      }
383  }
384#endif
385#endif
386
387  /* Now find the members one by one.  */
388  {
389#ifdef SARMAG
390    register long int member_offset = SARMAG;
391#else
392#ifdef AIAMAG
393    long int member_offset;
394    long int last_member_offset;
395
396#ifdef AIAMAGBIG
397    if ( big_archive )
398      {
399	sscanf (fl_header_big.fl_fstmoff, "%20ld", &member_offset);
400	sscanf (fl_header_big.fl_lstmoff, "%20ld", &last_member_offset);
401      }
402    else
403#endif
404      {
405	sscanf (fl_header.fl_fstmoff, "%12ld", &member_offset);
406	sscanf (fl_header.fl_lstmoff, "%12ld", &last_member_offset);
407      }
408
409    if (member_offset == 0)
410      {
411	/* Empty archive.  */
412	close (desc);
413	return 0;
414      }
415#else
416#ifndef	M_XENIX
417    register long int member_offset = sizeof (int);
418#else	/* Xenix.  */
419    register long int member_offset = sizeof (unsigned short int);
420#endif	/* Not Xenix.  */
421#endif
422#endif
423
424    while (1)
425      {
426	register int nread;
427	struct ar_hdr member_header;
428#ifdef AIAMAGBIG
429	struct ar_hdr_big member_header_big;
430#endif
431#ifdef AIAMAG
432	char name[256];
433	int name_len;
434	long int dateval;
435	int uidval, gidval;
436	long int data_offset;
437#else
438	char namebuf[sizeof member_header.ar_name + 1];
439	char *name;
440	int is_namemap;		/* Nonzero if this entry maps long names.  */
441#endif
442	long int eltsize;
443	int eltmode;
444	long int fnval;
445
446	if (lseek (desc, member_offset, 0) < 0)
447	  {
448	    (void) close (desc);
449	    return -2;
450	  }
451
452#ifdef AIAMAG
453#define       AR_MEMHDR_SZ(x) (sizeof(x) - sizeof (x._ar_name))
454
455#ifdef AIAMAGBIG
456	if (big_archive)
457	  {
458	    nread = read (desc, (char *) &member_header_big,
459			  AR_MEMHDR_SZ(member_header_big) );
460
461	    if (nread != AR_MEMHDR_SZ(member_header_big))
462	      {
463		(void) close (desc);
464		return -2;
465	      }
466
467	    sscanf (member_header_big.ar_namlen, "%4d", &name_len);
468	    nread = read (desc, name, name_len);
469
470	    if (nread != name_len)
471	      {
472		(void) close (desc);
473		return -2;
474	      }
475
476	    name[name_len] = 0;
477
478	    sscanf (member_header_big.ar_date, "%12ld", &dateval);
479	    sscanf (member_header_big.ar_uid, "%12d", &uidval);
480	    sscanf (member_header_big.ar_gid, "%12d", &gidval);
481	    sscanf (member_header_big.ar_mode, "%12o", &eltmode);
482	    sscanf (member_header_big.ar_size, "%20ld", &eltsize);
483
484	    data_offset = (member_offset + AR_MEMHDR_SZ(member_header_big)
485			   + name_len + 2);
486	  }
487	else
488#endif
489	  {
490	    nread = read (desc, (char *) &member_header,
491			  AR_MEMHDR_SZ(member_header) );
492
493	    if (nread != AR_MEMHDR_SZ(member_header))
494	      {
495		(void) close (desc);
496		return -2;
497	      }
498
499	    sscanf (member_header.ar_namlen, "%4d", &name_len);
500	    nread = read (desc, name, name_len);
501
502	    if (nread != name_len)
503	      {
504		(void) close (desc);
505		return -2;
506	      }
507
508	    name[name_len] = 0;
509
510	    sscanf (member_header.ar_date, "%12ld", &dateval);
511	    sscanf (member_header.ar_uid, "%12d", &uidval);
512	    sscanf (member_header.ar_gid, "%12d", &gidval);
513	    sscanf (member_header.ar_mode, "%12o", &eltmode);
514	    sscanf (member_header.ar_size, "%12ld", &eltsize);
515
516	    data_offset = (member_offset + AR_MEMHDR_SZ(member_header)
517			   + name_len + 2);
518	  }
519	data_offset += data_offset % 2;
520
521	fnval =
522	  (*function) (desc, name, 0,
523		       member_offset, data_offset, eltsize,
524		       dateval, uidval, gidval,
525		       eltmode, arg);
526
527#else	/* Not AIAMAG.  */
528	nread = read (desc, (char *) &member_header, AR_HDR_SIZE);
529	if (nread == 0)
530	  /* No data left means end of file; that is OK.  */
531	  break;
532
533	if (nread != AR_HDR_SIZE
534#if defined(ARFMAG) || defined(ARFZMAG)
535	    || (
536# ifdef ARFMAG
537                bcmp (member_header.ar_fmag, ARFMAG, 2)
538# else
539                1
540# endif
541                &&
542# ifdef ARFZMAG
543                bcmp (member_header.ar_fmag, ARFZMAG, 2)
544# else
545                1
546# endif
547               )
548#endif
549	    )
550	  {
551	    (void) close (desc);
552	    return -2;
553	  }
554
555	name = namebuf;
556	bcopy (member_header.ar_name, name, sizeof member_header.ar_name);
557	{
558	  register char *p = name + sizeof member_header.ar_name;
559	  do
560	    *p = '\0';
561	  while (p > name && *--p == ' ');
562
563#ifndef AIAMAG
564	  /* If the member name is "//" or "ARFILENAMES/" this may be
565	     a list of file name mappings.  The maximum file name
566 	     length supported by the standard archive format is 14
567 	     characters.  This member will actually always be the
568 	     first or second entry in the archive, but we don't check
569 	     that.  */
570 	  is_namemap = (!strcmp (name, "//")
571			|| !strcmp (name, "ARFILENAMES/"));
572#endif	/* Not AIAMAG. */
573	  /* On some systems, there is a slash after each member name.  */
574	  if (*p == '/')
575	    *p = '\0';
576
577#ifndef AIAMAG
578 	  /* If the member name starts with a space or a slash, this
579 	     is an index into the file name mappings (used by GNU ar).
580 	     Otherwise if the member name looks like #1/NUMBER the
581 	     real member name appears in the element data (used by
582 	     4.4BSD).  */
583 	  if (! is_namemap
584 	      && (name[0] == ' ' || name[0] == '/')
585 	      && namemap != 0)
586	    {
587	      name = namemap + atoi (name + 1);
588	      long_name = 1;
589	    }
590 	  else if (name[0] == '#'
591 		   && name[1] == '1'
592 		   && name[2] == '/')
593 	    {
594 	      int namesize = atoi (name + 3);
595
596 	      name = (char *) alloca (namesize + 1);
597 	      nread = read (desc, name, namesize);
598 	      if (nread != namesize)
599 		{
600 		  close (desc);
601 		  return -2;
602 		}
603 	      name[namesize] = '\0';
604
605	      long_name = 1;
606 	    }
607#endif /* Not AIAMAG. */
608	}
609
610#ifndef	M_XENIX
611	sscanf (member_header.ar_mode, "%o", &eltmode);
612	eltsize = atol (member_header.ar_size);
613#else	/* Xenix.  */
614	eltmode = (unsigned short int) member_header.ar_mode;
615	eltsize = member_header.ar_size;
616#endif	/* Not Xenix.  */
617
618	fnval =
619	  (*function) (desc, name, ! long_name, member_offset,
620		       member_offset + AR_HDR_SIZE, eltsize,
621#ifndef	M_XENIX
622		       atol (member_header.ar_date),
623		       atoi (member_header.ar_uid),
624		       atoi (member_header.ar_gid),
625#else	/* Xenix.  */
626		       member_header.ar_date,
627		       member_header.ar_uid,
628		       member_header.ar_gid,
629#endif	/* Not Xenix.  */
630		       eltmode, arg);
631
632#endif  /* AIAMAG.  */
633
634	if (fnval)
635	  {
636	    (void) close (desc);
637	    return fnval;
638	  }
639
640#ifdef AIAMAG
641	if (member_offset == last_member_offset)
642	  /* End of the chain.  */
643	  break;
644
645#ifdef AIAMAGBIG
646	if (big_archive)
647          sscanf (member_header_big.ar_nxtmem, "%20ld", &member_offset);
648	else
649#endif
650	  sscanf (member_header.ar_nxtmem, "%12ld", &member_offset);
651
652	if (lseek (desc, member_offset, 0) != member_offset)
653	  {
654	    (void) close (desc);
655	    return -2;
656	  }
657#else
658
659 	/* If this member maps archive names, we must read it in.  The
660 	   name map will always precede any members whose names must
661 	   be mapped.  */
662	if (is_namemap)
663 	  {
664 	    char *clear;
665 	    char *limit;
666
667 	    namemap = (char *) alloca (eltsize);
668 	    nread = read (desc, namemap, eltsize);
669 	    if (nread != eltsize)
670 	      {
671 		(void) close (desc);
672 		return -2;
673 	      }
674
675 	    /* The names are separated by newlines.  Some formats have
676 	       a trailing slash.  Null terminate the strings for
677 	       convenience.  */
678 	    limit = namemap + eltsize;
679 	    for (clear = namemap; clear < limit; clear++)
680 	      {
681 		if (*clear == '\n')
682 		  {
683 		    *clear = '\0';
684 		    if (clear[-1] == '/')
685 		      clear[-1] = '\0';
686 		  }
687 	      }
688
689	    is_namemap = 0;
690 	  }
691
692	member_offset += AR_HDR_SIZE + eltsize;
693	if (member_offset % 2 != 0)
694	  member_offset++;
695#endif
696      }
697  }
698
699  close (desc);
700  return 0;
701}
702#endif /* !VMS */
703
704/* Return nonzero iff NAME matches MEM.
705   If TRUNCATED is nonzero, MEM may be truncated to
706   sizeof (struct ar_hdr.ar_name) - 1.  */
707
708int
709ar_name_equal (char *name, char *mem, int truncated)
710{
711  char *p;
712
713  p = strrchr (name, '/');
714  if (p != 0)
715    name = p + 1;
716
717#ifndef VMS
718  if (truncated)
719    {
720#ifdef AIAMAG
721      /* TRUNCATED should never be set on this system.  */
722      abort ();
723#else
724      struct ar_hdr hdr;
725#if !defined (__hpux) && !defined (cray)
726      return strneq (name, mem, sizeof(hdr.ar_name) - 1);
727#else
728      return strneq (name, mem, sizeof(hdr.ar_name) - 2);
729#endif /* !__hpux && !cray */
730#endif /* !AIAMAG */
731    }
732#endif /* !VMS */
733
734  return !strcmp (name, mem);
735}
736
737#ifndef VMS
738/* ARGSUSED */
739static long int
740ar_member_pos (int desc UNUSED, char *mem, int truncated,
741	       long int hdrpos, long int datapos UNUSED, long int size UNUSED,
742               long int date UNUSED, int uid UNUSED, int gid UNUSED,
743               int mode UNUSED, char *name)
744{
745  if (!ar_name_equal (name, mem, truncated))
746    return 0;
747  return hdrpos;
748}
749
750/* Set date of member MEMNAME in archive ARNAME to current time.
751   Returns 0 if successful,
752   -1 if file ARNAME does not exist,
753   -2 if not a valid archive,
754   -3 if other random system call error (including file read-only),
755   1 if valid but member MEMNAME does not exist.  */
756
757int
758ar_member_touch (char *arname, char *memname)
759{
760  long int pos = ar_scan (arname, ar_member_pos, (long int) memname);
761  int fd;
762  struct ar_hdr ar_hdr;
763  int i;
764  unsigned int ui;
765  struct stat statbuf;
766
767  if (pos < 0)
768    return (int) pos;
769  if (!pos)
770    return 1;
771
772  fd = open (arname, O_RDWR, 0666);
773  if (fd < 0)
774    return -3;
775  /* Read in this member's header */
776  if (lseek (fd, pos, 0) < 0)
777    goto lose;
778  if (AR_HDR_SIZE != read (fd, (char *) &ar_hdr, AR_HDR_SIZE))
779    goto lose;
780  /* Write back the header, thus touching the archive file.  */
781  if (lseek (fd, pos, 0) < 0)
782    goto lose;
783  if (AR_HDR_SIZE != write (fd, (char *) &ar_hdr, AR_HDR_SIZE))
784    goto lose;
785  /* The file's mtime is the time we we want.  */
786  EINTRLOOP (i, fstat (fd, &statbuf));
787  if (i < 0)
788    goto lose;
789#if defined(ARFMAG) || defined(ARFZMAG) || defined(AIAMAG) || defined(WINDOWS32)
790  /* Advance member's time to that time */
791  for (ui = 0; ui < sizeof ar_hdr.ar_date; ui++)
792    ar_hdr.ar_date[ui] = ' ';
793  sprintf (ar_hdr.ar_date, "%ld", (long int) statbuf.st_mtime);
794#ifdef AIAMAG
795  ar_hdr.ar_date[strlen(ar_hdr.ar_date)] = ' ';
796#endif
797#else
798  ar_hdr.ar_date = statbuf.st_mtime;
799#endif
800  /* Write back this member's header */
801  if (lseek (fd, pos, 0) < 0)
802    goto lose;
803  if (AR_HDR_SIZE != write (fd, (char *) &ar_hdr, AR_HDR_SIZE))
804    goto lose;
805  close (fd);
806  return 0;
807
808 lose:
809  i = errno;
810  close (fd);
811  errno = i;
812  return -3;
813}
814#endif
815
816#ifdef TEST
817
818long int
819describe_member (int desc, char *name, int truncated,
820		 long int hdrpos, long int datapos, long int size,
821                 long int date, int uid, int gid, int mode)
822{
823  extern char *ctime ();
824
825  printf (_("Member `%s'%s: %ld bytes at %ld (%ld).\n"),
826	  name, truncated ? _(" (name might be truncated)") : "",
827	  size, hdrpos, datapos);
828  printf (_("  Date %s"), ctime (&date));
829  printf (_("  uid = %d, gid = %d, mode = 0%o.\n"), uid, gid, mode);
830
831  return 0;
832}
833
834int
835main (int argc, char **argv)
836{
837  ar_scan (argv[1], describe_member);
838  return 0;
839}
840
841#endif	/* TEST.  */
842#endif	/* NO_ARCHIVES.  */
843