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