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