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