847 lines
23 KiB
C++
847 lines
23 KiB
C++
|
/*
|
||
|
* CDDL HEADER START
|
||
|
*
|
||
|
* The contents of this file are subject to the terms of the
|
||
|
* Common Development and Distribution License (the "License").
|
||
|
* You may not use this file except in compliance with the License.
|
||
|
*
|
||
|
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||
|
* or http://www.opensolaris.org/os/licensing.
|
||
|
* See the License for the specific language governing permissions
|
||
|
* and limitations under the License.
|
||
|
*
|
||
|
* When distributing Covered Code, include this CDDL HEADER in each
|
||
|
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||
|
* If applicable, add the following below this CDDL HEADER, with the
|
||
|
* fields enclosed by brackets "[]" replaced with your own identifying
|
||
|
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||
|
*
|
||
|
* CDDL HEADER END
|
||
|
*/
|
||
|
/*
|
||
|
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
|
||
|
* Use is subject to license terms.
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* ar.c
|
||
|
*
|
||
|
* Deal with the lib.a(member.o) and lib.a((entry-point)) notations
|
||
|
*
|
||
|
* Look inside archives for notations a(b) and a((b))
|
||
|
* a(b) is file member b in archive a
|
||
|
* a((b)) is entry point b in object archive a
|
||
|
*
|
||
|
* For 6.0, create a make which can understand all archive
|
||
|
* formats. This is kind of tricky, and <ar.h> isnt any help.
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* Included files
|
||
|
*/
|
||
|
#include <alloca.h> /* alloca() */
|
||
|
#include <ar.h>
|
||
|
#include <errno.h> /* errno */
|
||
|
#include <fcntl.h> /* open() */
|
||
|
#include <libintl.h>
|
||
|
#include <mk/defs.h>
|
||
|
#include <mksh/misc.h> /* retmem_mb() */
|
||
|
|
||
|
struct ranlib {
|
||
|
union {
|
||
|
off_t ran_strx; /* string table index of */
|
||
|
char *ran_name; /* symbol defined by */
|
||
|
} ran_un;
|
||
|
off_t ran_off; /* library member at this offset */
|
||
|
};
|
||
|
|
||
|
#include <unistd.h> /* close() */
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Defined macros
|
||
|
*/
|
||
|
#ifndef S5EMUL
|
||
|
#undef BITSPERBYTE
|
||
|
#define BITSPERBYTE 8
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* Defines for all the different archive formats. See next comment
|
||
|
* block for justification for not using <ar.h>s versions.
|
||
|
*/
|
||
|
#define AR_5_MAGIC "<ar>" /* 5.0 format magic string */
|
||
|
#define AR_5_MAGIC_LENGTH 4 /* 5.0 format string length */
|
||
|
|
||
|
#define AR_PORT_MAGIC "!<arch>\n" /* Port. (6.0) magic string */
|
||
|
#define AR_PORT_MAGIC_LENGTH 8 /* Port. (6.0) string length */
|
||
|
#define AR_PORT_END_MAGIC "`\n" /* Port. (6.0) end of header */
|
||
|
#define AR_PORT_WORD 4 /* Port. (6.0) 'word' length */
|
||
|
|
||
|
/*
|
||
|
* typedefs & structs
|
||
|
*/
|
||
|
/*
|
||
|
* These are the archive file headers for the formats. Note
|
||
|
* that it really doesnt matter if these structures are defined
|
||
|
* here. They are correct as of the respective archive format
|
||
|
* releases. If the archive format is changed, then since backwards
|
||
|
* compatability is the desired behavior, a new structure is added
|
||
|
* to the list.
|
||
|
*/
|
||
|
typedef struct { /* 5.0 ar header format: vax family; 3b family */
|
||
|
char ar_magic[AR_5_MAGIC_LENGTH]; /* AR_5_MAGIC*/
|
||
|
char ar_name[16]; /* Space terminated */
|
||
|
char ar_date[AR_PORT_WORD]; /* sgetl() accessed */
|
||
|
char ar_syms[AR_PORT_WORD]; /* sgetl() accessed */
|
||
|
} Arh_5;
|
||
|
|
||
|
typedef struct { /* 5.0 ar symbol format: vax family; 3b family */
|
||
|
char sym_name[8]; /* Space terminated */
|
||
|
char sym_ptr[AR_PORT_WORD]; /* sgetl() accessed */
|
||
|
} Ars_5;
|
||
|
|
||
|
typedef struct { /* 5.0 ar member format: vax family; 3b family */
|
||
|
char arf_name[16]; /* Space terminated */
|
||
|
char arf_date[AR_PORT_WORD]; /* sgetl() accessed */
|
||
|
char arf_uid[AR_PORT_WORD]; /* sgetl() accessed */
|
||
|
char arf_gid[AR_PORT_WORD]; /* sgetl() accessed */
|
||
|
char arf_mode[AR_PORT_WORD]; /* sgetl() accessed */
|
||
|
char arf_size[AR_PORT_WORD]; /* sgetl() accessed */
|
||
|
} Arf_5;
|
||
|
|
||
|
typedef struct { /* Portable (6.0) ar format: vax family; 3b family */
|
||
|
char ar_name[16]; /* Space terminated */
|
||
|
/* left-adjusted fields; decimal ascii; blank filled */
|
||
|
char ar_date[12];
|
||
|
char ar_uid[6];
|
||
|
char ar_gid[6];
|
||
|
char ar_mode[8]; /* octal ascii */
|
||
|
char ar_size[10];
|
||
|
/* special end-of-header string (AR_PORT_END_MAGIC) */
|
||
|
char ar_fmag[2];
|
||
|
} Ar_port;
|
||
|
|
||
|
enum ar_type {
|
||
|
AR_5,
|
||
|
AR_PORT
|
||
|
};
|
||
|
|
||
|
typedef unsigned int ar_port_word; // must be 4-bytes long
|
||
|
|
||
|
typedef struct {
|
||
|
FILE *fd;
|
||
|
/* to distiguish ar format */
|
||
|
enum ar_type type;
|
||
|
/* where first ar member header is at */
|
||
|
long first_ar_mem;
|
||
|
/* where the symbol lookup starts */
|
||
|
long sym_begin;
|
||
|
/* the number of symbols available */
|
||
|
long num_symbols;
|
||
|
/* length of symbol directory file */
|
||
|
long sym_size;
|
||
|
Arh_5 arh_5;
|
||
|
Ars_5 ars_5;
|
||
|
Arf_5 arf_5;
|
||
|
Ar_port ar_port;
|
||
|
} Ar;
|
||
|
|
||
|
/*
|
||
|
* Static variables
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* File table of contents
|
||
|
*/
|
||
|
extern timestruc_t& read_archive(register Name target);
|
||
|
static Boolean open_archive(char *filename, register Ar *arp);
|
||
|
static void close_archive(register Ar *arp);
|
||
|
static Boolean read_archive_dir(register Ar *arp, Name library, char **long_names_table);
|
||
|
static void translate_entry(register Ar *arp, Name target, register Property member, char **long_names_table);
|
||
|
static long sgetl(char *);
|
||
|
|
||
|
/*
|
||
|
* read_archive(target)
|
||
|
*
|
||
|
* Read the contents of an ar file.
|
||
|
*
|
||
|
* Return value:
|
||
|
* The time the member was created
|
||
|
*
|
||
|
* Parameters:
|
||
|
* target The member to find time for
|
||
|
*
|
||
|
* Global variables used:
|
||
|
* empty_name The Name ""
|
||
|
*/
|
||
|
|
||
|
int read_member_header (Ar_port *header, FILE *fd, char* filename);
|
||
|
int process_long_names_member (register Ar *arp, char **long_names_table, char *filename);
|
||
|
|
||
|
timestruc_t&
|
||
|
read_archive(register Name target)
|
||
|
{
|
||
|
register Property member;
|
||
|
wchar_t *slash;
|
||
|
String_rec true_member_name;
|
||
|
wchar_t buffer[STRING_BUFFER_LENGTH];
|
||
|
register Name true_member = NULL;
|
||
|
Ar ar;
|
||
|
char *long_names_table = NULL; /* Table of long
|
||
|
member names */
|
||
|
|
||
|
member = get_prop(target->prop, member_prop);
|
||
|
/*
|
||
|
* Check if the member has directory component.
|
||
|
* If so, remove the dir and see if we know the date.
|
||
|
*/
|
||
|
if (member->body.member.member != NULL) {
|
||
|
Wstring member_string(member->body.member.member);
|
||
|
wchar_t * wcb = member_string.get_string();
|
||
|
if((slash = (wchar_t *) wcsrchr(wcb, (int) slash_char)) != NULL) {
|
||
|
INIT_STRING_FROM_STACK(true_member_name, buffer);
|
||
|
append_string(member->body.member.library->string_mb,
|
||
|
&true_member_name,
|
||
|
FIND_LENGTH);
|
||
|
append_char((int) parenleft_char, &true_member_name);
|
||
|
append_string(slash + 1, &true_member_name, FIND_LENGTH);
|
||
|
append_char((int) parenright_char, &true_member_name);
|
||
|
true_member = GETNAME(true_member_name.buffer.start,
|
||
|
FIND_LENGTH);
|
||
|
if (true_member->stat.time != file_no_time) {
|
||
|
target->stat.time = true_member->stat.time;
|
||
|
return target->stat.time;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (open_archive(member->body.member.library->string_mb, &ar) == failed) {
|
||
|
if (errno == ENOENT) {
|
||
|
target->stat.stat_errno = ENOENT;
|
||
|
close_archive(&ar);
|
||
|
if (member->body.member.member == NULL) {
|
||
|
member->body.member.member = empty_name;
|
||
|
}
|
||
|
return target->stat.time = file_doesnt_exist;
|
||
|
} else {
|
||
|
fatal(gettext("Can't access archive `%s': %s"),
|
||
|
member->body.member.library->string_mb,
|
||
|
errmsg(errno));
|
||
|
}
|
||
|
}
|
||
|
if (target->stat.time == file_no_time) {
|
||
|
if (read_archive_dir(&ar, member->body.member.library,
|
||
|
&long_names_table)
|
||
|
== failed){
|
||
|
fatal(gettext("Can't access archive `%s': %s"),
|
||
|
member->body.member.library->string_mb,
|
||
|
errmsg(errno));
|
||
|
}
|
||
|
}
|
||
|
if (member->body.member.entry != NULL) {
|
||
|
translate_entry(&ar, target, member,&long_names_table);
|
||
|
}
|
||
|
close_archive(&ar);
|
||
|
if (long_names_table) {
|
||
|
retmem_mb(long_names_table);
|
||
|
}
|
||
|
if (true_member != NULL) {
|
||
|
target->stat.time = true_member->stat.time;
|
||
|
}
|
||
|
if (target->stat.time == file_no_time) {
|
||
|
target->stat.time = file_doesnt_exist;
|
||
|
}
|
||
|
return target->stat.time;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* open_archive(filename, arp)
|
||
|
*
|
||
|
* Return value:
|
||
|
* Indicates if open failed or not
|
||
|
*
|
||
|
* Parameters:
|
||
|
* filename The name of the archive we need to read
|
||
|
* arp Pointer to ar file description block
|
||
|
*
|
||
|
* Global variables used:
|
||
|
*/
|
||
|
static Boolean
|
||
|
open_archive(char *filename, register Ar *arp)
|
||
|
{
|
||
|
int fd;
|
||
|
char mag_5[AR_5_MAGIC_LENGTH];
|
||
|
char mag_port[AR_PORT_MAGIC_LENGTH];
|
||
|
char buffer[4];
|
||
|
|
||
|
arp->fd = NULL;
|
||
|
fd = open_vroot(filename, O_RDONLY, 0, NULL, VROOT_DEFAULT);
|
||
|
if ((fd < 0) || ((arp->fd = fdopen(fd, "r")) == NULL)) {
|
||
|
return failed;
|
||
|
}
|
||
|
(void) fcntl(fileno(arp->fd), F_SETFD, 1);
|
||
|
|
||
|
if (fread(mag_port, AR_PORT_MAGIC_LENGTH, 1, arp->fd) != 1) {
|
||
|
return failed;
|
||
|
}
|
||
|
if (IS_EQUALN(mag_port, AR_PORT_MAGIC, AR_PORT_MAGIC_LENGTH)) {
|
||
|
arp->type = AR_PORT;
|
||
|
/*
|
||
|
* Read in first member header to find out if there is
|
||
|
* a symbol definition table.
|
||
|
*/
|
||
|
|
||
|
int ret = read_member_header(&arp->ar_port, arp->fd, filename);
|
||
|
if (ret == failed) {
|
||
|
return failed;
|
||
|
} else if(ret == -1) {
|
||
|
/* There is no member header - empty archive */
|
||
|
arp->sym_size = arp->num_symbols = arp->sym_begin = 0L;
|
||
|
arp->first_ar_mem = ftell(arp->fd);
|
||
|
return succeeded;
|
||
|
}
|
||
|
/*
|
||
|
* The following values are the default if there is
|
||
|
* no symbol directory and long member names.
|
||
|
*/
|
||
|
arp->sym_size = arp->num_symbols = arp->sym_begin = 0L;
|
||
|
arp->first_ar_mem = ftell(arp->fd) - (long) sizeof (Ar_port);
|
||
|
|
||
|
/*
|
||
|
* Do we have a symbol table? A symbol table is always
|
||
|
* the first member in an archive. In 4.1.x it has the
|
||
|
* name __.SYMDEF, in SVr4, it has the name "/ "
|
||
|
*/
|
||
|
/*
|
||
|
MBSTOWCS(wcs_buffer, "/ ");
|
||
|
if (IS_WEQUALN(arp->ar_port.ar_name, wcs_buffer, 16)) {
|
||
|
*/
|
||
|
if (IS_EQUALN(arp->ar_port.ar_name,
|
||
|
"/ ",
|
||
|
16)) {
|
||
|
if (sscanf(arp->ar_port.ar_size,
|
||
|
"%ld",
|
||
|
&arp->sym_size) != 1) {
|
||
|
return failed;
|
||
|
}
|
||
|
arp->sym_size += (arp->sym_size & 1); /* round up */
|
||
|
if (fread(buffer, sizeof buffer, 1, arp->fd) != 1) {
|
||
|
return failed;
|
||
|
}
|
||
|
arp->num_symbols = sgetl(buffer);
|
||
|
arp->sym_begin = ftell(arp->fd);
|
||
|
arp->first_ar_mem = arp->sym_begin +
|
||
|
arp->sym_size - sizeof buffer;
|
||
|
}
|
||
|
return succeeded;
|
||
|
}
|
||
|
fatal(gettext("`%s' is not an archive"), filename);
|
||
|
/* NOTREACHED */
|
||
|
return failed;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* close_archive(arp)
|
||
|
*
|
||
|
* Parameters:
|
||
|
* arp Pointer to ar file description block
|
||
|
*
|
||
|
* Global variables used:
|
||
|
*/
|
||
|
static void
|
||
|
close_archive(register Ar *arp)
|
||
|
{
|
||
|
if (arp->fd != NULL) {
|
||
|
(void) fclose(arp->fd);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* read_archive_dir(arp, library, long_names_table)
|
||
|
*
|
||
|
* Reads the directory of an archive and enters all
|
||
|
* the members into the make symboltable in lib(member) format
|
||
|
* with their dates.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* arp Pointer to ar file description block
|
||
|
* library Name of lib to enter members for.
|
||
|
* Used to form "lib(member)" string.
|
||
|
* long_names_table table that contains list of members
|
||
|
* with names > 15 characters long
|
||
|
*
|
||
|
* Global variables used:
|
||
|
*/
|
||
|
static Boolean
|
||
|
read_archive_dir(register Ar *arp, Name library, char **long_names_table)
|
||
|
{
|
||
|
wchar_t *name_string;
|
||
|
wchar_t *member_string;
|
||
|
register long len;
|
||
|
register wchar_t *p;
|
||
|
register char *q;
|
||
|
register Name name;
|
||
|
Property member;
|
||
|
long ptr;
|
||
|
long date;
|
||
|
|
||
|
int offset;
|
||
|
|
||
|
/*
|
||
|
* If any of the members has a name > 15 chars,
|
||
|
* it will be found here.
|
||
|
*/
|
||
|
if (process_long_names_member(arp, long_names_table, library->string_mb) == failed) {
|
||
|
return failed;
|
||
|
}
|
||
|
name_string = ALLOC_WC((int) (library->hash.length +
|
||
|
(int) ar_member_name_len * 2));
|
||
|
(void) mbstowcs(name_string, library->string_mb, (int) library->hash.length);
|
||
|
member_string = name_string + library->hash.length;
|
||
|
*member_string++ = (int) parenleft_char;
|
||
|
|
||
|
if (fseek(arp->fd, arp->first_ar_mem, 0) != 0) {
|
||
|
goto read_error;
|
||
|
}
|
||
|
/* Read the directory using the appropriate format */
|
||
|
switch (arp->type) {
|
||
|
case AR_5:
|
||
|
for (;;) {
|
||
|
if (fread((char *) &arp->arf_5, sizeof arp->arf_5, 1, arp->fd)
|
||
|
!= 1) {
|
||
|
if (feof(arp->fd)) {
|
||
|
return succeeded;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
len = sizeof arp->arf_5.arf_name;
|
||
|
for (p = member_string, q = arp->arf_5.arf_name;
|
||
|
(len > 0) && (*q != (int) nul_char) && !isspace(*q);
|
||
|
) {
|
||
|
MBTOWC(p, q);
|
||
|
p++;
|
||
|
q++;
|
||
|
}
|
||
|
*p++ = (int) parenright_char;
|
||
|
*p = (int) nul_char;
|
||
|
name = GETNAME(name_string, FIND_LENGTH);
|
||
|
/*
|
||
|
* [tolik] Fix for dmake bug 1234018.
|
||
|
* If name->stat.time is already set, then it should not
|
||
|
* be changed. (D)make propogates time stamp for one
|
||
|
* member, and when it calls exists() for another member,
|
||
|
* the first one may be changed.
|
||
|
*/
|
||
|
if(name->stat.time == file_no_time) {
|
||
|
name->stat.time.tv_sec = sgetl(arp->arf_5.arf_date);
|
||
|
name->stat.time.tv_nsec = LONG_MAX;
|
||
|
}
|
||
|
name->is_member = library->is_member;
|
||
|
member = maybe_append_prop(name, member_prop);
|
||
|
member->body.member.library = library;
|
||
|
*--p = (int) nul_char;
|
||
|
if (member->body.member.member == NULL) {
|
||
|
member->body.member.member =
|
||
|
GETNAME(member_string, FIND_LENGTH);
|
||
|
}
|
||
|
ptr = sgetl(arp->arf_5.arf_size);
|
||
|
ptr += (ptr & 1);
|
||
|
if (fseek(arp->fd, ptr, 1) != 0) {
|
||
|
goto read_error;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case AR_PORT:
|
||
|
for (;;) {
|
||
|
if ((fread((char *) &arp->ar_port,
|
||
|
sizeof arp->ar_port,
|
||
|
1,
|
||
|
arp->fd) != 1) ||
|
||
|
!IS_EQUALN(arp->ar_port.ar_fmag,
|
||
|
AR_PORT_END_MAGIC,
|
||
|
sizeof arp->ar_port.ar_fmag)) {
|
||
|
if (feof(arp->fd)) {
|
||
|
return succeeded;
|
||
|
}
|
||
|
fatal(
|
||
|
gettext("Read error in archive `%s': invalid archive file member header at 0x%x"),
|
||
|
library->string_mb,
|
||
|
ftell(arp->fd)
|
||
|
);
|
||
|
}
|
||
|
/* If it's a long name, retrieve it from long name table */
|
||
|
if (arp->ar_port.ar_name[0] == '/') {
|
||
|
/*
|
||
|
* "len" is used for hashing the string.
|
||
|
* We're using "ar_member_name_len" instead of
|
||
|
* the actual name length since it's the longest
|
||
|
* string the "ar" command can handle at this
|
||
|
* point.
|
||
|
*/
|
||
|
len = ar_member_name_len;
|
||
|
sscanf(arp->ar_port.ar_name + 1,
|
||
|
"%ld",
|
||
|
&offset);
|
||
|
q = *long_names_table + offset;
|
||
|
} else {
|
||
|
q = arp->ar_port.ar_name;
|
||
|
len = sizeof arp->ar_port.ar_name;
|
||
|
}
|
||
|
|
||
|
for (p = member_string;
|
||
|
(len > 0) &&
|
||
|
(*q != (int) nul_char) &&
|
||
|
!isspace(*q) &&
|
||
|
(*q != (int) slash_char);
|
||
|
) {
|
||
|
MBTOWC(p, q);
|
||
|
p++;
|
||
|
q++;
|
||
|
}
|
||
|
*p++ = (int) parenright_char;
|
||
|
*p = (int) nul_char;
|
||
|
name = GETNAME(name_string, FIND_LENGTH);
|
||
|
name->is_member = library->is_member;
|
||
|
member = maybe_append_prop(name, member_prop);
|
||
|
member->body.member.library = library;
|
||
|
*--p = (int) nul_char;
|
||
|
if (member->body.member.member == NULL) {
|
||
|
member->body.member.member =
|
||
|
GETNAME(member_string, FIND_LENGTH);
|
||
|
}
|
||
|
if (sscanf(arp->ar_port.ar_date, "%ld", &date) != 1) {
|
||
|
WCSTOMBS(mbs_buffer, name_string);
|
||
|
fatal(gettext("Bad date field for member `%s' in archive `%s'"),
|
||
|
mbs_buffer,
|
||
|
library->string_mb);
|
||
|
}
|
||
|
/*
|
||
|
* [tolik] Fix for dmake bug 1234018.
|
||
|
*/
|
||
|
if(name->stat.time == file_no_time) {
|
||
|
name->stat.time.tv_sec = date;
|
||
|
name->stat.time.tv_nsec = LONG_MAX;
|
||
|
}
|
||
|
if (sscanf(arp->ar_port.ar_size, "%ld", &ptr) != 1) {
|
||
|
WCSTOMBS(mbs_buffer, name_string);
|
||
|
fatal(gettext("Bad size field for member `%s' in archive `%s'"),
|
||
|
mbs_buffer,
|
||
|
library->string_mb);
|
||
|
}
|
||
|
ptr += (ptr & 1);
|
||
|
if (fseek(arp->fd, ptr, 1) != 0) {
|
||
|
goto read_error;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Only here if fread() [or IS_EQUALN()] failed and not at EOF */
|
||
|
read_error:
|
||
|
fatal(gettext("Read error in archive `%s': %s"),
|
||
|
library->string_mb,
|
||
|
errmsg(errno));
|
||
|
/* NOTREACHED */
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* process_long_names_member(arp)
|
||
|
*
|
||
|
* If the archive contains members with names longer
|
||
|
* than 15 characters, then it has a special member
|
||
|
* with the name "// " that contains a table
|
||
|
* of null-terminated long names. This member
|
||
|
* is always the first member, after the symbol table
|
||
|
* if it exists.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* arp Pointer to ar file description block
|
||
|
*
|
||
|
* Global variables used:
|
||
|
*/
|
||
|
int
|
||
|
process_long_names_member(register Ar *arp, char **long_names_table, char *filename)
|
||
|
{
|
||
|
Ar_port *ar_member_header;
|
||
|
int table_size;
|
||
|
|
||
|
if (fseek(arp->fd, arp->first_ar_mem, 0) != 0) {
|
||
|
return failed;
|
||
|
}
|
||
|
if ((ar_member_header =
|
||
|
(Ar_port *) alloca((int) sizeof(Ar_port))) == NULL){
|
||
|
perror(gettext("memory allocation failure"));
|
||
|
return failed;
|
||
|
}
|
||
|
int ret = read_member_header(ar_member_header, arp->fd, filename);
|
||
|
if (ret == failed) {
|
||
|
return failed;
|
||
|
} else if(ret == -1) {
|
||
|
/* There is no member header - empty archive */
|
||
|
return succeeded;
|
||
|
}
|
||
|
/* Do we have special member containing long names? */
|
||
|
if (IS_EQUALN(ar_member_header->ar_name,
|
||
|
"// ",
|
||
|
16)){
|
||
|
if (sscanf(ar_member_header->ar_size,
|
||
|
"%ld",
|
||
|
&table_size) != 1) {
|
||
|
return failed;
|
||
|
}
|
||
|
*long_names_table = (char *) malloc(table_size);
|
||
|
/* Read the list of long member names into the table */
|
||
|
if (fread(*long_names_table, table_size, 1, arp->fd) != 1) {
|
||
|
return failed;
|
||
|
}
|
||
|
arp->first_ar_mem = ftell(arp->fd);
|
||
|
}
|
||
|
return succeeded;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* translate_entry(arp, target, member)
|
||
|
*
|
||
|
* Finds the member for one lib.a((entry))
|
||
|
*
|
||
|
* Parameters:
|
||
|
* arp Pointer to ar file description block
|
||
|
* target Target to find member name for
|
||
|
* member Property to fill in with info
|
||
|
*
|
||
|
* Global variables used:
|
||
|
*/
|
||
|
static void
|
||
|
translate_entry(register Ar *arp, Name target, register Property member, char **long_names_table)
|
||
|
{
|
||
|
register int len;
|
||
|
register int i;
|
||
|
wchar_t *member_string;
|
||
|
ar_port_word *offs;
|
||
|
int strtablen;
|
||
|
char *syms; /* string table */
|
||
|
char *csym; /* string table */
|
||
|
ar_port_word *offend; /* end of offsets table */
|
||
|
int date;
|
||
|
register wchar_t *ap;
|
||
|
register char *hp;
|
||
|
int maxs;
|
||
|
int offset;
|
||
|
char buffer[4];
|
||
|
|
||
|
if (arp->sym_begin == 0L || arp->num_symbols == 0L) {
|
||
|
fatal(gettext("Cannot find symbol `%s' in archive `%s'"),
|
||
|
member->body.member.entry->string_mb,
|
||
|
member->body.member.library->string_mb);
|
||
|
}
|
||
|
|
||
|
if (fseek(arp->fd, arp->sym_begin, 0) != 0) {
|
||
|
goto read_error;
|
||
|
}
|
||
|
member_string = ALLOC_WC((int) ((int) ar_member_name_len * 2));
|
||
|
|
||
|
switch (arp->type) {
|
||
|
case AR_5:
|
||
|
if ((len = member->body.member.entry->hash.length) > 8) {
|
||
|
len = 8;
|
||
|
}
|
||
|
for (i = 0; i < arp->num_symbols; i++) {
|
||
|
if (fread((char *) &arp->ars_5,
|
||
|
sizeof arp->ars_5,
|
||
|
1,
|
||
|
arp->fd) != 1) {
|
||
|
goto read_error;
|
||
|
}
|
||
|
if (IS_EQUALN(arp->ars_5.sym_name,
|
||
|
member->body.member.entry->string_mb,
|
||
|
len)) {
|
||
|
if ((fseek(arp->fd,
|
||
|
sgetl(arp->ars_5.sym_ptr),
|
||
|
0) != 0) ||
|
||
|
(fread((char *) &arp->arf_5,
|
||
|
sizeof arp->arf_5,
|
||
|
1,
|
||
|
arp->fd) != 1)) {
|
||
|
goto read_error;
|
||
|
}
|
||
|
MBSTOWCS(wcs_buffer, arp->arf_5.arf_name);
|
||
|
(void) wcsncpy(member_string,
|
||
|
wcs_buffer,
|
||
|
wcslen(wcs_buffer));
|
||
|
member_string[sizeof(arp->arf_5.arf_name)] =
|
||
|
(int) nul_char;
|
||
|
member->body.member.member =
|
||
|
GETNAME(member_string, FIND_LENGTH);
|
||
|
target->stat.time.tv_sec = sgetl(arp->arf_5.arf_date);
|
||
|
target->stat.time.tv_nsec = LONG_MAX;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case AR_PORT:
|
||
|
offs = (ar_port_word *) alloca((int) (arp->num_symbols * AR_PORT_WORD));
|
||
|
if (fread((char *) offs,
|
||
|
AR_PORT_WORD,
|
||
|
(int) arp->num_symbols,
|
||
|
arp->fd) != arp->num_symbols) {
|
||
|
goto read_error;
|
||
|
}
|
||
|
|
||
|
for(i=0;i<arp->num_symbols;i++) {
|
||
|
*((int*)buffer)=offs[i];
|
||
|
offs[i]=(ar_port_word)sgetl(buffer);
|
||
|
}
|
||
|
|
||
|
strtablen=arp->sym_size-4-(int) (arp->num_symbols * AR_PORT_WORD);
|
||
|
syms = (char *) alloca(strtablen);
|
||
|
if (fread(syms,
|
||
|
sizeof (char),
|
||
|
strtablen,
|
||
|
arp->fd) != strtablen) {
|
||
|
goto read_error;
|
||
|
}
|
||
|
offend = &offs[arp->num_symbols];
|
||
|
while (offs < offend) {
|
||
|
maxs = strlen(member->body.member.entry->string_mb);
|
||
|
if(strlen(syms) > maxs)
|
||
|
maxs = strlen(syms);
|
||
|
if (IS_EQUALN(syms,
|
||
|
member->body.member.entry->string_mb,
|
||
|
maxs)) {
|
||
|
if (fseek(arp->fd,
|
||
|
(long) *offs,
|
||
|
0) != 0) {
|
||
|
goto read_error;
|
||
|
}
|
||
|
if ((fread((char *) &arp->ar_port,
|
||
|
sizeof arp->ar_port,
|
||
|
1,
|
||
|
arp->fd) != 1) ||
|
||
|
!IS_EQUALN(arp->ar_port.ar_fmag,
|
||
|
AR_PORT_END_MAGIC,
|
||
|
sizeof arp->ar_port.ar_fmag)) {
|
||
|
goto read_error;
|
||
|
}
|
||
|
if (sscanf(arp->ar_port.ar_date,
|
||
|
"%ld",
|
||
|
&date) != 1) {
|
||
|
fatal(gettext("Bad date field for member `%s' in archive `%s'"),
|
||
|
arp->ar_port.ar_name,
|
||
|
target->string_mb);
|
||
|
}
|
||
|
/* If it's a long name, retrieve it from long name table */
|
||
|
if (arp->ar_port.ar_name[0] == '/') {
|
||
|
sscanf(arp->ar_port.ar_name + 1,
|
||
|
"%ld",
|
||
|
&offset);
|
||
|
len = ar_member_name_len;
|
||
|
hp = *long_names_table + offset;
|
||
|
} else {
|
||
|
len = sizeof arp->ar_port.ar_name;
|
||
|
hp = arp->ar_port.ar_name;
|
||
|
}
|
||
|
ap = member_string;
|
||
|
while (*hp &&
|
||
|
(*hp != (int) slash_char) &&
|
||
|
(ap < &member_string[len])) {
|
||
|
MBTOWC(ap, hp);
|
||
|
ap++;
|
||
|
hp++;
|
||
|
}
|
||
|
*ap = (int) nul_char;
|
||
|
member->body.member.member =
|
||
|
GETNAME(member_string, FIND_LENGTH);
|
||
|
target->stat.time.tv_sec = date;
|
||
|
target->stat.time.tv_nsec = LONG_MAX;
|
||
|
return;
|
||
|
}
|
||
|
offs++;
|
||
|
while(*syms!='\0') syms++;
|
||
|
syms++;
|
||
|
}
|
||
|
}
|
||
|
fatal(gettext("Cannot find symbol `%s' in archive `%s'"),
|
||
|
member->body.member.entry->string_mb,
|
||
|
member->body.member.library->string_mb);
|
||
|
/*NOTREACHED*/
|
||
|
|
||
|
read_error:
|
||
|
if (ferror(arp->fd)) {
|
||
|
fatal(gettext("Read error in archive `%s': %s"),
|
||
|
member->body.member.library->string_mb,
|
||
|
errmsg(errno));
|
||
|
} else {
|
||
|
fatal(gettext("Read error in archive `%s': Premature EOF"),
|
||
|
member->body.member.library->string_mb);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* sgetl(buffer)
|
||
|
*
|
||
|
* The intent here is to provide a means to make the value of
|
||
|
* bytes in an io-buffer correspond to the value of a long
|
||
|
* in the memory while doing the io a long at a time.
|
||
|
* Files written and read in this way are machine-independent.
|
||
|
*
|
||
|
* Return value:
|
||
|
* Long int read from buffer
|
||
|
* Parameters:
|
||
|
* buffer buffer we need to read long int from
|
||
|
*
|
||
|
* Global variables used:
|
||
|
*/
|
||
|
static long
|
||
|
sgetl(register char *buffer)
|
||
|
{
|
||
|
register long w = 0;
|
||
|
register int i = BITSPERBYTE * AR_PORT_WORD;
|
||
|
|
||
|
while ((i -= BITSPERBYTE) >= 0) {
|
||
|
w |= (long) ((unsigned char) *buffer++) << i;
|
||
|
}
|
||
|
return w;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* read_member_header(header, fd, filename)
|
||
|
*
|
||
|
* reads the member header for the 4.1.x and SVr4 archives.
|
||
|
*
|
||
|
* Return value:
|
||
|
* fails if read error or member
|
||
|
* header is not the right format
|
||
|
* Parameters:
|
||
|
* header There's one before each archive member
|
||
|
* fd file descriptor for the archive file.
|
||
|
*
|
||
|
* Global variables used:
|
||
|
*/
|
||
|
int
|
||
|
read_member_header(Ar_port *header, FILE *fd, char* filename)
|
||
|
{
|
||
|
int num = fread((char *) header, sizeof (Ar_port), 1, fd);
|
||
|
if (num != 1 && feof(fd)) {
|
||
|
/* There is no member header - empty archive */
|
||
|
return -1;
|
||
|
}
|
||
|
if ((num != 1) ||
|
||
|
!IS_EQUALN(
|
||
|
AR_PORT_END_MAGIC,
|
||
|
header->ar_fmag,
|
||
|
sizeof (header->ar_fmag)
|
||
|
)
|
||
|
) {
|
||
|
fatal(
|
||
|
gettext("Read error in archive `%s': invalid archive file member header at 0x%x"),
|
||
|
filename,
|
||
|
ftell(fd)
|
||
|
);
|
||
|
}
|
||
|
return succeeded;
|
||
|
}
|
||
|
|