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