#ifndef lint
static char rcsid[] = "$Header$";
#endif

#include "../../h/out.h"
#include "const.h"
#include "defs.h"
#include "memory.h"
#include "orig.h"
#include "scan.h"

extern bool	incore;
extern int	flagword;

static		adjust_names();
static		handle_relos();
static		put_locals();
static		compute_origins();

/*
 * We know all there is to know about the current module.
 * Now we relocate the values in the emitted bytes and write
 * those to the final output file. Then we compute the relative origins
 * for the next module.
 */
finish()
{
	struct outhead	*head;
	struct outsect	*sects;
	struct outname	*names;
	char		*chars;

	get_modul();
	head = (struct outhead *)modulptr(IND_HEAD);
	sects = (struct outsect *)modulptr(IND_SECT(*head));
	names = (struct outname *)modulptr(IND_NAME(*head));
	chars = (char *)modulptr(IND_CHAR(*head));
	adjust_names(names, head, chars);
	handle_relos(head, sects, names);
	if (!incore && !(flagword & SFLAG)) {
		put_locals(names, head->oh_nname, sects);
#ifdef SYMDBUG
		put_dbug(OFF_DBUG(*head));
#endif SYMDBUG
	}
	compute_origins(sects, head->oh_nsect);
	skip_modul(head);
}

/*
 * Adjust all local names for the move into core.
 */
static
adjust_names(name, head, chars)
	register struct outname	*name;
	struct outhead		*head;
	register char		*chars;
{
	register int		cnt;
	register ind_t		charoff;

	cnt = head->oh_nname;
	charoff = OFF_CHAR(*head);
	while (cnt--) {
		if (name->on_foff != (long)0)
			name->on_mptr = chars + name->on_foff - charoff;
		name++;
	}
}

/*
 * If all sections are in core, we can access them randomly, so we need only
 * scan the relocation table once. Otherwise we must for each section scan
 * the relocation table again, because the relocation entries of one section
 * need not be consecutive.
 */
static
handle_relos(head, sects, names)
	struct outhead		*head;
	struct outsect		*sects;
	struct outname		*names;
{
	register struct outrelo	*relo;
	register int		sectindex;
	register int		nrelo;
	register char		*emit;
	extern char		*getemit();
	extern struct outrelo	*nextrelo();

	if (incore) {
		nrelo = head->oh_nrelo; sectindex = -1;
		startrelo(head); relo = nextrelo();
		while (nrelo--) {
			if (sectindex != relo->or_sect - S_MIN) {
				sectindex = relo->or_sect - S_MIN;
				emit = getemit(head, sects, sectindex);
			}
			relocate(head, emit, names, relo, sects);
			relo++;
		}
	} else {
		for (sectindex = 0; sectindex < head->oh_nsect; sectindex++) {
			emit = getemit(head, sects, sectindex);
			nrelo = head->oh_nrelo; startrelo(head);
			while (nrelo--) {
				relo = nextrelo();
				if (relo->or_sect - S_MIN == sectindex) {
					relocate(head,emit,names,relo,sects);
					/*
					 * Write out the (probably changed)
					 * relocation information.
					 */
					if (flagword & RFLAG)
						wrt_relo(relo);
				}
			}
			wrt_emit(emit, sectindex, sects[sectindex].os_flen);
			/*
			 * XXX We should be able to free the emitted bytes.
			 */
		}
	}
}

/*
 * Write out the local names that must be saved.
 */
static
put_locals(name, nnames, sects)
	register struct outname	*name;
	register ushort		nnames;
	register struct outsect	*sects;
{
	while (nnames--) {
		if ((name->on_type & S_EXT) == 0 && mustsavelocal(name)) {
			namerelocate(name, sects);
			addbase(name);
			wrt_name(name);
		}
		name++;
	}
}

/*
 * Add all flen's and all (size - flen == zero)'s of preceding sections
 * with the same number.
 */
static
compute_origins(sect, nsect)
	register struct outsect	*sect;
	register ushort		nsect;
{
	extern struct orig	relorig[];
	register struct orig	*orig = relorig;

	while (nsect--) {
		orig->org_flen += sect->os_flen;
		orig->org_zero += sect->os_size - sect->os_flen;
		orig++; sect++;
	}
}
#ifdef SYMDBUG

/*
 * Write out what is after the string area. This is likely to be
 * debugging information.
 */
static
put_dbug(offdbug)
	long		offdbug;
{
	char		buf[512];
	register int	nbytes;
	register long	dbugsize;
	extern long	objectsize;
	extern long	position;

	dbugsize = objectsize - offdbug;
	seek(position + offdbug);
	while (dbugsize) {
		nbytes = dbugsize > 512 ? 512 : dbugsize;
		read_char(buf, (long)nbytes);
		wrt_dbug(buf, nbytes);
		dbugsize -= nbytes;
	}
}
#endif SYMDBUG