#ifndef lint
static char sccsid[] = "@(#)rpc←util.c 1.1 90/10/29 (C) 1987 SMI";
#endif

/*
 * rpc←util.c, Utility routines for the RPC protocol compiler 
 * Copyright (C) 1987, Sun Microsystems, Inc. 
 */
#include <stdio.h>
#include <string.h>
#include "rpc←scan.h"
#include "rpc←parse.h"
#include "rpc←util.h"

#define NFILES 5
char *outfiles[NFILES];		/* output file names */
int nfiles;

FILE *fout;			/* file pointer of current output */

/*
 * Reinitialize the world 
 */
reinitialize()
{
	bzero(CurrentContext->curline, MAXLINESIZE);
	CurrentContext->where = CurrentContext->curline;
	CurrentContext->linenum = 0;
	CurrentContext->defined = NULL;
	CurrentContext->printDirectives = TRUE;

	CurrentProgNameFlag = -1;
}

/*
 * string equality 
 */
streq(a, b)
	char *a;
	char *b;
{
	return (strcmp(a, b) == 0);
}

/*
 * find a value in a list 
 */
char *
findval(lst, val, cmp)
	list *lst;
	char *val;
	int (*cmp) ();

{
	for (; lst != NULL; lst = lst->next) {
		if ((*cmp) (lst->val, val)) {
			return (lst->val);
		}
	}
	return (NULL);
}

/*
 * store a value in a list 
 */
void
storeval(lstp, val)
	list **lstp;
	char *val;
{
	list **l;
	list *lst;

	for (l = lstp; *l != NULL; l = (list **) & (*l)->next);
	lst = ALLOC(list);
	lst->val = val;
	lst->next = NULL;
	*l = lst;
}

static
findit(def, type)
	definition *def;
	char *type;
{
	return (streq(def->def←name, type));
}

static char *
fixit(type, orig)
	char *type;
	char *orig;
{
	definition *def;

	def = (definition *) FINDVAL(CurrentContext->defined, type, findit);
	if (def == NULL || def->def←kind != DEF←TYPEDEF) {
		return (orig);
	}
	switch (def->def.ty.rel) {
	case REL←VECTOR:
		return (def->def.ty.old←type);
	case REL←ALIAS:
		return (fixit(def->def.ty.old←type, orig));
	default:
		return (orig);
	}
}

char *
fixtype(type)
	char *type;
{
	return (fixit(type, type));
}

char *
stringfix(type)
	char *type;
{
	if (streq(type, "string")) {
		return ("wrapstring");
	} else {
		return (type);
	}
}

void
ptype(prefix, type, follow)
	char *prefix;
	char *type;
	int follow;
{
	if (prefix != NULL) {
		if (streq(prefix, "enum")) {
			f←print(fout, "enum ");
		} else {
			f←print(fout, "struct ");
		}
	}
	if (streq(type, "bool")) {
		f←print(fout, "bool←t ");
	} else if (streq(type, "string")) {
		f←print(fout, "char *");
	} else {
		f←print(fout, "%s ", follow ? fixtype(type) : type);
	}
}

static
typedefed(def, type)
	definition *def;
	char *type;
{
	if (def->def←kind != DEF←TYPEDEF || def->def.ty.old←prefix != NULL) {
		return (0);
	} else {
		return (streq(def->def←name, type));
	}
}

isvectordef(type, rel)
	char *type;
	relation rel;
{
	definition *def;

	for (;;) {
		switch (rel) {
		case REL←VECTOR:
			return (!streq(type, "string"));
		case REL←ARRAY:
			return (0);
		case REL←POINTER:
			return (0);
		case REL←ALIAS:
			def = (definition *) FINDVAL(CurrentContext->defined,
						     type, typedefed);
			if (def == NULL) {
				return (0);
			}
			type = def->def.ty.old←type;
			rel = def->def.ty.rel;
		}
	}
}

char *
locase(str)
	char *str;
{
	char c;
	static char buf[100];
	char *p = buf;

	while (c = *str++) {
		*p++ = (c >= 'A' && c <= 'Z') ? (c - 'A' + 'a') : c;
	}
	*p = 0;
	return (buf);
}

void
pvname(pname, vnum)
	char *pname;
	char *vnum;
{
	f←print(fout, "%s←%s", locase(pname), vnum);
}

/*
 * print a useful (?) error message, and then die 
 */
void
error(msg)
	char *msg;
{
	printwhere();
	f←print(stderr, "%s, line %d: ",
		CurrentContext->infilename, CurrentContext->linenum);
	f←print(stderr, "%s\n", msg);
	crash();
}

/*
 * Something went wrong, unlink any files that we may have created and then
 * die. 
 */
crash()
{
	int i;

	for (i = 0; i < nfiles; i++) {
		(void) unlink(outfiles[i]);
	}
	exit(1);
}

void
record←open(file)
	char *file;
{
	if (nfiles < NFILES) {
		outfiles[nfiles++] = file;
	} else {
		f←print(stderr, "too many files!\n");
		crash();
	}
}

static char expectbuf[100];
static char *toktostr();

/*
 * error, token encountered was not the expected one 
 */
void
expected1(exp1)
	tok←kind exp1;
{
	s←print(expectbuf, "expected '%s'",
		toktostr(exp1));
	error(expectbuf);
}

/*
 * error, token encountered was not one of two expected ones 
 */
void
expected2(exp1, exp2)
	tok←kind exp1, exp2;
{
	s←print(expectbuf, "expected '%s' or '%s'",
		toktostr(exp1),
		toktostr(exp2));
	error(expectbuf);
}

/*
 * error, token encountered was not one of 3 expected ones 
 */
void
expected3(exp1, exp2, exp3)
	tok←kind exp1, exp2, exp3;
{
	s←print(expectbuf, "expected '%s', '%s' or '%s'",
		toktostr(exp1),
		toktostr(exp2),
		toktostr(exp3));
	error(expectbuf);
}

void
tabify(f, tab)
	FILE *f;
	int tab;
{
	while (tab--) {
		(void) fputc('\t', f);
	}
}


static token tokstrings[] = {
			     {TOK←IDENT, "identifier"},
			     {TOK←CONST, "const"},
			     {TOK←RPAREN, ")"},
			     {TOK←LPAREN, "("},
			     {TOK←RBRACE, "}"},
			     {TOK←LBRACE, "{"},
			     {TOK←LBRACKET, "["},
			     {TOK←RBRACKET, "]"},
			     {TOK←STAR, "*"},
			     {TOK←COMMA, ","},
			     {TOK←EQUAL, "="},
			     {TOK←COLON, ":"},
			     {TOK←SEMICOLON, ";"},
			     {TOK←UNION, "union"},
			     {TOK←STRUCT, "struct"},
			     {TOK←SWITCH, "switch"},
			     {TOK←CASE, "case"},
			     {TOK←DEFAULT, "default"},
			     {TOK←ENUM, "enum"},
			     {TOK←TYPEDEF, "typedef"},
			     {TOK←INT, "int"},
			     {TOK←SHORT, "short"},
			     {TOK←LONG, "long"},
			     {TOK←UNSIGNED, "unsigned"},
			     {TOK←DOUBLE, "double"},
			     {TOK←FLOAT, "float"},
			     {TOK←CHAR, "char"},
			     {TOK←STRING, "string"},
			     {TOK←OPAQUE, "opaque"},
			     {TOK←BOOL, "bool"},
			     {TOK←VOID, "void"},
			     {TOK←PROGRAM, "program"},
			     {TOK←VERSION, "version"},
			     {TOK←EOF, "??????"}
};

static char *
toktostr(kind)
	tok←kind kind;
{
	token *sp;

	for (sp = tokstrings; sp->kind != TOK←EOF && sp->kind != kind; sp++);
	return (sp->str);
}

static
printbuf()
{
	char c;
	int i;
	int cnt;

#	define TABSIZE 4

	for (i = 0; c = CurrentContext->curline[i]; i++) {
		if (c == '\t') {
			cnt = 8 - (i % TABSIZE);
			c = ' ';
		} else {
			cnt = 1;
		}
		while (cnt--) {
			(void) fputc(c, stderr);
		}
	}
}

static
printwhere()
{
	int i;
	char c;
	int cnt;

	printbuf();
	for (i = 0; i < CurrentContext->where - CurrentContext->curline; i++) {
		c = CurrentContext->curline[i];
		if (c == '\t') {
			cnt = 8 - (i % TABSIZE);
		} else {
			cnt = 1;
		}
		while (cnt--) {
			(void) fputc('↑', stderr);
		}
	}
	(void) fputc('\n', stderr);
}

/*
 * Return the module name for a given defn.
 */
char *ModuleName(defn)
     char *defn;
{
    StringHashElement importElem;
    ImportModuleRec *m;

    importElem = StringHashFind(ImportNames, defn, find, NULL);
    if (importElem != NULL) {
	m = (ImportModuleRec *) importElem->userData;
	return (m->moduleName);
    }
    else
	return (svcName);
}

/*
 * Return the getput module name for a given defn, followed by a period.
 * If it is the name of the getput module for the service we are currently
 * compiling then return the "" string if defaultFlag is TRUE.
 */
char *GetPutModuleName(defn, defaultFlag)
     char *defn;
     int defaultFlag;
{
    StringHashElement importElem;
    ImportModuleRec *m;
    char buf[256];

    importElem = StringHashFind(ImportNames, defn, find, NULL);
    if (importElem != NULL) {
	m = (ImportModuleRec *) importElem->userData;
	sprintf(buf, "%sGetPut.", m->moduleName);
	return (strdup(buf));
    }
    else if (defaultFlag)
	return ("");
    else {
	sprintf(buf, "%sGetPut.", svcName);
	return (strdup(buf));
    }
}

/*
 * Map from an rpcgen name to a Cedar name.
 */
char *MapToCedarName(name, defaultFlag)
     char *name;
     int defaultFlag;
{
    StringHashElement importElem;
    ImportModuleRec *m;
    char buf[256];
    char *newName;

    importElem = StringHashFind(ImportNames, name, find, NULL);
    if (importElem != NULL) {
	m = (ImportModuleRec *) importElem->userData;
	newName = m->defnName;
    }
    else if (defaultFlag) {
	newName = name;
    }
    else {
	sprintf(buf, "%s.%s", svcName, name);
	newName = strdup(buf);
    }
    return (newName);
}

/*
 * Map from an rpcgen type name to an Cedar type name.
 */
char *MapToCedarType(type, defaultFlag)
     char *type;
     int defaultFlag;
{
    if (streq(type, "int") || streq(type, "long"))
	return ("INT32");
    else if (streq(type, "string"))
	return ("ROPE");
    else if (streq(type, "bool"))
	return ("BOOLEAN");
    else if (streq(type, "char"))
	return ("BYTE");
    else if (streq(type, "u←int") ||
	     streq(type, "uint") ||
	     streq(type, "u←long") ||
	     streq(type, "ulong"))
	return ("CARD32");
    else if (streq(type, "u←short") ||
	     streq(type, "ushort"))
	return ("CARD16");
    else if (streq(type, "u←char") ||
             streq(type, "uchar"))
	return ("BYTE");
    else if (streq(type, "short"))
	return ("INT16");
    else if (streq(type, "float"))
	return ("REAL");
    else if (streq(type, "double"))
	return ("DREAL");
    else if (streq(type, "opaque"))
	return ("REF ARRAY OF CHAR");
    else if (streq(type, "~0"))
	return ("LAST(INTEGER)");
    else {
	if (isdigit(type[0]))
	    return type;
	else
	    return (MapToCedarName(type, defaultFlag));
    }
}

/*
 * Return the Cedar source/sink method type for an rpcgen type.
 * Returns NULL if there is none.
 */
char *rpcgenBaseType(type)
     char *type;
{
    if (streq(type, "int") || streq(type, "long"))
	return ("Int32");
    else if (streq(type, "string"))
	return ("Rope");
    else if (streq(type, "bool"))
	return ("Int32");
    else if (streq(type, "char"))
	return ("Byte");
    else if (streq(type, "u←int") ||
	     streq(type, "uint") ||
	     streq(type, "u←long") ||
	     streq(type, "ulong"))
	return ("Card32");
    else if (streq(type, "u←short") ||
	     streq(type, "ushort"))
	return ("Card32");
    else if (streq(type, "u←char") ||
             streq(type, "uchar"))
	return ("Byte");
    else if (streq(type, "short"))
	return ("Int32");
    else if (streq(type, "float"))
	return ("Real");
    else if (streq(type, "double"))
	return ("DReal");
    else
	return (NULL);
}

/*
 * Return the appropriate array index number string.
 * If array←max is a number then return that.
 * If array←max is an identifier then return the appropriate Cedar
 * identifier name (which may include a module name).
 */
char *ArrayMax(numStr, defaultFlag)
     char *numStr;
     int defaultFlag;
{
    char *str;
    char buf[256];
    int n = atoi(numStr);
    if (n == 0) {
	str = MapToCedarType(numStr, defaultFlag);
	return (strdup(str));
    }
    else {
	sprintf(buf, "%d", n);
	return (strdup(buf));
    }
}


/*
 * Return the base type of a definition.
 */
definition *BaseType(def)
     definition *def;
{
    StringHashElement elem;

    while (def->def←kind == DEF←TYPEDEF) {
	elem = StringHashFind(SymbolNames, def->def.ty.old←type, find, NULL);
	if (elem == NULL) break;
	def = (definition *) (elem->userData);
    }
    return def;
}


/*
 * Return the base type of a struct definition.
 * If typeName is not the name of a struct defn then return NULL.
 */
definition *FindStructDefn(typeName)
{
    StringHashElement elem;
    definition *defStruct;

    elem = StringHashFind(SymbolNames, typeName, find, NULL);
    if (elem != NULL) {
	defStruct = (definition *) (elem->userData);
	defStruct = BaseType(defStruct);
	if (defStruct->def←kind == DEF←STRUCT)
	    return (defStruct);
    }

    return NULL;
}


/*
 * Return a unique name that is different from any of the names in dl.
 */
char *FindUniqueName(name, dl)
     char *name;
     decl←list *dl;
{
    char *newname = name;
    int indx = 0;
    char buf[128];

  L:
    for (; dl != NULL; dl = dl->next) {
	if (streq(newname, dl->decl.name)) {
	    sprintf(buf, "%s%d", name, indx++);
	    newname = buf;
	    goto L;
	}
    }
    return (strdup(newname));
}


/*
 * Strip module name prefix off name if there is one.
 */
char *
StripPrefix(module, str)
     char *module;
     char *str;
{
    char *p;
    char namePrefix[128];
    int namePrefixLen;

    if (PFlag) {
	sprintf(namePrefix, "%s", module);
	namePrefixLen = strlen(namePrefix);
	
	if (strncmp(str, namePrefix, namePrefixLen) == 0)
	    return (str + namePrefixLen);
	else
	    return str;
    }
    else {
	return str;
    }
}


/*
 * Make sure all the assignment fields of "l" have meaningful values.
 * Return an indication of whether the enumerated type is a compact, 0-based
 * enumeration or not.
 */
int
FillEnumAssignments(l)
    enumval←list *l;
{
    char buf[64];
    int val, prevVal;
    int i, minVal, maxVal, nVals;
    
    if (l->assignment == NULL) {
	l->assignment = "0";
    }
    prevVal = 0;
    minVal = maxVal = atoi(l->assignment);
    nVals = 0;
    for (; l != NULL; l = l->next) {
        if (l->assignment == NULL) {
            sprintf(buf, "%d", prevVal + 1);
            l->assignment = strdup(buf);
	}
	prevVal = atoi(l->assignment);
	if (prevVal < minVal) minVal = prevVal;
	if (prevVal > maxVal) maxVal = prevVal;
	nVals++;
    }
    
    if ((minVal == 0) && (maxVal == (nVals - 1))) {
        return 1;
    }
    else  {
        return 0;
    }
}


/*
 * Return TRUE if the type of decl is "int" or "unsigned int".
 */
int NumericEnumType(type)
     char *type;
{
    if (streq(type, "int") ||
	streq(type, "u←int") ||
	streq(type, "uint"))
	return (TRUE);
    else
	return (FALSE);
}


/*
 * Returns a comma-separated list of enum element names that correspond
 * to the default case of a union and a null-terminated array of such
 * element names.
 */
char *DefaultEnumTypes(def, namesArray)
     definition *def;
     char *namesArray[];
{
    StringHashElement elem;
    list *l;
    case←list *cl;
    char *names[256];
    int n = 0;
    int i, j;
    static char str[256];

    elem = StringHashFind(EnumTypes, def->def.un.enum←decl.type, find, NULL);
    if (elem == NULL) return "";
    for (l = (list *) elem->userData; l != NULL; l = l->next) {
	names[n++] = l->val;
    }
    names[n] = NULL;

    for (cl = def->def.un.cases; cl != NULL; cl = cl->next) {
	for (i = 0; i < n; i++) {
	    if (streq(names[i], cl->case←name)) {
		names[i] = "";
		break;
	    }
	}
    }

    str[0] = '\0';
    for (i = 0, j = 0; i < n; i++) {
	if (!streq(names[i], "")) {
	    strcat(str, names[i]);
	    strcat(str, ", ");
	    namesArray[j++] = names[i];
	}
    }
    str[strlen(str)-2] = '\0';
    namesArray[j] = NULL;
    return str;
}