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