/*
 *    (c)Copyright 1992-1997 Obvious Implementations Corp.  Redistribution and
 *    use is allowed under the terms of the DICE-LICENSE FILE,
 *    DICE-LICENSE.TXT.
 */

/*
 *  FDTOLIB.C
 *
 *  FDTOLIB fdfile[s] [-h hdrfile] -o libname [-mr] [-mD] [-pprefix] -I incldir
 *			-prof -mc -mC -auto libname -AUTO libname
 *
 *  Generates a normal model or registerized modem library interface given
 *  any set of FD files.
 *
 *  -ms, -r, and -pr are ignored
 */

#ifdef AMIGA
#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/alib_protos.h>
#include <lib/profile.h>
#include <lib/version.h>
#else
#include <include/lib/profile.h>
#include <include/lib/version.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifndef AMIGA
#include <suplib/all.h>
#endif

#ifndef L_tmpnam
#define L_tmpnam    64
#endif

#define RF_SCRATCH  0x0303
#define RB_BP	    (8+6)
#define RF_BP	    (1 << RB_BP)

IDENT("fdtolib", ".5");
DCOPYRIGHT;

typedef unsigned short uword;
typedef struct List	List;
typedef struct Node	Node;

typedef struct LVONode {
    Node    lv_Node;
    short   lv_Offset;
} LVONode;

typedef struct RSNode {
    Node    rn_Node;
    short   rn_Args;	    /*	-1 specifies stack  */
    short   rn_Flag;	    /*	equiv/fd found	    */
    short   rn_Regs[32];    /*	transfer registers  */
} RSNode;

void	help(int);
void	ScanFD(FILE *, FILE *);
void	ScanRSTmp(FILE *);
void	GenerateFunction(FILE *, char *, char *, int);
int	AssembleFile(char *, char *);
void	JoinOutput(FILE *, char *);
char	*RegMaskToStr(uword, short *);
uword	RegsToMask(short *, short);
char	*RegToStr(short);
void	RegsCall(FILE *, char *, long, RSNode *, short *);
void	StackCall(FILE *, char *, long, short *, short);
short	PushMask(FILE *, uword);
short	PopMask(FILE *, uword);
void	AddLVOList(char *, int);
void	GenerateLVOList(FILE *);
void	GenerateAutoOpen(FILE *, char *);
void	DicePrefix(char *, char *, char *);

List	FDList; 	/*  list of FD files   */
List	RSList; 	/*  register spec list */
List	LVOList;
char	DccOptsBuf[1024];
char	*OutFile;
char	*HdrFile;
char	*FuncPrefix = "_";
char	Buf[256];
char	FuncName[256];
char	Prefix[64];
short	RegOpt;
short	Verbose;
short	Symbols = 1;
short	SmallData = 1;
short	SmallCode = 1;
short	ProfOpt;
short	AutoOpt;
char	*DataModel = "(A4)";
char	*CodeModel = "(PC)";
char	*SharedLibName;

int
main(int ac, char **av)
{
    int i;

    NewList(&FDList);
    NewList(&RSList);
    NewList(&LVOList);

    DicePrefix(Prefix, av[0], "");

#ifndef LATTICE
#ifndef unix
    expand_args(ac, av, &ac, &av);
#endif
#else
    setbuf(stdout, NULL);
#endif

    for (i = 1; i < ac; ++i) {
	char *ptr = av[i];

	if (*ptr != '-') {
	    Node *node = malloc(sizeof(Node));
	    node->ln_Name = ptr;
	    AddTail(&FDList, node);
	    continue;
	}
	ptr += 2;
	switch(ptr[-1]) {
	case 'a':
	    AutoOpt = 1;
	    SharedLibName = av[++i];
	    break;
	case 'A':
	    AutoOpt = 2;
	    SharedLibName = av[++i];
	    break;
	case 'p':
	    if (strcmp(ptr, "rof") == 0)
		ProfOpt = 1;
	    else if (strcmp(ptr, "r") == 0)     /*  ignore -pr  */
		;
	    else
		FuncPrefix = (*ptr) ? ptr : av[++i];
	    break;
	case 'o':
	    OutFile = (*ptr) ? ptr : av[++i];
	    break;
	case 'h':
	    HdrFile = (*ptr) ? ptr : av[++i];
	    break;
	case 'm':
	    if (*ptr == 'r' || *ptr == 'R')
		RegOpt = 1;
	    else if (*ptr == 'd') {
		SmallData = 1;
		DataModel = "(A4)";
	    } else if (*ptr == 'D') {
		SmallData = 0;
		DataModel = "";
	    } else if (*ptr == 'c') {
		SmallCode = 1;
		CodeModel = "(pc)";
	    } else if (*ptr == 'C') {
		SmallCode = 0;
		CodeModel = "";
	    } else if (*ptr == 's') {
		/*  ignore  */
	    }
	    break;
	case 'r':
	    /* ignore -r    */
	    break;
	case 'v':
	    ++Verbose;
	    break;
	case 'I':
	    sprintf(DccOptsBuf + strlen(DccOptsBuf), " -I%s", (*ptr) ? ptr : av[++i]);
	    break;

	case 'n':
	    Symbols = 0;
	    break;

	default:
	    help(1);
	}
    }
    if (OutFile == NULL || (RegOpt && HdrFile == NULL))
	help(ac != 1);

    if (RegOpt == 0 && HdrFile)
	puts("Warning: header file ignored (used only with -mr)");

    /*
     *	step 2, ask DCC to generate a register specification file
     */

    if (RegOpt) {
	FILE *fi;
	char rs_tmp[L_tmpnam];

	sprintf(Buf, "%sdcc -mRRX %s -a -o %s%s",
	    Prefix,
	    HdrFile,
	    tmpnam(rs_tmp),
	    DccOptsBuf
	);
	if(Symbols)strcat(Buf," -s -sym");
	puts(Buf);
#ifdef unix
    	system(Buf);
#else
	Execute(Buf, NULL, NULL);
#endif

	if ((fi = fopen(rs_tmp, "r")) == NULL) {
	    puts("Unable to generate register specification file");
	    help(20);
	}
	ScanRSTmp(fi);
	fclose(fi);
	remove(rs_tmp);
    }

    /*
     *	step 3
     */


    {
	FILE *fi;
	FILE *fo = fopen(OutFile, "w");
	Node *node;

	if (fo == NULL) {
	    printf("Error, Unable to create %s\n", OutFile);
	    exit(20);
	}
	while ((node = RemHead(&FDList)) != NULL) {
	    printf("generate %s", node->ln_Name);
	    fi = fopen(node->ln_Name, "r");
	    if (fi) {
		puts("");
		ScanFD(fi, fo);
		fclose(fi);
	    } else {
		puts(" (open failed)");
	    }
	}
    }
    {
#ifdef NOTDEF
	RSNode *rs;

	for (rs = RSList.lh_Head; rs->rn_Node.ln_Succ; rs = (RSNode *)rs->rn_Node.ln_Succ) {
	    if (rs->rn_Flag == 0)
		printf("Warning, no FD entry found for: %s\n", rs->rn_Node.ln_Name);
	}
#endif
    }
    return(0);
}

void
help(int code)
{
    puts(Ident);
    puts(DCopyright);
    puts("FDTOLIB files/wildcard [-h hdrfile] -o libname [-mr] [-mD]");
    puts("  -mr :   generate registered library from header & fd file");
    puts("  -mD :   large data model (else small data model)");
    exit(code);
}

void
ScanFD(fi, fo)
FILE *fi;
FILE *fo;
{
    char *base = NULL;
    long bias = -1;
    short end = 0;
    short public = 1;

    char *key;

    while (fgets(Buf, sizeof(Buf), fi)) {
	if (Buf[0] == '\n' || Buf[0] == '*')
	    continue;
	if (strncmp(Buf, "##", 2) != 0) {
	    if (bias < 0 || base == NULL) {
		printf("Error, No ##base/##bias before function: %s\n", Buf);
		continue;
	    }
	    if (public && AutoOpt != 2)
		GenerateFunction(fo, Buf, base, bias);
	    bias += 6;
	    continue;
	}
	if ((key = strtok(Buf + 2, " \t\n")) == NULL) {
	    printf("\tError, Illegal null directive\n");
	    continue;
	}
	if (stricmp(key, "base") == 0) {
	    if ((key = strtok(NULL, " \t\n")) != NULL) {
		if (base)
		    free(base);
		base = strdup(key);
	    } else {
		printf("\tError, Illegal ##base directive\n");
	    }
	    continue;
	}
	if (stricmp(key, "bias") == 0) {
	    if ((key = strtok(NULL, " \t\n")) != NULL) {
		char *dummy;

		bias = strtol(key, &dummy, 0);
		if (bias <= 0)
		    printf("\tError, Illegal ##bias: %ld\n", bias);
	    } else {
		printf("\tError, Illegal ##bias directive\n");
	    }
	    continue;
	}
	if (stricmp(key, "public") == 0) {
	    public = 1;
	    continue;
	}
	if (stricmp(key, "private") == 0) {
	    public = 0;
	    continue;
	}
	if (stricmp(key, "end") == 0) {
	    end = 1;
	    break;
	}
	printf("\tError, Unrecognized directive: %s\n", key);
    }
    if (bias < 0)
	puts("\tUnexpected EOF, no ##bias");
    if (base == NULL)
	puts("\tUnexpected EOF, no ##base");
    if (end == 0)
	puts("\tUnexpected EOF, no ##end directive");
    if (AutoOpt != 2)
	GenerateLVOList(fo);
    if (AutoOpt)
	GenerateAutoOpen(fo, base);
}

/*
 *  funcname(var,var,var)(reg,reg,reg)	    (or reg/reg)
 */

static short	FRegs[128];

void
GenerateFunction(fo, buf, base, bias)
FILE *fo;
char *buf;
char *base;
int bias;
{
    char tmpFile[L_tmpnam];
    char objFile[L_tmpnam+4];
    char *funcName;
    FILE *ft;
    short argCnt;
    short noArgs = 0;

    funcName = buf;
    while (*funcName && *funcName != '\t' && *funcName != ' ' && *funcName != '(')
	++funcName;
    if (*funcName == ' ' || *funcName == '\t') {
	while (*funcName && *funcName != '(')
	    *funcName++ = 0;
    }
    if (*funcName == '(') {
	*funcName++ = 0;
	if (*funcName == ')')
	    noArgs = 1;
    }
    while (*funcName && *funcName != ')')   /*  skip text args  */
	++funcName;
    while (*funcName && *funcName != '(')
	++funcName;
    if (noArgs == 0 && *funcName == 0) {
	printf("\tError in line: %s\n", buf);
	return;
    }

    /*
     *	get register description
     */

    if (*funcName)
	++funcName;
    for (argCnt = 0; *funcName && *funcName != '\n' && *funcName != ')'; ++argCnt) {
	switch(*funcName) {
	case 'd':
	case 'D':
	    FRegs[argCnt] = *++funcName - '0';
	    ++funcName;
	    break;
	case 'a':
	case 'A':
	    FRegs[argCnt] = *++funcName - '0' + 8;
	    ++funcName;
	    break;
	default:
	    printf("\tError in register spec: %s\n", funcName);
	    return;
	}
	if (*funcName == ',' || *funcName == '/')
	    ++funcName;
    }
    if (noArgs == 0 && *funcName != ')') {
	printf("\tError in register spec: %s\n", funcName);
	return;
    }

    /*
     *	generate
     */

    funcName = strdup(buf);
    {
	RSNode *rs = NULL;

	if (Verbose)
	    printf("    %-15s %d %d ", funcName, -bias, argCnt);

	if (RegOpt) {
	    for (rs = (RSNode *)RSList.lh_Head; rs->rn_Node.ln_Succ; rs = (RSNode *)rs->rn_Node.ln_Succ) {
		if (strcmp(rs->rn_Node.ln_Name + 1, funcName) == 0) {
		    rs->rn_Flag = 1;
		    break;
		}
	    }
	    if (rs->rn_Node.ln_Succ == NULL) {	/* (list tail)	*/
		if (Verbose)
		    puts("NO MATCH FOUND");
		return;
	    }

	    if (rs->rn_Args >= 0 && rs->rn_Args != argCnt) {
		printf("Error, argCnt mismatch %s (%d/%d)\n", rs->rn_Node.ln_Name, rs->rn_Args, argCnt);
		return;
	    }
	}
	if (Verbose)
	    fflush(stdout);

	tmpnam(tmpFile);
	sprintf(objFile, "%s.o", tmpFile);

	if (RegOpt)
	    sprintf(FuncName, "%s", rs->rn_Node.ln_Name);
	else
	    sprintf(FuncName, "%s%s", FuncPrefix, funcName);

	if ((ft = fopen(tmpFile, "w")) != NULL) {
	    if (ProfOpt) {
		fprintf(ft, "\txref\t__ProfInit\n");
		fprintf(ft, "\txref\t__ProfExec\n");

		fprintf(ft, "\n\tsection autoinit1,code\n");
		fprintf(ft, "\tlea\tlp0%s,A0\n", DataModel);
		fprintf(ft, "\tjsr\t__ProfInit\n");
		fprintf(ft, "\n\tsection libdata,data\n");
		fprintf(ft, "\tds.l\t0\n");
		fprintf(ft, "lp0\n");
		fprintf(ft, "\tdc.l\t0\n");
		fprintf(ft, "\tdc.l\t0\n");
		fprintf(ft, "\tdc.l\t0\n");
		fprintf(ft, "\tdc.w\t%d\n", (40 + strlen(FuncName) + (1 + 3)) & ~3);
		fprintf(ft, "\tdc.w\t0\n");
		fprintf(ft, "\tdc.l\t0\n");
		fprintf(ft, "\tdc.l\t0\n");
		fprintf(ft, "\tdc.l\t0\n");
		fprintf(ft, "\tdc.l\t0\n");
		fprintf(ft, "\tdc.l\tlp1\n");
		fprintf(ft, "\tdc.l\tlp2\n");
		fprintf(ft, "\tdc.b\t\'%s\',0\n", FuncName);

		fprintf(ft, "\tds.l\t0\n");
	    }
	    fprintf(ft, "\n\tsection ,code\n\n");
	    fprintf(ft, "\txref\t%s\n\n", base);

	    fprintf(ft, "\txdef\t%s\n", FuncName);
	    fprintf(ft, "%s:\n", FuncName);

	    AddLVOList(funcName, -bias);

	    if (ProfOpt) {
		fprintf(ft, "\tjsr\t__ProfExec%s\n", CodeModel);
		fprintf(ft, "lp1\n");
	    }

	    if (RegOpt == 0 || rs->rn_Args == -1) {	/*  STACK CALL */
		StackCall(ft, base, bias, FRegs, argCnt);
	    } else {					/*  REG CALL   */
		RegsCall(ft, base, bias, rs, FRegs);
	    }
	    fputs("\tEND\n", ft);
	    fclose(ft);

	    /*
	     *	assemble the temp file
	     */

	    if (Verbose)
		puts("");
	    if (AssembleFile(tmpFile, objFile))
		JoinOutput(fo, objFile);
	    remove(objFile);
	    remove(tmpFile);
	} else {
	    printf("\tError, Unable to create file: %s\n", tmpFile);
	}
    }
}

void
ScanRSTmp(fi)
FILE *fi;
{
    RSNode *rs;
    char *ptr;
    char *symPtr;

    while (fgets(Buf, sizeof(Buf), fi)) {
	if (strnicmp(Buf, "##regspec", 9) != 0)
	    continue;

	for (ptr = Buf + 9; *ptr == ' ' || *ptr == '\t'; ++ptr);
	symPtr = ptr;
	while (*ptr && *ptr != '(')
	    ++ptr;
	if (*ptr != '(') {
	    printf("Error scanning RS file: %s\n", Buf);
	    continue;
	}
	*ptr++ = 0;
	rs = malloc(sizeof(RSNode) + strlen(symPtr) + 1);
	rs->rn_Node.ln_Name = (char *)(rs + 1);
	rs->rn_Flag = 0;
	strcpy(rs->rn_Node.ln_Name, symPtr);

	if (*ptr == '*') {
	    rs->rn_Args = -1;
	    while (*ptr && *ptr != ')')
		++ptr;
	} else {
	    rs->rn_Args = 0;
	    while (*ptr && *ptr != ')') {
		switch(*ptr) {
		case 'd':
		case 'D':
		    rs->rn_Regs[rs->rn_Args++] = ptr[1] - '0';
		    ptr += 2;
		    break;
		case 'a':
		case 'A':
		    rs->rn_Regs[rs->rn_Args++] = ptr[1] - '0' + 8;
		    ptr += 2;
		    break;
		default:
		    printf("Error, Illegal RS file register spec: %s\n", ptr);
		    ptr = "";
		    break;
		}
		if (*ptr == ',')
		    ++ptr;
	    }
	}
	if (*ptr != ')') {
	    printf("Error scanning RS file: %s\n", symPtr + strlen(symPtr) + 1);
	    continue;
	}
	if (Verbose > 1)
	    printf("RS-SCAN: %s (%d)\n", rs->rn_Node.ln_Name, rs->rn_Args);
	AddTail((struct List *)&RSList, &rs->rn_Node);
    }
}

int
AssembleFile(inFile, outFile)
char *inFile;
char *outFile;
{
    remove(outFile);

    if (Verbose > 2) {
	FILE *fi;

	if ((fi = fopen(inFile, "r")) != NULL) {
	    while (fgets(Buf, sizeof(Buf), fi))
		fputs(Buf, stdout);
	    fclose(fi);
	}
    }
/*    sprintf(Buf, "%sdas %s -o %s -nu -sym", */
    sprintf(Buf, "%sdas %s -o %s",
	Prefix,
	inFile,
	outFile
     );
     if (Verbose > 0)
	 puts(Buf);
     if(Symbols)strcat(Buf," -s");
#ifdef unix
    system(Buf);
#else
    Execute(Buf, NULL, NULL);
#endif
    return(1);
}

void
JoinOutput(fo, file)
FILE *fo;
char *file;
{
    FILE *fi;
    short c;

    if ((fi = fopen(file, "r")) != NULL) {
	while ((c = getc(fi)) != EOF)
	    putc(c, fo);
	fclose(fi);
    } else {
	printf("Error, Can't read %s\n", file);
    }
}

/*
 *  Generate assembly for a stack based call
 */

void
StackCall(FILE *ft, char *base, long bias, short *regs, short args)
{
    uword mask = (RegsToMask(regs, args) | RF_BP) & ~RF_SCRATCH;
    char *ptr;
    short i;
    short j;
    short n;

    /*
     *	step 1, what regs need to be saved?
     */

    n = PushMask(ft, mask);

    /*
     *	step 2, load regs from stack
     */

    for (i = j = 0; i < args; i = j) {
	uword lmask = 1 << regs[i];
	short ln;
	int offset = n * 4 + i * 4 + 4;

	for (j = i + 1; j < args; ++j) {
	    if (regs[j] < regs[j-1])
		break;
	    lmask |= 1 << regs[j];
	}
	ptr = RegMaskToStr(lmask, &ln);
	if (ln > 1)
	    fprintf(ft, "\tmovem.l\t%d(sp),%s\n", offset, ptr);
	else if (ln > 0)
	    fprintf(ft, "\tmove.l\t%d(sp),%s\n", offset, ptr);
    }
    /*
     *	step 3, load library base register
     */

    fprintf(ft, "\tmove.l\t%s%s,A%d\n", base, DataModel, RB_BP - 8);

    /*
     *	step 4, make call & return
     */

    if (n) {
	fprintf(ft, "\tjsr\t-%ld(A%d)\n", bias, RB_BP - 8);
	PopMask(ft, mask);
	if (ProfOpt) {
	    fprintf(ft, "\tjsr\t__ProfExec%s\n", CodeModel);
	    fprintf(ft, "lp2\n");
	}
	fprintf(ft, "\tRTS\n");
    } else {
	if (ProfOpt) {
	    fprintf(ft, "\tjsr\t-%ld(A%d)\n", bias, RB_BP - 8);
	    fprintf(ft, "\tjsr\t__ProfExec%s\n", CodeModel);
	    fprintf(ft, "lp2\n");
	    fprintf(ft, "\tRTS\n");
	} else {
	    fprintf(ft, "\tjmp\t-%ld(A%d)\n", bias, RB_BP - 8);
	}
    }
}

/*
 *  Generate assembly for a register based call
 */

void
RegsCall(ft, base, bias, rs, regs)
FILE *ft;
char *base;
long bias;
RSNode *rs;
short *regs;
{
    uword mask = (RegsToMask(regs, rs->rn_Args) | RF_BP) & ~RF_SCRATCH;
    short i;
    short j;
    short n;

    /*
     *	step 1, what regs need to be saved?
     */

    n = PushMask(ft, mask);

    /*
     *	step 2, load regs from other regs.  If destination is 'in use',
     *	then use EXG instead (and track where the reg went to)
     *
     *	src:	rs->rn_Regs[i]
     *	dest:	regs[i]
     */

    for (i = 0; i < rs->rn_Args; ++i) {
	for (j = 0; j < rs->rn_Args; ++j) { /*	is dest in use? */
	    if (i != j && regs[i] == rs->rn_Regs[j])
		break;
	}
	if (j == rs->rn_Args) { 		/*  not in use		*/
	    if (regs[i] != rs->rn_Regs[i]) {	/*  not in right plac	*/
		fprintf(ft, "\tmove.l\t%s,%s\n", RegToStr(rs->rn_Regs[i]), RegToStr(regs[i]));
		rs->rn_Regs[i] = -1;
	    }
	} else {
	    fprintf(ft, "\texg.l\t%s,%s\n", RegToStr(rs->rn_Regs[i]), RegToStr(regs[i]));
	    rs->rn_Regs[j] = rs->rn_Regs[i];
	    rs->rn_Regs[i] = -1;
	}
    }

    /*
     *	step 3, load library base register
     */

    fprintf(ft, "\tmove.l\t%s%s,A%d\n", base, DataModel, RB_BP - 8);

    /*
     *	step 4, make call and return
     */

    if (n) {
	fprintf(ft, "\tjsr\t-%ld(A%d)\n", bias, RB_BP - 8);
	PopMask(ft, mask);

	if (ProfOpt) {
	    fprintf(ft, "\tjsr\t__ProfExec%s\n", CodeModel);
	    fprintf(ft, "lp2\n");
	}
	fprintf(ft, "\tRTS\n");
    } else {
	if (ProfOpt) {
	    fprintf(ft, "\tjsr\t-%ld(A%d)\n", bias, RB_BP - 8);
	    fprintf(ft, "\tjsr\t__ProfExec%s\n", CodeModel);
	    fprintf(ft, "lp2\n");
	    fprintf(ft, "\tRTS\n");
	} else {
	    fprintf(ft, "\tjmp\t-%ld(A%d)\n", bias, RB_BP - 8);
	}
    }
}

char *
RegMaskToStr(uword mask, short *nr)
{
    static char buf[64];
    char *ptr = buf;
    short i;
    short l = -1;

    *nr = 0;

    for (i = 0; i < 8; ++i) {
	if (mask & (1 << i)) {
	    ++*nr;
	    if (l >= 0)
		*ptr++ = '/';
	    ptr += sprintf(ptr, "D%d", i);
	    l = i;
	}
    }
    for (i = 8; i < 16; ++i) {
	if (mask & (1 << i)) {
	    ++*nr;
	    if (l >= 0)
		*ptr++ = '/';
	    ptr += sprintf(ptr, "A%d", i - 8);
	    l = i;
	}
    }
    *ptr = 0;
    return(buf);
}

uword
RegsToMask(short *regs, short args)
{
    uword mask = 0;

    while (args > 0) {
	mask |= 1 << *regs++;
	--args;
    }
    return(mask);
}

char *
RegToStr(short rno)
{
    static char Buf[2][16];
    static short BNo;
    char *ptr = Buf[BNo];

    BNo = 1 - BNo;
    if (rno < 8)
	sprintf(ptr, "D%d", rno);
    else
	sprintf(ptr, "A%d", rno - 8);
    return(ptr);
}

short
PushMask(FILE *fo, uword mask)
{
    short n;
    char *ptr;

    ptr = RegMaskToStr(mask, &n);
    if (n > 1)
	fprintf(fo, "\tmovem.l\t%s,-(sp)\n", ptr);
    else if (n > 0)
	fprintf(fo, "\tmove.l\t%s,-(sp)\n", ptr);
    return(n);
}

short
PopMask(FILE *fo, uword mask)
{
    short n;
    char *ptr;

    ptr = RegMaskToStr(mask, &n);
    if (n > 1)
	fprintf(fo, "\tmovem.l\t(sp)+,%s\n", ptr);
    else if (n > 0)
	fprintf(fo, "\tmove.l\t(sp)+,%s\n", ptr);
    return(n);
}

void
AddLVOList(funcName, bias)
char *funcName;
int bias;
{
    LVONode *node;

    if ((node = malloc(sizeof(LVONode) + strlen(funcName) + 1)) != NULL) {
	AddTail(&LVOList, &node->lv_Node);
	node->lv_Node.ln_Name = (char *)(node + 1);
	strcpy(node->lv_Node.ln_Name, funcName);
	node->lv_Offset = bias;
    }
}

void
GenerateLVOList(fo)
FILE *fo;
{
    char tmpFile[L_tmpnam];
    char objFile[L_tmpnam + 4];
    LVONode *node;
    FILE *ft;

    tmpnam(tmpFile);
    sprintf(objFile, "%s.o", tmpFile);

    if ((node = (LVONode *)RemHead((struct List *)&LVOList)) != NULL) {
	if ((ft = fopen(tmpFile, "w")) != NULL) {
	    fprintf(ft, "\n\tsection ,code\n\n");

	    while (node) {
		if (Verbose)
		    printf("_LVO%-20s = %d\n", node->lv_Node.ln_Name, node->lv_Offset);
		fprintf(ft, "_LVO%s\tEQU\t%d\n", node->lv_Node.ln_Name, node->lv_Offset);
		fprintf(ft, "\txdef\t_LVO%s\n", node->lv_Node.ln_Name);
		free(node);
		node = (LVONode *)RemHead((struct List *)&LVOList);
	    }
	    fputs("\tEND\n", ft);
	    fclose(ft);

	    if (AssembleFile(tmpFile, objFile))
		JoinOutput(fo, objFile);
	    remove(objFile);
	    remove(tmpFile);
	}
    }
}

/*
 *  generate auto-open tag code for this library
 */

void
GenerateAutoOpen(fo, base)
FILE *fo;
char *base;
{
    char tmpFile[L_tmpnam];
    char objFile[L_tmpnam + 4];
    FILE *ft;

    tmpnam(tmpFile);
    sprintf(objFile, "%s.o", tmpFile);

    if ((ft = fopen(tmpFile, "w")) != NULL) {
	fprintf(ft, "\n\txdef\t%s\n", base);
	fprintf(ft, "\n\txref\t__AutoFail0\n\n");
	fprintf(ft, "_LVOOpenLibrary\tequ\t-552\n");
	fprintf(ft, "_LVOCloseLibrary\tequ\t-414\n\n");

	fprintf(ft, "\n\tsection autoinit0,code\n\n");

	fprintf(ft, "\tmoveq.l\t#0,D0\n");
	fprintf(ft, "\tlea\tlibname(pc),A1\n");
	fprintf(ft, "\tjsr\t_LVOOpenLibrary(A6)\n");
	fprintf(ft, "\tmove.l\tD0,%s%s\n", base, DataModel);
	fprintf(ft, "\tbeq\t__AutoFail0\n");
	fprintf(ft, "\tbra\topennext\n");
	fprintf(ft, "libname\tdc.b\t'%s',0\n", SharedLibName);
	fprintf(ft, "\tds.w\t0\n");
	fprintf(ft, "opennext\n\n");

	fprintf(ft, "\tsection autoexit0,code\n\n");
	fprintf(ft, "\tmove.l\t%s%s,D0\n", base, DataModel);
	fprintf(ft, "\tbeq\tclosenext\n");
	fprintf(ft, "\tmove.l\tD0,A1\n");
	fprintf(ft, "\tjsr\t_LVOCloseLibrary(A6)\n");
	fprintf(ft, "closenext\n\n");

	fprintf(ft, "\tsection libdata,data\n\n");
	fprintf(ft, "%s\tdc.l\t0\n", base);

	fputs("\tEND\n", ft);
	fclose(ft);

	if (AssembleFile(tmpFile, objFile))
	    JoinOutput(fo, objFile);
	remove(objFile);
	remove(tmpFile);
    }
}


#ifdef LATTICE

char *
tmpnam(buf)
char *buf;
{
    static char Buf[L_tmpnam];
    static short Seq;

    if (buf == NULL)
	buf = Buf;
    sprintf(buf, "T:%06lx%d", (long)FindTask(NULL) >> 4, Seq++);
    return(buf);
}

#endif

void
DicePrefix(char *buf, char *av0, char *app)
{
    char *ptr;
    short n = 0;

    for (ptr=av0+strlen(av0); ptr >= av0 && *ptr != '/' && *ptr != ':'; --ptr)
	;
    ++ptr;
    if ((av0 = strchr(ptr, '_')) != NULL) {
	n = av0 - ptr + 1;
	strncpy(buf, ptr, n);
    }
    strcpy(buf + n, app);
}

