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