 /* $Header:assyms.c 12.0$ */
 /* $ACIS:assyms.c 12.0$ */
 /* $Source: /ibm/acis/usr/src/bin/as_ca/RCS/assyms.c,v $ */
#ifndef lint
static char rcsid[] = "$Header:assyms.c 12.0$";
#endif
/* $Source: /ibm/acis/usr/src/bin/as_ca/RCS/assyms.c,v $ */
/*
 *	Copyright (c) 1982 Regents of the University of California
 */
#ifndef lint
static char sccsid[] = "@(#)assyms.c 4.14 8/11/83";
#endif not lint

#include <stdio.h>
#include <ctype.h>
#include "as.h"
#include "asscan.h"
#include "assyms.h"

#if DEBUG
void printstab(struct symtab *stpt, int i);	/* declaration to help dbx */
#endif

/*
 *	Managers for chunks of symbols allocated from calloc()
 *	We maintain a linked list of such chunks.
 *
 */
struct	allocbox	*allochead;	/*head of chunk list*/
struct	allocbox	*alloctail;	/*tail*/
struct	allocbox	*newbox;	/*for creating a new chunk*/
struct	symtab		*nextsym;	/*next symbol free*/
int			symsleft;	/*slots left in current chunk*/

struct	symtab		**symptrs;
struct	symtab		**symdelim[NLOC + NLOC +1];
struct	symtab		**symptrub;
/*
 *	Managers for the dynamically extendable hash table
 */
struct	hashdallop	*htab;

Iptr	*itab[NINST];	/*maps opcodes to instructions*/
/*
 *	Counts what went into the symbol table, so that the
 *	size of the symbol table can be computed.
 */
int	nsyms;		/* total number in the symbol table */
int	njxxx;		/* number of jxxx entrys */
int	nforgotten;	/* number of symbols erroneously entered */
int	nlabels;	/* number of label entries */

/*
 *	Managers of the symbol literal storage.
 */
struct	strpool		*strplhead = 0;

symtabinit()
{
	allochead = 0;
	alloctail = 0;
	nextsym = 0;
	symsleft = 0;
	strpoolalloc();		/* get the first strpool storage area */
	htab = 0;
	htaballoc();		/* get the first part of the hash table */
}

/*
 *	Install all known instructions in the symbol table
 */
syminstall()
{
	register	Iptr	ip;
	register	struct	symtab	**hp;
	register	char	*p1, *p2;
	register	int	i;

	for (i = 0; i < NINST; i++)
		itab[i] = (Iptr*)BADPOINT;

	for (ip = (Iptr)instab; FETCHNAME(ip)[0]; ip++) {
		p1 = FETCHNAME(ip);
		p2 = yytext;
		while (*p2++ = *p1++);
		hp = lookup(0);		/* 0 => don't install this*/
		if (*hp==NULL) {
			*hp = (struct symtab *)ip;
			if (   (ip->s_tag!=INSTn)
			    && (ip->s_tag!=INST0)
			    && (ip->s_tag!=0))
				continue; /* was pseudo-op */
			if (itab[ip->i_eopcode] == (Iptr*)BADPOINT){
				itab[ip->i_eopcode] =
					(Iptr*)ClearCalloc(256, sizeof(Iptr));
				for (i = 0; i < 256; i++)
					itab[ip->i_eopcode][i] =
						(Iptr)BADPOINT;
			}
			itab[ip->i_eopcode][ip->i_popcode] = ip;
		}
	}
}	/*end of syminstall*/

#define ISLABEL(sp) \
	(   (!savelabels) \
	 && (sp->s_tag == LABELID) \
	 && (STRPLACE(sp) & STR_CORE) \
	 && (FETCHNAME(sp)[0] == 'L'))
/*
 *	Assign final values to symbols,
 *	and overwrite the index field with its relative position in
 *	the symbol table we give to the loader.
 */
extern struct exec hdr;

freezesymtab()
{
	register	struct	symtab	*sp;
				long	bs;
	register	int	relpos = 0;
	register	struct	symtab		*ubsp;
	register	struct	allocbox	*allocwalk;

	DECLITERATE(allocwalk, sp, ubsp)
	{
		if (sp->s_tag >= IGNOREBOUND)
			continue; 		/*totally ignore jxxx entries */
		/*
		 *	Ignore stabs, but give them a symbol table index
		 */
#if DEBUG
	if (debug) printstab(sp, 3);
#endif
		if (sp->s_type & STABFLAG)
			goto assignindex;
		if ((sp->s_type&XTYPE)==XUNDEF)
			sp->s_type = XXTRN+XUNDEF;
		else if ((sp->s_type&XTYPE)==XDATA)
			sp->s_value += usedot[sp->s_segno].e_xvalue;
		else if ((sp->s_type&XTYPE)==XTEXT)
			sp->s_value += usedot[sp->s_segno].e_xvalue;
		else if ((sp->s_type&XTYPE)==XBSS) {
			/* Force alignment of bss objects on 2- or 4-byte
			 * boundaries if their length implies a requirement.
			 */
			static int rounding[] = {FW,0,HW,0};
			bs = sp->s_value; 
			hdr.a_bss = round(hdr.a_bss, rounding[bs&FW]); 
			sp->s_value = hdr.a_bss + datbase; 
			hdr.a_bss += bs; 
		}
#if DEBUG
	if (debug) printstab(sp, 2); 
#endif
	   assignindex:
		if (!ISLABEL(sp))
			sp->s_index = relpos++;
	}
}

/*
 *	For all of the stabs that had their final value undefined during pass 1
 *	and during pass 2 assign a final value.
 *	We have already given stab entrys a initial approximation
 *	when we constsructed the sorted symbol table.
 *	Iteration order doesn't matter.
 */

stabfix()
{
	register struct symtab *sp, **cosp;
	register struct symtab *p;
	
	SYMITERATE(cosp, sp){
		if(sp->s_ptype && (sp->s_type & STABFLAG)) {	
			p = sp->s_dest;	
/* 
 * STABFLOATING indicates that the offset has been saved in s_desc, s_other
 */
			if(sp->s_tag == STABFLOATING) {
			  sp->s_value = ( ( ((unsigned char) sp->s_other) << 16)  					| ( (unsigned short) sp->s_desc )  );
			  sp->s_value = sp->s_value + p->s_value;
			}
			else sp->s_value = p->s_value;
			sp->s_index = p->s_index; 
			sp->s_type = p->s_type;

              
		}
	}
}

char *Calloc(number, size)
	int	number, size;
{
	register	char *newstuff;
	register	int   nsize;
	char	*sbrk();
	nsize = round(number*size, ALLOCBOUND);
	newstuff = sbrk(nsize);
	if ((int)newstuff == -1){
		yyerror("Ran out of Memory");
		delexit();
	}
	if (BOUNDNOTOK(newstuff))
		panic("Calloc: allocation not on proper boundary.\n");
	return(newstuff);
}

char *ClearCalloc(number, size)
	int	number, size;
{
	register	char	*newstuff;		/* r11 */
	register	int	length = number * size;	/* r10 */
#ifdef lint
	length = length;
#endif lint
	newstuff = Calloc(number, size);
#ifdef vax
	asm("movc5 $0, (r0), $0, r10, (r11)");
#else
	bzero (newstuff, length);
#endif
	return(newstuff);
}

struct symtab *symalloc()
{
	if (symsleft == 0){
		newbox = (struct allocbox *)ClearCalloc(1,ALLOCQTY);
		symsleft = SYMDALLOP;
		nextsym = &newbox->symslots[0];
		if (alloctail == 0){
			allochead = alloctail = newbox;
		} else {
			alloctail->nextalloc = newbox;
			alloctail = newbox;
		}
	}
	--symsleft;
	++nsyms;
	return(nextsym++);
}

strpoolalloc()
{
	register	struct	strpool	*new;

	new = (struct strpool *)Calloc(1, sizeof (struct strpool));
	new->str_nalloc = 0;
	new->str_next = strplhead;
	strplhead = new;
}

symcmp(Pptr, Qptr)
	struct symtab **Pptr, **Qptr;
{
	register struct symtab *p = *Pptr;
	register struct symtab *q = *Qptr;
	int i;
	if ((i = p->s_segno - q->s_segno))
		return(i);
	if ((i = p->s_value - q->s_value))
		return(i);
	/*
	 *	Force jxxx entries to virtually preceed labels defined
	 *	to follow the jxxxx instruction, so that bumping the
	 *	jxxx instruction correctly fixes up the following labels
	 */
	if (p->s_tag >= IGNOREBOUND)	/*p points to a jxxx*/
		return(-1);		
	if (q->s_tag >= IGNOREBOUND)
		return(1);
	/*
	 *	both are now just plain labels; the relative order doesn't
	 *	matter.  Both can't be jxxxes, as they would have different
	 *	values.
	 */
	return(0);			
}	/*end of symcmp*/

/*
 *	We construct the auxiliary table of pointers, symptrs and
 *	symdelim
 *	We also assign preliminary values to stab entries that did not yet
 *	have an absolute value (because they initially referred to
 *	forward references). We don't worry about .stabds, as they
 *	already have an estimated final value
 */

sortsymtab()
{
	register	struct	symtab	*sp;
	register	struct	symtab	**cowalk;
	register	struct	allocbox	*allocwalk;
			struct	symtab	*ubsp;
				int	segno;
				int	slotno;
				int	symsin;	/*number put into symptrs*/

	/*
	 * We need the segment number for pass 2 and we need a signed variable
	 * for use in jxxxfix, so move value from index to segno.
	 */
	DECLITERATE(allocwalk, sp, ubsp) {
		sp->s_segno = sp->s_index;
		sp->s_index = 0;  /* for jxxxfix() */
	}

	symptrs =  (struct symtab **)Calloc(nsyms + 2, sizeof *symptrs);
	/*
	 *	Allocate one word at the beginning of the symptr array
	 *	so that backwards scans through the symptr array will
	 *	work correctly while scanning through the zeroth segment
	 */
	*symptrs++ = 0;
	cowalk = symptrs;
	symsin = 0;
	DECLITERATE(allocwalk, sp, ubsp) {
		if (sp->s_ptype && (sp->s_type &STABFLAG)){
			sp->s_value = sp->s_dest->s_value;
			sp->s_segno = sp->s_dest->s_segno;
		}
		if (symsin >= nsyms)
			yyerror("INTERNAL ERROR: overfilled symbol table indirection table");
		*cowalk++ = sp;
		symsin++;
	}
	if (symsin != nsyms)
		yyerror("INTERNAL ERROR: installed %d syms, should have installed %d",
			symsin, nsyms);
	symptrub = &symptrs[nsyms ];
	qsort(symptrs, nsyms, sizeof *symptrs, symcmp);
	symdelim[0] = symptrs;
	for (cowalk = symptrs, sp = *cowalk, segno = 0, slotno = 1;
	     segno < NLOC + NLOC;
	     segno++, slotno++){
		for (; sp && sp->s_segno == segno; sp = *++cowalk);
		symdelim[slotno] = cowalk;	/*forms the ub delimeter*/
	}
}	/*end of sortsymtab*/

#ifdef DEBUG
dumpsymtab()
{
	register	int	segno;
	register	struct symtab *sp, **cosp, *ub;
	char		*tagstring();
	char		*cptr;

	printf("Symbol Table dump:\n");
	for (segno = 0; segno < NLOC + NLOC; segno++){
		printf("Segment number: %d\n", segno);
		SEGITERATE(segno, 0, 0, cosp, sp, ub, ++){
			cptr = (struct strdesc *)(sp)->s_name == NULL ?
				"" : FETCHNAME(sp) ;
			printf("\tSeg: %d \"%s\" value: %d index: %d tag %s\n",
				segno, cptr, 
				sp->s_value, sp->s_index,
				tagstring(sp->s_tag));
			printf("\t\ttype: %d jxbump %d jxfear: %d\n",
				sp->s_type, sp->s_jxbump, sp->s_jxfear);
		}
		printf("\n\n");
	}
}

static	char tagbuff[4];

char *tagstring(tag)
	unsigned	char	tag;
{
	switch(tag){
		case JXACTIVE:		return("active");
		case JXNOTYET:		return("notyet");
		case JXALIGN:		return("align");
		case JXQUESTIONABLE:	return("jxquestionable");
		case JXINACTIVE:	return("inactive");
		case JXTUNNEL:		return("tunnel");
		case OBSOLETE:		return("obsolete");
		case IGNOREBOUND:	return("ignorebound");
		case STABFLOATING:	return("stabfloating");
		case STABFIXED:		return("stabfixed");
		case LABELID:		return("labelid");
		case OKTOBUMP:		return("oktobump");
		case ISET:		return("iset");
		case ILSYM:		return("ilsym");
		default:		sprintf(tagbuff,"%d", tag);
					return(tagbuff);
	}
}
#endif DEBUG

htaballoc()
{
	register	struct	hashdallop	*new;
	new = (struct hashdallop *)ClearCalloc(1, sizeof (struct hashdallop));
	if (htab == 0)
		htab = new;
	else {		/* add AFTER the 1st slot */
		new->h_next = htab->h_next;
		htab->h_next = new;
	}
}

#define 	HASHCLOGGED	(NHASH / 2)

/*
 *	Lookup a symbol stored in extern yytext.
 *	All strings passed in via extern yytext had better have
 *	a trailing null.  Strings are placed in yytext for hashing by
 *	syminstall() and by yylex();
 *
 *	We take pains to avoid function calls; this functdion
 *	is called quite frequently, and the calls overhead
 *	in the vax contributes significantly to the overall
 *	execution speed of as.
 */
struct symtab **lookup(instflg)
	int	instflg;		/* 0: don't install */
{
	static	 int		initialprobe;
	register struct	symtab 	**hp;
	register char 		*from;
	register char		*to;
	register	int	len;
	register	int	nprobes;
	static	struct	hashdallop *hdallop;
	static	struct	symtab	**emptyslot;
	static 	struct	hashdallop *emptyhd;
	static	struct	symtab	**hp_ub;

	emptyslot = 0;
	for (nprobes = 0, from = yytext;
	     *from;
	     nprobes <<= 2, nprobes += *from++)
		continue;
	nprobes += from[-1] << 5;
	nprobes %= NHASH;
	if (nprobes < 0)
		nprobes += NHASH;

	initialprobe = nprobes;
	for (hdallop = htab; hdallop != 0; hdallop = hdallop->h_next){
		for (hp = &(hdallop->h_htab[initialprobe]),
				nprobes = 1,
				hp_ub = &(hdallop->h_htab[NHASH]);
		     (*hp) && (nprobes < NHASH);
				hp += nprobes,
				hp -= (hp >= hp_ub) ? NHASH:0,
				nprobes += 2)
		{
			from = yytext;
			to = FETCHNAME(*hp);
			while (*from && *to)
				if (*from++ != *to++)
					goto nextprobe;
			if (*to == *from)	/*assert both are == 0*/
				return(hp);
		nextprobe: ;
		}
		if (*hp == 0 && emptyslot == 0 &&
		    hdallop->h_nused < HASHCLOGGED) {
			emptyslot = hp;
			emptyhd = hdallop;
		}
	}
	if (emptyslot == 0) {
		htaballoc();
		hdallop = htab->h_next;		/* aren't we smart! */
		hp = &hdallop->h_htab[initialprobe];
	} else {
		hdallop = emptyhd;
		hp = emptyslot;
	}
	if (instflg) {
		*hp = symalloc();
		hdallop->h_nused++;
		for (from = yytext, len = 0; *from++; len++)
			continue;
		(*hp)->s_name = (char *)savestr(yytext, len + 1, STR_BOTH);
	}
	return(hp);
}	/*end of lookup*/
/*
 *	save a string str with len in the places indicated by place
 */
struct strdesc *savestr(str, len, place)
	char	*str;
	int	len;
	int	place;
{
	reg	struct	strdesc	*res;
		int	tlen;
	/*
	 *	Compute the total length of the record to live in core
	 */
/*	tlen = sizeof(struct strdesc) - sizeof(res->sd_string);		*/
	if (place & STR_CORE)
/*		tlen += len;	*/
		tlen = round(SD_STRUCTLEN + len, SD_STRUCTBOUND);
	else
		tlen = round(SD_STRUCTLEN, SD_STRUCTBOUND);
	/*
	 *	See if there is enough space for the record,
	 *	and allocate the record.
	 */
	if (tlen >= (STRPOOLDALLOP - strplhead->str_nalloc))
		strpoolalloc();
	res = (struct strdesc *)(strplhead->str_names + strplhead->str_nalloc);
	/*
	 *	Save the string information that is always present
	 */
	res->sd_stroff = strfilepos;
	res->sd_strlen = len;
	res->sd_place = place;
	/*
	 *	Now, save the string itself.  If str is null, then
	 *	the characters have already been dumped to the file
	 */
	if ((place & STR_CORE) && str)
		movestr(res[0].sd_string, str, len);
	if (place & STR_FILE){
		if (str){
			fwrite(str, 1, len, strfile);
		}
		strfilepos += len;
	}
	/*
	 *	Adjust the in core string pool size
	 */
	strplhead->str_nalloc += tlen;
	return(res);
}
/*
 *	The relocation information is saved internally in an array of
 *	lists of relocation buffers.  The relocation buffers are
 *	exactly the same size as a token buffer; if we use VM for the
 *	temporary file we reclaim this storage, otherwise we create
 *	them by mallocing.
 */
#define	RELBUFLG	TOKBUFLG
#define	NRELOC		((TOKBUFLG - \
			  (sizeof (int) + sizeof (struct relbufdesc *)) \
			) / (sizeof (struct relocation_info)))

struct	relbufdesc{
	int	rel_count;
	struct	relbufdesc	*rel_next;
	struct	relocation_info	rel_reloc[NRELOC];
};
extern	struct	relbufdesc	*tok_free;
#define	rel_free tok_free
static	struct	relbufdesc	*rel_temp;
struct	relocation_info r_can_1PC;
struct	relocation_info	r_can_0PC;

initoutrel()
{
	r_can_0PC.r_address = 0;
	r_can_0PC.r_symbolnum = 0;
	r_can_0PC.r_pcrel = 0;
	r_can_0PC.r_length = 0;
	r_can_0PC.r_extern = 0;

	r_can_1PC = r_can_0PC;
	r_can_1PC.r_pcrel = 1;
}

outrel(xp, reloc_how)
	register	struct	exp	*xp;
	int		reloc_how;	/* TYPB..TYPH + (possibly)RELOC_PCREL */
{
	struct		relocation_info	reloc;
	register	int	x_type_mask;	
			int	pcrel;
			char	*cp;

	x_type_mask = xp->e_xtype & ~XFORW;
	pcrel = reloc_how & RELOC_PCREL;
	reloc_how &= ~RELOC_PCREL;
	
	if (bitoff&07)
		yyerror("Padding error");
	if (x_type_mask == XUNDEF)
		yyerror("Undefined reference");

	if ( (x_type_mask != XABS) || pcrel ) {
		if (ty_NORELOC[reloc_how])
			yyerror("Illegal Relocation of floating or large int number.");
		reloc = pcrel ? r_can_1PC : r_can_0PC;
		reloc.r_address = dotp->e_xvalue -
		    ( (dotp < &usedot[NLOC] || readonlydata) ? 0 : datbase );
		/* For 3 byte data, back up loc to include op code */
		if (reloc_how == TYPZ) reloc.r_address -= 1;
		reloc.r_length = ty_nlg[reloc_how];
		switch(x_type_mask){
			case XXTRN | XUNDEF:
				reloc.r_symbolnum = xp->e_xname->s_index;
				reloc.r_extern = 1;
				break;
			default:
				if (readonlydata && (x_type_mask&~XXTRN) == XDATA)
					x_type_mask = XTEXT | (x_type_mask&XXTRN);
				reloc.r_symbolnum = x_type_mask;
				break;
		}
		if ( (relfil == 0) || (relfil->rel_count >= NRELOC) ){
			if (rel_free){
				rel_temp = rel_free;
				rel_free = rel_temp->rel_next;
			} else {
				rel_temp = (struct relbufdesc *)
					Calloc(1,sizeof (struct relbufdesc));
			}
			rel_temp->rel_count = 0;
			rel_temp->rel_next = relfil;
			relfil = rusefile[dotp - &usedot[0]] = rel_temp;
		}
		relfil->rel_reloc[relfil->rel_count++] = reloc;
	}
	/*
	 *	write the unrelocated value to the text file
	 */
	dotp->e_xvalue += ty_nbyte[reloc_how];
/*	if (pcrel)
		xp->e_xvalue -= dotp->e_xvalue;   *//* let loader do this */
	switch(reloc_how){
	case TYPU:
	case TYPF:	/* F, D, and H are non-relocatable in */
	case TYPD:	/*    ty_NORELOC[]		      */
	case TYPH:
		bignumwrite(xp->e_number, reloc_how);
		break;
	default:
		yywarning("BYTE or SHORT relocatable data may cause ld error");
		/* FALLTHROUGH */
	case TYPV:	/* split rloacatable address (first two bytes) */
	case TYPL:	/* long word */
	case TYPZ:	/* three byte (BI format) */
	
#ifndef	ONVAX
		cp = (char *)&(xp->e_xvalue) + sizeof(xp->e_xvalue) -
						ty_nbyte[reloc_how];
		bwrite( cp, ty_nbyte[reloc_how], txtfil); 
#else
	       bwriteflip((char *)&(xp->e_xvalue), ty_nbyte[reloc_how], txtfil);
#endif
		break;

	}
}
/*
 *	Flush out all of the relocation information.
 *	Note that the individual lists of buffers are in
 *	reverse order, so we must reverse them
 */
off_t closeoutrel(relocfile)
	BFILE	*relocfile;
{
	int	locindex;
	u_long	Closeoutrel();

	trsize = 0;
	for (locindex = 0; locindex < NLOC; locindex++){
		trsize += Closeoutrel(rusefile[locindex], relocfile);
	}
	drsize = 0;
	for (locindex = 0; locindex < NLOC; locindex++){
		drsize += Closeoutrel(rusefile[NLOC + locindex], relocfile);
	}
	return(trsize + drsize);
}

u_long Closeoutrel(relfil, relocfile)
	struct	relbufdesc	*relfil;
	BFILE	*relocfile;
{
	u_long	tail;
	if (relfil == 0)
		return(0L);
	tail = Closeoutrel(relfil->rel_next, relocfile);
	bwrite((char *)&relfil->rel_reloc[0],
		relfil->rel_count * sizeof (struct relocation_info),
		relocfile);
	return(tail + relfil->rel_count * sizeof (struct relocation_info));
}

#define NOUTSYMS (nsyms - njxxx - nforgotten - (savelabels ? 0 : nlabels))
int sizesymtab()
{
	return (sizeof (struct nlist) * NOUTSYMS);
}
/*
 *	Write out n symbols to file f, beginning at p
 *	ignoring symbols that are obsolete, jxxx instructions, and
 *	possibly, labels
 */
int symwrite(symfile)
	BFILE *symfile;
{
		int	symsout;		/*those actually written*/
		int	symsdesired = NOUTSYMS;
	reg	struct	symtab *sp, *ub;
		char	*name;			/* temp to save the name */
		int	totalstr;
	/*
	 *	We use sp->s_index to hold the length of the
	 *	name; it isn't used for anything else
	 */
	register	struct	allocbox	*allocwalk;

	symsout = 0;
	totalstr = sizeof(totalstr);
	DECLITERATE(allocwalk, sp, ub) {
		if (sp->s_tag >= IGNOREBOUND) 
			continue;
		if (ISLABEL(sp))
			continue;
		symsout++;
		name = sp->s_name;		/* save pointer */
		/*
		 *	the length of the symbol table string
		 *	always includes the trailing null;
		 *	blast the pointer to its a.out value.
		 */
		if (sp->s_name && (sp->s_index = STRLEN(sp))){
			sp->s_nmx = totalstr;
			totalstr += sp->s_index;
		} else {
			sp->s_nmx = 0;
		}
		if (sp->s_ptype != 0)
			sp->s_type = sp->s_ptype;
		else
			sp->s_type = (sp->s_type & (~XFORW));
		if (readonlydata && (sp->s_type&~N_EXT) == N_DATA)
			sp->s_type = N_TEXT | (sp->s_type & N_EXT);
		bwrite((char *)&sp->s_nm, sizeof (struct nlist), symfile);
		sp->s_name = name;		/* restore pointer */
	}
	if (symsout != symsdesired)
		yyerror("INTERNAL ERROR: Wrote %d symbols, wanted to write %d symbols\n",
			symsout, symsdesired);
	/*
	 *	Construct the string pool from the symbols that were written,
	 *	possibly fetching from the string file if the string
	 *	is not core resident.
	 */
	bwrite(&totalstr, sizeof(totalstr), symfile);
	symsout = 0;
	DECLITERATE(allocwalk, sp, ub) {
		if (sp->s_tag >= IGNOREBOUND) 
			continue;
		if (ISLABEL(sp))
			continue;
		symsout++;
		if (sp->s_name && STRLEN(sp) > 0){
		 if (STRPLACE(sp) & STR_CORE){
			bwrite(FETCHNAME(sp), STRLEN(sp), symfile);
		 } else if (STRPLACE(sp) & STR_FILE){
			char	rbuf[2048];
			int	left, nread;
			fseek(strfile, STROFF(sp), 0);
			for (left = STRLEN(sp); left > 0; left -= nread){
				nread = fread(rbuf, sizeof(char),
					min(sizeof(rbuf), left), strfile);
				if (nread == 0)
					break;
				bwrite(rbuf, nread, symfile);
			}
		 }
		}
	}
	if (symsout != symsdesired)
		yyerror("INTERNAL ERROR: Wrote %d strings, wanted %d\n",
			symsout, symsdesired);
}

