/*
 * 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.cc 1.28 06/12/12
 */

#pragma	ident	"@(#)ar.cc	1.28	06/12/12"

/*
 * This file contains modifications Copyright 2017 J. Schilling
 *
 * @(#)ar.cc	1.6 17/05/18 2017 J. Schilling
 */
#include <schily/mconfig.h>
#ifndef lint
static	UConst char sccsid[] =
	"@(#)ar.cc	1.6 17/05/18 2017 J. Schilling";
#endif

/*
 *	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 <avo/avo_alloca.h>		/* alloca() */
#ifdef	HAVE_AR_H
#include <ar.h>
#endif
#include <mk/defs.h>
#include <mksh/misc.h>		/* retmem_mb() */

#ifdef	HAVE_RANLIB_H
#include <ranlib.h>
#else
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 */
};
#endif

#include <ctype.h>		/* isspace */


/*
 * 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 !defined(SUN5_0) && !defined(linux) //XXX
	/* Read enough of the archive to distinguish between the formats */
	if (fread(mag_5, AR_5_MAGIC_LENGTH, 1, arp->fd) != 1) {
		return failed;
	}
	if (IS_EQUALN(mag_5, AR_5_MAGIC, AR_5_MAGIC_LENGTH)) {
		arp->type = AR_5;
		/* Must read in header to set necessary info */
		if (fseek(arp->fd, 0L, 0) != 0 ||
		    fread((char *) &arp->arh_5, sizeof (Arh_5), 1, arp->fd) !=
									1) {
			return failed;
		}
		arp->sym_begin = ftell(arp->fd);
		arp->num_symbols = sgetl(arp->arh_5.ar_syms);
		arp->first_ar_mem = arp->sym_begin +
					sizeof (Ars_5) * arp->num_symbols;
		arp->sym_size = 0L;
		return succeeded;
	}
	if (fseek(arp->fd, 0L, 0) != 0) {
		return failed;
	}
#endif
	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 "/        "
		 */
/*
#ifdef SUN5_0
		MBSTOWCS(wcs_buffer, NOCATGETS("/               "));
		if (IS_WEQUALN(arp->ar_port.ar_name, wcs_buffer, 16)) {
#else
		MBSTOWCS(wcs_buffer, NOCATGETS("__.SYMDEF       "));
		if (IS_WEQUALN(arp->ar_port.ar_name, wcs_buffer, 16)) {
#endif
 */
#if defined(SUN5_0) || defined(HP_UX) || defined(linux)
		if (IS_EQUALN(arp->ar_port.ar_name,
			      NOCATGETS("/               "),
			      16)) {
#else
		if (IS_EQUALN(arp->ar_port.ar_name,
			      NOCATGETS("__.SYMDEF       "),
			      16)) {
#endif
			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
#if defined(SUN5_0) || defined(linux) //XXX
read_archive_dir(register Ar *arp, Name library, char **long_names_table)
#else
read_archive_dir(register Ar *arp, Name library, char **)
#endif
{
	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;

#if defined(SUN5_0) || defined(linux) //XXX
	long			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;
	}
#endif
	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 defined(SUN5_0) || defined(linux) //XXX
		    /* 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;
		    }
#else
		    q = arp->ar_port.ar_name;	
		    len = sizeof arp->ar_port.ar_name;
#endif
		    
		    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 */

	return (failed); /* fake return if compiler doesn't grok 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;
	long			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, 
		      NOCATGETS("//              "),
		      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 */
	long			date;
	register wchar_t	*ap;
	register char		*hp;
	int			maxs;
	long			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 defined(SUN5_0) || defined(linux) //XXX
		    /* 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;	
		    }
#else
		    hp = arp->ar_port.ar_name;	
#endif
				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;
}

