#include <alloca.h>
#include <ar.h>
#include <errno.h>
#include <fcntl.h>
#include <libintl.h>
#include <mk/defs.h>
#include <mksh/misc.h>
struct ranlib {
union {
off_t ran_strx;
char *ran_name;
} ran_un;
off_t ran_off;
};
#include <unistd.h>
#ifndef S5EMUL
#undef BITSPERBYTE
#define BITSPERBYTE 8
#endif
#define AR_5_MAGIC "<ar>"
#define AR_5_MAGIC_LENGTH 4
#define AR_PORT_MAGIC "!<arch>\n"
#define AR_PORT_MAGIC_LENGTH 8
#define AR_PORT_END_MAGIC "`\n"
#define AR_PORT_WORD 4
typedef struct {
char ar_magic[AR_5_MAGIC_LENGTH];
char ar_name[16];
char ar_date[AR_PORT_WORD];
char ar_syms[AR_PORT_WORD];
} Arh_5;
typedef struct {
char sym_name[8];
char sym_ptr[AR_PORT_WORD];
} Ars_5;
typedef struct {
char arf_name[16];
char arf_date[AR_PORT_WORD];
char arf_uid[AR_PORT_WORD];
char arf_gid[AR_PORT_WORD];
char arf_mode[AR_PORT_WORD];
char arf_size[AR_PORT_WORD];
} Arf_5;
typedef struct {
char ar_name[16];
char ar_date[12];
char ar_uid[6];
char ar_gid[6];
char ar_mode[8];
char ar_size[10];
char ar_fmag[2];
} Ar_port;
enum ar_type {
AR_5,
AR_PORT
};
typedef unsigned int ar_port_word;
typedef struct {
FILE *fd;
enum ar_type type;
long first_ar_mem;
long sym_begin;
long num_symbols;
long sym_size;
Arh_5 arh_5;
Ars_5 ars_5;
Arf_5 arf_5;
Ar_port ar_port;
} Ar;
extern timestruc_t& read_archive(Name target);
static Boolean open_archive(char *filename, Ar *arp);
static void close_archive(Ar *arp);
static Boolean read_archive_dir(Ar *arp, Name library, char **long_names_table);
static void translate_entry(Ar *arp, Name target, Property member, char **long_names_table);
static long sgetl(char *);
int read_member_header (Ar_port *header, FILE *fd, char* filename);
int process_long_names_member (Ar *arp, char **long_names_table, char *filename);
timestruc_t&
read_archive(Name target)
{
Property member;
wchar_t *slash;
String_rec true_member_name;
wchar_t buffer[STRING_BUFFER_LENGTH];
Name true_member = NULL;
Ar ar;
char *long_names_table = NULL;
member = get_prop(target->prop, member_prop);
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;
}
static Boolean
open_archive(char *filename, 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;
int ret = read_member_header(&arp->ar_port, arp->fd, filename);
if (ret == failed) {
return failed;
} else if(ret == -1) {
arp->sym_size = arp->num_symbols = arp->sym_begin = 0L;
arp->first_ar_mem = ftell(arp->fd);
return succeeded;
}
arp->sym_size = arp->num_symbols = arp->sym_begin = 0L;
arp->first_ar_mem = ftell(arp->fd) - (long) sizeof (Ar_port);
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);
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);
return failed;
}
static void
close_archive(Ar *arp)
{
if (arp->fd != NULL) {
(void) fclose(arp->fd);
}
}
static Boolean
read_archive_dir(Ar *arp, Name library, char **long_names_table)
{
wchar_t *name_string;
wchar_t *member_string;
long len;
wchar_t *p;
char *q;
Name name;
Property member;
long ptr;
long date;
int offset;
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;
}
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);
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 (arp->ar_port.ar_name[0] == '/') {
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);
}
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;
}
read_error:
fatal(gettext("Read error in archive `%s': %s"),
library->string_mb,
errmsg(errno));
}
int
process_long_names_member(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) {
return succeeded;
}
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);
if (fread(*long_names_table, table_size, 1, arp->fd) != 1) {
return failed;
}
arp->first_ar_mem = ftell(arp->fd);
}
return succeeded;
}
static void
translate_entry(Ar *arp, Name target, Property member, char **long_names_table)
{
int len;
int i;
wchar_t *member_string;
ar_port_word *offs;
int strtablen;
char *syms;
char *csym;
ar_port_word *offend;
int date;
wchar_t *ap;
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 (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);
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);
}
}
static long
sgetl(char *buffer)
{
long w = 0;
int i = BITSPERBYTE * AR_PORT_WORD;
while ((i -= BITSPERBYTE) >= 0) {
w |= (long) ((unsigned char) *buffer++) << i;
}
return w;
}
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)) {
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;
}