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