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