1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 /*
  27  *      ar.c
  28  *
  29  *      Deal with the lib.a(member.o) and lib.a((entry-point)) notations
  30  *
  31  * Look inside archives for notations a(b) and a((b))
  32  *      a(b)    is file member   b  in archive a
  33  *      a((b))  is entry point   b  in object archive a
  34  *
  35  * For 6.0, create a make which can understand all archive
  36  * formats.  This is kind of tricky, and <ar.h> isnt any help.
  37  */
  38 
  39 /*
  40  * Included files
  41  */
  42 #include <alloca.h>               /* alloca() */
  43 #include <ar.h>
  44 #include <errno.h>                /* errno */
  45 #include <fcntl.h>                /* open() */
  46 #include <libintl.h>
  47 #include <mk/defs.h>
  48 #include <mksh/misc.h>            /* retmem_mb() */
  49 
  50 struct ranlib {
  51         union {
  52                 off_t   ran_strx;       /* string table index of */
  53                 char    *ran_name;      /* symbol defined by */
  54         }       ran_un;
  55         off_t   ran_off;                /* library member at this offset */
  56 };
  57 
  58 #include <unistd.h>               /* close() */
  59 
  60 
  61 /*
  62  * Defined macros
  63  */
  64 #ifndef S5EMUL
  65 #undef BITSPERBYTE
  66 #define BITSPERBYTE     8
  67 #endif
  68 
  69 /*
  70  * Defines for all the different archive formats.  See next comment
  71  * block for justification for not using <ar.h>s versions.
  72  */
  73 #define AR_5_MAGIC              "<ar>"            /* 5.0 format magic string */
  74 #define AR_5_MAGIC_LENGTH       4               /* 5.0 format string length */
  75 
  76 #define AR_PORT_MAGIC           "!<arch>\n"       /* Port. (6.0) magic string */
  77 #define AR_PORT_MAGIC_LENGTH    8               /* Port. (6.0) string length */
  78 #define AR_PORT_END_MAGIC       "`\n"           /* Port. (6.0) end of header */
  79 #define AR_PORT_WORD            4               /* Port. (6.0) 'word' length */
  80 
  81 /*
  82  * typedefs & structs
  83  */
  84 /*
  85  * These are the archive file headers for the formats.  Note
  86  * that it really doesnt matter if these structures are defined
  87  * here.  They are correct as of the respective archive format
  88  * releases.  If the archive format is changed, then since backwards
  89  * compatability is the desired behavior, a new structure is added
  90  * to the list.
  91  */
  92 typedef struct {        /* 5.0 ar header format: vax family; 3b family */
  93         char                    ar_magic[AR_5_MAGIC_LENGTH];    /* AR_5_MAGIC*/
  94         char                    ar_name[16];    /* Space terminated */
  95         char                    ar_date[AR_PORT_WORD];  /* sgetl() accessed */
  96         char                    ar_syms[AR_PORT_WORD];  /* sgetl() accessed */
  97 }                       Arh_5;
  98 
  99 typedef struct {        /* 5.0 ar symbol format: vax family; 3b family */
 100         char                    sym_name[8];    /* Space terminated */
 101         char                    sym_ptr[AR_PORT_WORD];  /* sgetl() accessed */
 102 }                       Ars_5;
 103 
 104 typedef struct {        /* 5.0 ar member format: vax family; 3b family */
 105         char                    arf_name[16];   /* Space terminated */
 106         char                    arf_date[AR_PORT_WORD]; /* sgetl() accessed */
 107         char                    arf_uid[AR_PORT_WORD];  /* sgetl() accessed */
 108         char                    arf_gid[AR_PORT_WORD];  /* sgetl() accessed */
 109         char                    arf_mode[AR_PORT_WORD]; /* sgetl() accessed */
 110         char                    arf_size[AR_PORT_WORD]; /* sgetl() accessed */
 111 }                       Arf_5;
 112 
 113 typedef struct {        /* Portable (6.0) ar format: vax family; 3b family */
 114         char                    ar_name[16];    /* Space terminated */
 115         /* left-adjusted fields; decimal ascii; blank filled */
 116         char                    ar_date[12];    
 117         char                    ar_uid[6];
 118         char                    ar_gid[6];
 119         char                    ar_mode[8];     /* octal ascii */
 120         char                    ar_size[10];
 121         /* special end-of-header string (AR_PORT_END_MAGIC) */
 122         char                    ar_fmag[2];     
 123 }                       Ar_port;
 124 
 125 enum ar_type {
 126                 AR_5,
 127                 AR_PORT
 128 };
 129 
 130 typedef unsigned int ar_port_word; // must be 4-bytes long
 131 
 132 typedef struct {
 133         FILE                    *fd;
 134         /* to distiguish ar format */
 135         enum ar_type            type;
 136         /* where first ar member header is at */
 137         long                    first_ar_mem;
 138         /* where the symbol lookup starts */
 139         long                    sym_begin;
 140         /* the number of symbols available */
 141         long                    num_symbols;
 142         /* length of symbol directory file */
 143         long                    sym_size;
 144         Arh_5                   arh_5;
 145         Ars_5                   ars_5;
 146         Arf_5                   arf_5;
 147         Ar_port                 ar_port;
 148 }                       Ar;
 149 
 150 /*
 151  * Static variables
 152  */
 153 
 154 /*
 155  * File table of contents
 156  */
 157 extern  timestruc_t&        read_archive(register Name target);
 158 static  Boolean         open_archive(char *filename, register Ar *arp);
 159 static  void            close_archive(register Ar *arp);
 160 static  Boolean         read_archive_dir(register Ar *arp, Name library, char **long_names_table);
 161 static  void            translate_entry(register Ar *arp, Name target, register Property member, char **long_names_table);
 162 static  long            sgetl(char *);
 163 
 164 /*
 165  *      read_archive(target)
 166  *
 167  *      Read the contents of an ar file.
 168  *
 169  *      Return value:
 170  *                              The time the member was created
 171  *
 172  *      Parameters:
 173  *              target          The member to find time for
 174  *
 175  *      Global variables used:
 176  *              empty_name      The Name ""
 177  */
 178 
 179 int read_member_header (Ar_port *header, FILE *fd, char* filename);
 180 int process_long_names_member (register Ar *arp, char **long_names_table, char *filename);
 181 
 182 timestruc_t&
 183 read_archive(register Name target)
 184 {
 185         register Property       member;
 186         wchar_t                 *slash;
 187         String_rec              true_member_name;
 188         wchar_t                 buffer[STRING_BUFFER_LENGTH];
 189         register Name           true_member = NULL;
 190         Ar                      ar;
 191         char                    *long_names_table = NULL; /* Table of long
 192                                                              member names */
 193 
 194         member = get_prop(target->prop, member_prop);
 195         /*
 196          * Check if the member has directory component.
 197          * If so, remove the dir and see if we know the date.
 198          */
 199         if (member->body.member.member != NULL) {
 200                 Wstring member_string(member->body.member.member);
 201                 wchar_t * wcb = member_string.get_string();
 202                 if((slash = (wchar_t *) wcsrchr(wcb, (int) slash_char)) != NULL) {
 203                         INIT_STRING_FROM_STACK(true_member_name, buffer);
 204                         append_string(member->body.member.library->string_mb,
 205                                       &true_member_name,
 206                                       FIND_LENGTH);
 207                         append_char((int) parenleft_char, &true_member_name);
 208                         append_string(slash + 1, &true_member_name, FIND_LENGTH);
 209                         append_char((int) parenright_char, &true_member_name);
 210                         true_member = GETNAME(true_member_name.buffer.start,
 211                                               FIND_LENGTH);
 212                         if (true_member->stat.time != file_no_time) {
 213                                 target->stat.time = true_member->stat.time;
 214                                 return target->stat.time;
 215                         }
 216                 }
 217         }
 218         if (open_archive(member->body.member.library->string_mb, &ar) == failed) {
 219                 if (errno == ENOENT) {
 220                         target->stat.stat_errno = ENOENT;
 221                         close_archive(&ar);
 222                         if (member->body.member.member == NULL) {
 223                                 member->body.member.member = empty_name;
 224                         }
 225                         return target->stat.time = file_doesnt_exist;
 226                 } else {
 227                         fatal(gettext("Can't access archive `%s': %s"),
 228                               member->body.member.library->string_mb,
 229                               errmsg(errno));
 230                 }
 231         }
 232         if (target->stat.time == file_no_time) {
 233                 if (read_archive_dir(&ar, member->body.member.library, 
 234                                      &long_names_table)
 235                     == failed){
 236                         fatal(gettext("Can't access archive `%s': %s"),
 237                               member->body.member.library->string_mb,
 238                               errmsg(errno));
 239                 }
 240         }
 241         if (member->body.member.entry != NULL) {
 242                 translate_entry(&ar, target, member,&long_names_table);
 243         }
 244         close_archive(&ar);
 245         if (long_names_table) {
 246                 retmem_mb(long_names_table);
 247         }
 248         if (true_member != NULL) {
 249                 target->stat.time = true_member->stat.time;
 250         }
 251         if (target->stat.time == file_no_time) {
 252                 target->stat.time = file_doesnt_exist;
 253         }
 254         return target->stat.time;
 255 }
 256 
 257 /*
 258  *      open_archive(filename, arp)
 259  *
 260  *      Return value:
 261  *                              Indicates if open failed or not
 262  *
 263  *      Parameters:
 264  *              filename        The name of the archive we need to read
 265  *              arp             Pointer to ar file description block
 266  *
 267  *      Global variables used:
 268  */
 269 static Boolean
 270 open_archive(char *filename, register Ar *arp)
 271 {
 272         int                     fd;
 273         char                    mag_5[AR_5_MAGIC_LENGTH];
 274         char                    mag_port[AR_PORT_MAGIC_LENGTH];
 275         char                    buffer[4];
 276 
 277         arp->fd = NULL;
 278         fd = open_vroot(filename, O_RDONLY, 0, NULL, VROOT_DEFAULT);
 279         if ((fd < 0) || ((arp->fd = fdopen(fd, "r")) == NULL)) {
 280                 return failed;
 281         }
 282         (void) fcntl(fileno(arp->fd), F_SETFD, 1);
 283 
 284         if (fread(mag_port, AR_PORT_MAGIC_LENGTH, 1, arp->fd) != 1) {
 285                 return failed;
 286         }
 287         if (IS_EQUALN(mag_port, AR_PORT_MAGIC, AR_PORT_MAGIC_LENGTH)) {
 288                 arp->type = AR_PORT;
 289                 /*
 290                  * Read in first member header to find out if there is 
 291                  * a symbol definition table.
 292                  */
 293 
 294                 int ret = read_member_header(&arp->ar_port, arp->fd, filename);
 295                 if (ret == failed) {
 296                         return failed;
 297                 } else if(ret == -1) {
 298                         /* There is no member header - empty archive */
 299                         arp->sym_size = arp->num_symbols = arp->sym_begin = 0L;
 300                         arp->first_ar_mem = ftell(arp->fd);
 301                         return succeeded;
 302                 }
 303                 /*
 304                  * The following values are the default if there is 
 305                  * no symbol directory and long member names.
 306                  */
 307                 arp->sym_size = arp->num_symbols = arp->sym_begin = 0L;
 308                 arp->first_ar_mem = ftell(arp->fd) - (long) sizeof (Ar_port);
 309 
 310                 /*
 311                  * Do we have a symbol table? A symbol table is always
 312                  * the first member in an archive. In 4.1.x it has the 
 313                  * name __.SYMDEF, in SVr4, it has the name "/        "
 314                  */
 315 /*
 316                 MBSTOWCS(wcs_buffer, "/               ");
 317                 if (IS_WEQUALN(arp->ar_port.ar_name, wcs_buffer, 16)) {
 318  */
 319                 if (IS_EQUALN(arp->ar_port.ar_name,
 320                               "/               ",
 321                               16)) {
 322                         if (sscanf(arp->ar_port.ar_size,
 323                                    "%ld",
 324                                    &arp->sym_size) != 1) {
 325                                 return failed;
 326                         }
 327                         arp->sym_size += (arp->sym_size & 1); /* round up */
 328                         if (fread(buffer, sizeof buffer, 1, arp->fd) != 1) {
 329                                 return failed;
 330                         }
 331                         arp->num_symbols = sgetl(buffer);
 332                         arp->sym_begin = ftell(arp->fd);
 333                         arp->first_ar_mem = arp->sym_begin +
 334                                                 arp->sym_size - sizeof buffer;
 335                 }
 336                 return succeeded;
 337         }
 338         fatal(gettext("`%s' is not an archive"), filename);
 339         /* NOTREACHED */
 340         return failed;
 341 }
 342 
 343 
 344 /*
 345  *      close_archive(arp)
 346  *
 347  *      Parameters:
 348  *              arp             Pointer to ar file description block
 349  *
 350  *      Global variables used:
 351  */
 352 static void
 353 close_archive(register Ar *arp)
 354 {
 355         if (arp->fd != NULL) {
 356                 (void) fclose(arp->fd);
 357         }
 358 }
 359 
 360 /*
 361  *      read_archive_dir(arp, library, long_names_table)
 362  *
 363  *      Reads the directory of an archive and enters all
 364  *      the members into the make symboltable in lib(member) format
 365  *      with their dates.
 366  *
 367  *      Parameters:
 368  *              arp             Pointer to ar file description block
 369  *              library         Name of lib to enter members for.
 370  *                              Used to form "lib(member)" string.
 371  *              long_names_table table that contains list of members
 372  *                              with names > 15 characters long
 373  *
 374  *      Global variables used:
 375  */
 376 static Boolean
 377 read_archive_dir(register Ar *arp, Name library, char **long_names_table)
 378 {
 379         wchar_t                 *name_string;
 380         wchar_t                 *member_string;
 381         register long           len;
 382         register wchar_t        *p;
 383         register char           *q;
 384         register Name           name;
 385         Property                member;
 386         long                    ptr;
 387         long                    date;
 388 
 389         int                     offset;
 390 
 391         /*
 392          * If any of the members has a name > 15 chars,
 393          * it will be found here.
 394          */
 395         if (process_long_names_member(arp, long_names_table, library->string_mb) == failed) {
 396                 return failed;
 397         }
 398         name_string = ALLOC_WC((int) (library->hash.length +
 399                                       (int) ar_member_name_len * 2));
 400         (void) mbstowcs(name_string, library->string_mb, (int) library->hash.length);
 401         member_string = name_string + library->hash.length;
 402         *member_string++ = (int) parenleft_char;
 403 
 404         if (fseek(arp->fd, arp->first_ar_mem, 0) != 0) {
 405                 goto read_error;
 406         }
 407         /* Read the directory using the appropriate format */
 408         switch (arp->type) {
 409         case AR_5:
 410             for (;;) {
 411                 if (fread((char *) &arp->arf_5, sizeof arp->arf_5, 1, arp->fd)
 412                     != 1) {
 413                         if (feof(arp->fd)) {
 414                                 return succeeded;
 415                         }
 416                         break;
 417                 }
 418                 len = sizeof arp->arf_5.arf_name;
 419                 for (p = member_string, q = arp->arf_5.arf_name;
 420                      (len > 0) && (*q != (int) nul_char) && !isspace(*q);
 421                      ) {
 422                         MBTOWC(p, q);
 423                         p++;
 424                         q++;
 425                 }
 426                 *p++ = (int) parenright_char;
 427                 *p = (int) nul_char;
 428                 name = GETNAME(name_string, FIND_LENGTH);
 429                 /*
 430                  * [tolik] Fix for dmake bug 1234018.
 431                  * If name->stat.time is already set, then it should not
 432                  * be changed. (D)make propogates time stamp for one
 433                  * member, and when it calls exists() for another member,
 434                  * the first one may be changed.
 435                  */
 436                 if(name->stat.time == file_no_time) {
 437                         name->stat.time.tv_sec = sgetl(arp->arf_5.arf_date);
 438                         name->stat.time.tv_nsec = LONG_MAX;
 439                 }
 440                 name->is_member = library->is_member;
 441                 member = maybe_append_prop(name, member_prop);
 442                 member->body.member.library = library;
 443                 *--p = (int) nul_char;
 444                 if (member->body.member.member == NULL) {
 445                         member->body.member.member =
 446                           GETNAME(member_string, FIND_LENGTH);
 447                 }
 448                 ptr = sgetl(arp->arf_5.arf_size);
 449                 ptr += (ptr & 1);
 450                 if (fseek(arp->fd, ptr, 1) != 0) {
 451                         goto read_error;
 452                 }
 453             }
 454             break;
 455         case AR_PORT:
 456             for (;;) {
 457                     if ((fread((char *) &arp->ar_port,
 458                                sizeof arp->ar_port,
 459                                1,
 460                                arp->fd) != 1) ||
 461                         !IS_EQUALN(arp->ar_port.ar_fmag,
 462                                    AR_PORT_END_MAGIC,
 463                                    sizeof arp->ar_port.ar_fmag)) {
 464                             if (feof(arp->fd)) {
 465                                     return succeeded;
 466                             }
 467                             fatal(
 468                                 gettext("Read error in archive `%s': invalid archive file member header at 0x%x"),
 469                                 library->string_mb,
 470                                 ftell(arp->fd)
 471                             );
 472                     }
 473                     /* If it's a long name, retrieve it from long name table */
 474                     if (arp->ar_port.ar_name[0] == '/') {
 475                             /*
 476                              * "len" is used for hashing the string.
 477                              * We're using "ar_member_name_len" instead of
 478                              * the actual name length since it's the longest
 479                              * string the "ar" command can handle at this
 480                              * point.
 481                              */
 482                             len = ar_member_name_len;
 483                             sscanf(arp->ar_port.ar_name + 1,
 484                                    "%ld",
 485                                    &offset);
 486                             q = *long_names_table + offset;
 487                     } else {
 488                             q = arp->ar_port.ar_name;        
 489                             len = sizeof arp->ar_port.ar_name;
 490                     }
 491                     
 492                     for (p = member_string;
 493                          (len > 0) &&
 494                          (*q != (int) nul_char) &&
 495                          !isspace(*q) &&
 496                          (*q != (int) slash_char);
 497                          ) {
 498                             MBTOWC(p, q);
 499                             p++;
 500                             q++;
 501                     }
 502                     *p++ = (int) parenright_char;
 503                     *p = (int) nul_char;
 504                     name = GETNAME(name_string, FIND_LENGTH);
 505                     name->is_member = library->is_member;
 506                     member = maybe_append_prop(name, member_prop);
 507                     member->body.member.library = library;
 508                     *--p = (int) nul_char;
 509                     if (member->body.member.member == NULL) {
 510                             member->body.member.member =
 511                               GETNAME(member_string, FIND_LENGTH);
 512                     }
 513                     if (sscanf(arp->ar_port.ar_date, "%ld", &date) != 1) {
 514                             WCSTOMBS(mbs_buffer, name_string);
 515                             fatal(gettext("Bad date field for member `%s' in archive `%s'"),
 516                                   mbs_buffer,
 517                                   library->string_mb);
 518                     }
 519                     /*
 520                      * [tolik] Fix for dmake bug 1234018.
 521                      */
 522                     if(name->stat.time == file_no_time) {
 523                         name->stat.time.tv_sec = date;
 524                         name->stat.time.tv_nsec = LONG_MAX;
 525                     }
 526                     if (sscanf(arp->ar_port.ar_size, "%ld", &ptr) != 1) {
 527                             WCSTOMBS(mbs_buffer, name_string);
 528                             fatal(gettext("Bad size field for member `%s' in archive `%s'"),
 529                                   mbs_buffer,
 530                                   library->string_mb);
 531                     }
 532                     ptr += (ptr & 1);
 533                     if (fseek(arp->fd, ptr, 1) != 0) {
 534                             goto read_error;
 535                     }   
 536             }
 537             break;
 538         }
 539 
 540         /* Only here if fread() [or IS_EQUALN()] failed and not at EOF */
 541 read_error:
 542         fatal(gettext("Read error in archive `%s': %s"),
 543               library->string_mb,
 544               errmsg(errno));
 545             /* NOTREACHED */
 546 }
 547 
 548 
 549 /*
 550  *      process_long_names_member(arp)
 551  *
 552  *      If the archive contains members with names longer
 553  *      than 15 characters, then it has a special member
 554  *      with the name "//        " that contains a table
 555  *      of null-terminated long names. This member
 556  *      is always the first member, after the symbol table
 557  *      if it exists.
 558  *
 559  *      Parameters:
 560  *              arp             Pointer to ar file description block
 561  *
 562  *      Global variables used:
 563  */
 564 int
 565 process_long_names_member(register Ar *arp, char **long_names_table, char *filename)
 566 {  
 567         Ar_port                 *ar_member_header;
 568         int                     table_size;
 569 
 570         if (fseek(arp->fd, arp->first_ar_mem, 0) != 0) {
 571                 return failed;
 572         }
 573         if ((ar_member_header = 
 574              (Ar_port *) alloca((int) sizeof(Ar_port))) == NULL){
 575                 perror(gettext("memory allocation failure"));
 576                 return failed;
 577         } 
 578         int ret = read_member_header(ar_member_header, arp->fd, filename);
 579         if (ret == failed) {
 580                 return failed;
 581         } else if(ret == -1) {
 582                 /* There is no member header - empty archive */
 583                 return succeeded;
 584         }
 585         /* Do we have special member containing long names? */
 586         if (IS_EQUALN(ar_member_header->ar_name, 
 587                       "//              ",
 588                       16)){
 589                 if (sscanf(ar_member_header->ar_size,
 590                            "%ld",
 591                            &table_size) != 1) {
 592                         return failed;
 593                 }
 594                 *long_names_table = (char *) malloc(table_size);
 595                 /* Read the list of long member names into the table */
 596                 if (fread(*long_names_table, table_size, 1, arp->fd) != 1) {
 597                         return failed;
 598                 }
 599                 arp->first_ar_mem = ftell(arp->fd);
 600         }
 601         return succeeded;
 602 }
 603 
 604 /*
 605  *      translate_entry(arp, target, member)
 606  *
 607  *      Finds the member for one lib.a((entry))
 608  *
 609  *      Parameters:
 610  *              arp             Pointer to ar file description block
 611  *              target          Target to find member name for
 612  *              member          Property to fill in with info
 613  *
 614  *      Global variables used:
 615  */
 616 static void
 617 translate_entry(register Ar *arp, Name target, register Property member, char **long_names_table)
 618 {
 619         register int            len;
 620         register int            i;
 621         wchar_t                 *member_string;
 622         ar_port_word            *offs;
 623         int                     strtablen;
 624         char                    *syms;           /* string table */
 625         char                    *csym;           /* string table */
 626         ar_port_word            *offend;         /* end of offsets table */
 627         int                     date;
 628         register wchar_t        *ap;
 629         register char           *hp;
 630         int                     maxs;
 631         int                     offset;
 632         char            buffer[4];
 633 
 634         if (arp->sym_begin == 0L || arp->num_symbols == 0L) {
 635                 fatal(gettext("Cannot find symbol `%s' in archive `%s'"),
 636                       member->body.member.entry->string_mb,
 637                       member->body.member.library->string_mb);
 638         }
 639 
 640         if (fseek(arp->fd, arp->sym_begin, 0) != 0) {
 641                 goto read_error;
 642         }
 643         member_string = ALLOC_WC((int) ((int) ar_member_name_len * 2));
 644 
 645         switch (arp->type) {
 646         case AR_5:
 647                 if ((len = member->body.member.entry->hash.length) > 8) {
 648                         len = 8;
 649                 }
 650                 for (i = 0; i < arp->num_symbols; i++) {
 651                         if (fread((char *) &arp->ars_5,
 652                                   sizeof arp->ars_5,
 653                                   1,
 654                                   arp->fd) != 1) {
 655                                 goto read_error;
 656                         }
 657                         if (IS_EQUALN(arp->ars_5.sym_name,
 658                                       member->body.member.entry->string_mb,
 659                                       len)) {
 660                                 if ((fseek(arp->fd,
 661                                            sgetl(arp->ars_5.sym_ptr),
 662                                            0) != 0) ||
 663                                     (fread((char *) &arp->arf_5,
 664                                            sizeof arp->arf_5,
 665                                            1,
 666                                            arp->fd) != 1)) {
 667                                         goto read_error;
 668                                 }
 669                                 MBSTOWCS(wcs_buffer, arp->arf_5.arf_name);
 670                                 (void) wcsncpy(member_string,
 671                                               wcs_buffer,
 672                                               wcslen(wcs_buffer));
 673                                 member_string[sizeof(arp->arf_5.arf_name)] =
 674                                                                 (int) nul_char;
 675                                 member->body.member.member =
 676                                         GETNAME(member_string, FIND_LENGTH);
 677                                 target->stat.time.tv_sec = sgetl(arp->arf_5.arf_date);
 678                                 target->stat.time.tv_nsec = LONG_MAX;
 679                                 return;
 680                         }
 681                 }
 682                 break;
 683         case AR_PORT:
 684                 offs = (ar_port_word *) alloca((int) (arp->num_symbols * AR_PORT_WORD));
 685                 if (fread((char *) offs,
 686                           AR_PORT_WORD,
 687                           (int) arp->num_symbols,
 688                           arp->fd) != arp->num_symbols) {
 689                         goto read_error;
 690                 }
 691 
 692                 for(i=0;i<arp->num_symbols;i++) {
 693                         *((int*)buffer)=offs[i];
 694                         offs[i]=(ar_port_word)sgetl(buffer);
 695                 }
 696 
 697                 strtablen=arp->sym_size-4-(int) (arp->num_symbols * AR_PORT_WORD);
 698                 syms = (char *) alloca(strtablen);
 699                 if (fread(syms,
 700                           sizeof (char),
 701                           strtablen,
 702                           arp->fd) != strtablen) {
 703                         goto read_error;
 704                 }
 705                 offend = &offs[arp->num_symbols];
 706                 while (offs < offend) {
 707                         maxs = strlen(member->body.member.entry->string_mb);
 708                         if(strlen(syms) > maxs)
 709                                 maxs = strlen(syms);
 710                         if (IS_EQUALN(syms,
 711                                       member->body.member.entry->string_mb,
 712                                       maxs)) {
 713                                 if (fseek(arp->fd,
 714                                           (long) *offs,
 715                                           0) != 0) {
 716                                         goto read_error;
 717                                 }
 718                                 if ((fread((char *) &arp->ar_port,
 719                                            sizeof arp->ar_port,
 720                                            1,
 721                                            arp->fd) != 1) ||
 722                                     !IS_EQUALN(arp->ar_port.ar_fmag,
 723                                                AR_PORT_END_MAGIC,
 724                                                sizeof arp->ar_port.ar_fmag)) {
 725                                         goto read_error;
 726                                 }
 727                                 if (sscanf(arp->ar_port.ar_date,
 728                                            "%ld",
 729                                            &date) != 1) {
 730                                         fatal(gettext("Bad date field for member `%s' in archive `%s'"),
 731                                               arp->ar_port.ar_name,
 732                                               target->string_mb);
 733                                 }
 734                     /* If it's a long name, retrieve it from long name table */
 735                     if (arp->ar_port.ar_name[0] == '/') {
 736                             sscanf(arp->ar_port.ar_name + 1,
 737                                    "%ld",
 738                                    &offset);
 739                             len = ar_member_name_len;
 740                             hp = *long_names_table + offset;
 741                     } else {
 742                             len = sizeof arp->ar_port.ar_name;
 743                             hp = arp->ar_port.ar_name;       
 744                     }
 745                                 ap = member_string;
 746                                 while (*hp &&
 747                                        (*hp != (int) slash_char) &&
 748                                        (ap < &member_string[len])) {
 749                                         MBTOWC(ap, hp);
 750                                         ap++;
 751                                         hp++;
 752                                 }
 753                                 *ap = (int) nul_char;
 754                                 member->body.member.member =
 755                                         GETNAME(member_string, FIND_LENGTH);
 756                                 target->stat.time.tv_sec = date;
 757                                 target->stat.time.tv_nsec = LONG_MAX;
 758                                 return;
 759                         }
 760                         offs++;
 761                         while(*syms!='\0') syms++;
 762                         syms++;
 763                 }
 764         }
 765         fatal(gettext("Cannot find symbol `%s' in archive `%s'"),
 766               member->body.member.entry->string_mb,
 767               member->body.member.library->string_mb);
 768         /*NOTREACHED*/
 769 
 770 read_error:
 771         if (ferror(arp->fd)) {
 772                 fatal(gettext("Read error in archive `%s': %s"),
 773                       member->body.member.library->string_mb,
 774                       errmsg(errno));
 775         } else {
 776                 fatal(gettext("Read error in archive `%s': Premature EOF"),
 777                       member->body.member.library->string_mb);
 778         }
 779 }
 780 
 781 /*
 782  *      sgetl(buffer)
 783  *
 784  *      The intent here is to provide a means to make the value of
 785  *      bytes in an io-buffer correspond to the value of a long
 786  *      in the memory while doing the io a long at a time.
 787  *      Files written and read in this way are machine-independent.
 788  *
 789  *      Return value:
 790  *                              Long int read from buffer
 791  *      Parameters:
 792  *              buffer          buffer we need to read long int from
 793  *
 794  *      Global variables used:
 795  */
 796 static long
 797 sgetl(register char *buffer)
 798 {
 799         register long           w = 0;
 800         register int            i = BITSPERBYTE * AR_PORT_WORD;
 801 
 802         while ((i -= BITSPERBYTE) >= 0) {
 803                 w |= (long) ((unsigned char) *buffer++) << i;
 804         }
 805         return w;
 806 }
 807 
 808 
 809 /*
 810  *      read_member_header(header, fd, filename)
 811  *
 812  *      reads the member header for the 4.1.x and SVr4 archives.
 813  *
 814  *      Return value:
 815  *                              fails if read error or member
 816  *                              header is not the right format
 817  *      Parameters:
 818  *              header          There's one before each archive member
 819  *              fd              file descriptor for the archive file.
 820  *
 821  *      Global variables used:
 822  */
 823 int 
 824 read_member_header(Ar_port *header, FILE *fd, char* filename)
 825 {
 826         int num = fread((char *) header, sizeof (Ar_port), 1, fd);
 827         if (num != 1 && feof(fd)) {
 828                 /* There is no member header - empty archive */
 829                 return -1;
 830         }
 831         if ((num != 1) ||
 832             !IS_EQUALN(
 833                 AR_PORT_END_MAGIC,
 834                 header->ar_fmag,
 835                 sizeof (header->ar_fmag)
 836             )
 837         ) {
 838                 fatal(
 839                         gettext("Read error in archive `%s': invalid archive file member header at 0x%x"),
 840                         filename,
 841                         ftell(fd)
 842                 );
 843         }
 844         return succeeded;
 845 }
 846