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