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