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

/*
 * rpc←cout.c, XDR routine outputter for the RPC protocol compiler 
 * Copyright (C) 1987, Sun Microsystems, Inc. 
 */
#include <stdio.h>
#include <strings.h>
#include "rpc←util.h"
#include "rpc←parse.h"


/*
 * Emit the interface for the given definition 
 */
void
emitDefn(def)
     definition *def;
{
    if (def->def←kind == DEF←PROGRAM || def->def←kind == DEF←CONST) {
	return;
    }
    f←print(fout,
	    "Get%s: PROC[h: Handle] RETURNS [res: %s.%s];\n",
	    def->def←name, svcName, def->def←name);
    f←print(fout,
	    "Put%s: PROC[h: Handle, in: %s.%s];\n\n",
	    def->def←name, svcName, def->def←name);
}

/*
 * Emit the implementation for the given definition 
 */
void
emitImpl(def)
     definition *def;
{
    enumval←list *l;

    if (def->def←kind == DEF←PROGRAM || def->def←kind == DEF←CONST) {
	return;
    }
    else if (def->def←kind == DEF←ENUM) {
        if (FillEnumAssignments(def->def.en.vals)) {
            f←print(fout, "%sNames: PUBLIC ARRAY %s.%s OF ROPE ← [\n",
                    def->def←name, svcName, def->def←name);
            for (l = def->def.en.vals; l != NULL; l = l->next) {
                f←print(fout, "\t\"%s\"", l->name);
	        if (l->next != NULL) f←print(fout, ",\n");
	        else f←print(fout, "\n");
            }
            f←print(fout, "\t];\n\n");
        }
    }

    f←print(fout,
	    "Get%s: PUBLIC PROC[h: Handle] RETURNS [res: %s.%s] = {\n",
	    def->def←name, svcName, def->def←name);
    switch (def->def←kind) {
      case DEF←UNION:
	emit←union←get(def);
	break;
      case DEF←ENUM:
	emit←enum←get(def);
	break;
      case DEF←STRUCT:
	emit←struct←get(def);
	break;
      case DEF←TYPEDEF:
	emit←typedef←get(def);
	break;
    }
    f←print(fout, "  };\n\n");

    f←print(fout,
	    "Put%s: PUBLIC PROC[h: Handle, in: %s.%s] = {\n",
	    def->def←name, svcName, def->def←name);
    switch (def->def←kind) {
      case DEF←UNION:
	emit←union←put(def);
	break;
      case DEF←ENUM:
	emit←enum←put(def);
	break;
      case DEF←STRUCT:
	emit←struct←put(def);
	break;
      case DEF←TYPEDEF:
	emit←typedef←put(def);
	break;
    }
    f←print(fout, "  };\n\n");
}


/* ARGSUSED */
static
emit←enum←get(def)
	definition *def;
{
    f←print(fout, "    res ← VAL[%s.GetInt32[h]];\n", SunRPC);
}

static
emit←enum←put(def)
     definition *def;
{
    f←print(fout, "    %s.PutInt32[h, ORD[in]];\n", SunRPC);
}

static emit←union←get←default(def, dflt, objPrefix)
     definition *def;
     declaration *dflt;
     char *objPrefix;
{
    char *defaultEnumElement;
    char *namesArray[128];
    int i;

    defaultEnumElement = DefaultEnumTypes(def, namesArray);
    for (i = 0; namesArray[i] != NULL; i++) {
	f←print(fout, "      %s => {\n", namesArray[i]);
	f←print(fout,
		"          v: REF %s %sObject ← NEW[%s %sObject];\n",
		namesArray[i], objPrefix,
		namesArray[i], objPrefix);
	if (!streq(dflt->type, "void"))
	    emit←get←stat(dflt, "      ", "v", TRUE, "h", def->def←name);
	f←print(fout, "          res ← v;\n");
	f←print(fout, "        };\n");
    }
    f←print(fout, "      ENDCASE => NULL;\n");
}

static
emit←union←get(def)
	definition *def;
{
    case←list *cl;
    declaration *cs, *dflt;
    char *srcMethod;
    char *defaultPrefix = "";
    char objPrefix[128];
    int numericFlag;
    char *mName;

    if (NumericEnumType(def->def.un.enum←decl.type))
	numericFlag = TRUE;
    else
	numericFlag = FALSE;

    f←print(fout, "    tag: %s;\n",
	    MapToCedarType(def->def.un.enum←decl.type, FALSE));
    
    srcMethod = rpcgenBaseType(def->def.un.enum←decl.type);
    if (srcMethod != NULL) {
	if (streq(srcMethod, "Int32"))
	    f←print(fout, "    tag ← VAL[%s.Get%s[h]];\n", SunRPC, srcMethod);
	else
	    f←print(fout, "    tag ← %s.Get%s[h];\n", SunRPC, srcMethod);
    }
    else {
	mName = ModuleName(def->def.un.enum←decl.type);
	f←print(fout, "    tag ← %sGet%s[h];\n",
		GetPutModuleName(def->def.un.enum←decl.type, TRUE),
		StripPrefix(mName, def->def.un.enum←decl.type));
    }

    sprintf(objPrefix, "%s.%s", svcName, def->def←name);
    f←print(fout, "    SELECT tag FROM\n");
    for (cl = def->def.un.cases; cl != NULL; cl = cl->next) {
	cs = &cl->case←decl;
	if (streq(cl->case←name, "default")) defaultPrefix = "0";
	if ((srcMethod != NULL) && streq(srcMethod, "Integer"))
	    f←print(fout, "      %s => {\n", cl->case←name);
	else
	    f←print(fout, "      %s => {\n", cl->case←name);
	if (PFlag) {
	    if (numericFlag)
		f←print(fout,
			"          v: REF v%s %sObject ← NEW[v%s %sObject];\n",
			cl->case←name, objPrefix,
			cl->case←name, objPrefix);
	    else
		f←print(fout,
			"          v: REF %s %sObject ← NEW[%s %sObject];\n",
			cl->case←name, objPrefix,
			cl->case←name, objPrefix);
	}
	else {
	    f←print(fout, "          v: REF %s%s %sObject ← NEW[%s%s %sObject];\n",
		    def->def←name, cl->case←name, objPrefix,
		    def->def←name, cl->case←name, objPrefix);
	    f←print(fout, "          v.%s ← tag;\n", def->def.un.enum←decl.name);
	}
	if (!streq(cs->type, "void"))
	    emit←get←stat(cs, "      ", "v", TRUE, "h", def->def←name);
	f←print(fout, "          res ← v;\n");
	f←print(fout, "          };\n");
    }
    dflt = def->def.un.default←decl;
    if (dflt != NULL) {
	if (PFlag && !numericFlag) {
	    emit←union←get←default(def, dflt, objPrefix);
	}
	else {
	    f←print(fout, "      ENDCASE => {\n");
	    if (PFlag && numericFlag)
		f←print(fout,
			"          v: REF default %sObject ← NEW[default %sObject];\n",
			objPrefix, objPrefix);
	    else
		f←print(fout, "          v: REF %s%sDefault %sObject ← NEW[%s%sDefault %sObject];\n",
			def->def←name, defaultPrefix, objPrefix,
			def->def←name, defaultPrefix, objPrefix);
	    f←print(fout, "          v.%s ← tag;\n", def->def.un.enum←decl.name);
	    if (!streq(dflt->type, "void"))
		emit←get←stat(dflt, "    ", "v", TRUE, "h", def->def←name);
	    f←print(fout, "        res ← v;\n");
	    f←print(fout, "        };\n");
	}
    }
    else {
	f←print(fout, "      ENDCASE => NULL;\n");
    }
}

static emit←union←put←default(def, dflt, objPrefix)
     definition *def;
     declaration *dflt;
     char *objPrefix;
{
    char *defaultEnumElement;
    char *namesArray[128];
    int i;

    defaultEnumElement = DefaultEnumTypes(def, namesArray);
    for (i = 0; namesArray[i] != NULL; i++) {
	f←print(fout, "      %s => {\n", namesArray[i]);
	f←print(fout,
		"          v: REF %s %sObject ← NARROW[in];\n",
		namesArray[i], objPrefix);
	    if (!streq(dflt->type, "void"))
		emit←put←stat(dflt, "      ", "v", TRUE, "h");
	    f←print(fout, "        };\n");
    }
    f←print(fout, "      ENDCASE => NULL;\n");
}

static
emit←union←put(def)
	definition *def;
{
    case←list *cl;
    declaration *cs, *dflt;
    char *srcMethod;
    char *defaultPrefix = "";
    char objPrefix[128];
    int numericFlag;
    char *mName;

    srcMethod = rpcgenBaseType(def->def.un.enum←decl.type);
    if (srcMethod != NULL) {
	if (streq(srcMethod, "Int32"))
	    f←print(fout, "    %s.Put%s[h, ORD[in.%s]];\n",
		    SunRPC, srcMethod, def->def.un.enum←decl.name);
	else
	    f←print(fout, "    %s.Put%s[h, in.%s];\n",
		    SunRPC, srcMethod, def->def.un.enum←decl.name);
    }
    else {
	mName = ModuleName(def->def.un.enum←decl.type);
	f←print(fout, "    %sPut%s[h, in.%s];\n",
		GetPutModuleName(def->def.un.enum←decl.type, TRUE),
		StripPrefix(mName, def->def.un.enum←decl.type),
		def->def.un.enum←decl.name);
    }

    if (NumericEnumType(def->def.un.enum←decl.type))
	numericFlag = TRUE;
    else
	numericFlag = FALSE;
    sprintf(objPrefix, "%s.%s", svcName, def->def←name);
    f←print(fout, "    SELECT in.%s FROM\n", def->def.un.enum←decl.name);
    for (cl = def->def.un.cases; cl != NULL; cl = cl->next) {
	cs = &cl->case←decl;
	if (streq(cl->case←name, "default")) defaultPrefix = "0";
	if (((srcMethod != NULL) &&
	     (streq(srcMethod, "Int32") || streq(srcMethod, "Card32"))))
	    f←print(fout, "      %s => {\n", cl->case←name);
	else
	    f←print(fout, "      %s => {\n", cl->case←name);
	if (PFlag) {
	    if (numericFlag)
		f←print(fout, "          v: REF v%s %sObject ← NARROW[in];\n",
			cl->case←name, objPrefix);
	    else
		f←print(fout, "          v: REF %s %sObject ← NARROW[in];\n",
			cl->case←name, objPrefix);
	}
	else {
	    f←print(fout, "          v: REF %s%s %sObject ← NARROW[in];\n",
		    def->def←name, cl->case←name, objPrefix);
	}
	if (!streq(cs->type, "void"))
	    emit←put←stat(cs, "      ", "v", TRUE, "h");
	f←print(fout, "          };\n");
    }
    dflt = def->def.un.default←decl;
    if (dflt != NULL) {
	if (PFlag && !numericFlag) {
	    emit←union←put←default(def, dflt, objPrefix);
	}
	else {
	    f←print(fout, "      ENDCASE => {\n");
	    if (PFlag && numericFlag)
		f←print(fout,
			"          v: REF default %sObject ← NARROW[in];\n",
			objPrefix);
	    else
		f←print(fout,
			"          v: REF %s%sDefault %sObject ← NARROW[in];\n",
			def->def←name, defaultPrefix, objPrefix);
	    if (!streq(dflt->type, "void"))
		emit←put←stat(dflt, "    ", "v", TRUE, "h");
	    f←print(fout, "        };\n");
	}
    }
    else {
	f←print(fout, "      ENDCASE => NULL;\n");
    }
}

static
emit←struct←get(def)
	definition *def;
{
    decl←list *dl;
    char *srcMethod;
    
    for (dl = def->def.st.decls; dl != NULL; dl = dl->next) {
	emit←get←stat(&(dl->decl), "", "res", TRUE, "h", def->def←name);
    }
}

emit←get←stat(dec, indent, varName, defaultFlag, h, structName)
     declaration *dec;
     char *indent;
     char *varName;
     int defaultFlag;		/* TRUE => svcName is default in the output
				   file. */
     char *h;
     char *structName;		/* Contains the name of the struct if dec is
				   the field of a struct.  Not used otherwise. */
{
    char decName[128];
    char *srcMethod;
    char modifier[8];
    int endFlag = 0;
    relation rel;
    StringHashElement elem;
    char buf[256];
    int inserted;
    char *mName;

    if (dec->name == NULL || streq(dec->name, ""))
	strcpy(decName, "");
    else if (streq(varName, ""))
	strcpy(decName, dec->name);
    else
	sprintf(decName, ".%s", dec->name);

    if (streq(dec->type, "opaque")) {
	if (dec->rel == REL←VECTOR) {
	    f←print(fout, "%s    FOR i: INT IN [0..%s) DO\n",
		    indent, MapToCedarType(dec->array←max, FALSE));
	    f←print(fout, "%s      %s%s[i] ← %s.GetByte[%s];\n",
		    indent, varName, decName, SunRPC, h);
	    f←print(fout, "%s      ENDLOOP;\n", indent);
	    f←print(fout, "%s    %s.GetAlign[%s];\n", indent, SunRPC, h);
	}
	else {
	    f←print(fout, "%s    %s%s ← %s.GetRefText[%s];\n",
		    indent, varName, decName, SunRPC, h);
	}
	return;
    }

    srcMethod = rpcgenBaseType(dec->type);
    rel = streq(dec->type, "string") ? REL←ALIAS : dec->rel;
    switch (rel) {
      case REL←POINTER:
	strcpy(modifier, "↑");
	f←print(fout, "%s    IF %s.GetInt32[%s] # 0 THEN {\n",
		indent, SunRPC, h);
	f←print(fout, "%s      %s%s ← NEW[%s];\n",
		indent, varName, decName,
		MapToCedarType(dec->type, !defaultFlag));
	endFlag = 1;
	break;
      case REL←VECTOR:
	strcpy(modifier, "[i]");
	f←print(fout, "%s    FOR i: INT IN [0..%s) DO\n",
		indent, ArrayMax(dec->array←max, FALSE));
	endFlag = 2;
	break;
      case REL←ARRAY:
	strcpy(modifier, "[i]");
	f←print(fout, "%s    {\n", indent);
	f←print(fout, "%s    len: INT ← %s.GetInt32[%s];\n", indent, SunRPC, h);
	if (dec->name == NULL)
	    sprintf(buf, "%s", structName);
	else
	    sprintf(buf, "%s.%s", structName, dec->name);
	elem = StringHashFind(SeqTypes, buf,
			      find, &inserted);
	if (index(elem->userData, '.') == NULL)
	    f←print(fout, "%s    %s%s ← NEW[%s.%sObject[len]];\n",
		    indent, varName, decName, svcName, 
		    elem->userData);
	else
	    f←print(fout, "%s    %s%s ← NEW[%sObject[len]];\n",
		    indent, varName, decName, elem->userData);
	f←print(fout, "%s    FOR i: INT IN [0..len) DO\n", indent);
	endFlag = 3;
	break;
      case REL←ALIAS:
	strcpy(modifier, "");
	break;
    }

    if (srcMethod != NULL) {
	if (streq(dec->type, "bool"))
	    f←print(fout, "%s%s    %s%s%s ← %s.Get%s[%s] # 0;\n",
		indent, endFlag > 0 ? "  " : "",
		varName, decName, modifier, SunRPC, srcMethod, h);
	else if (streq(dec->type, "float") || streq(dec->type, "double")) {
	    if (PCedarUDP)
		f←print(fout, "%s%s    %s%s%s ← SunRPCNumbers.Get%s[%s];\n",
			indent, endFlag > 0 ? "  " : "",
			varName, decName, modifier, srcMethod, h);
	    else		/* PCedarTCP or Cedar10 */
		f←print(fout, "%s%s    %s%s%s ← %s.Get%s[%s];\n",
			indent, endFlag > 0 ? "  " : "",
			varName, decName, modifier, SunRPC, srcMethod, h);
	}
	else if (streq(srcMethod, "Byte")) {
	    f←print(fout, "%s%s    [] ← %s.GetByte[%s];\n",
		    indent, endFlag > 0 ? "  " : "", SunRPC, h);
	    f←print(fout, "%s%s    [] ← %s.GetByte[%s];\n",
		    indent, endFlag > 0 ? "  " : "", SunRPC, h);
	    f←print(fout, "%s%s    [] ← %s.GetByte[%s];\n",
		    indent, endFlag > 0 ? "  " : "", SunRPC, h);
	    f←print(fout, "%s%s    %s%s%s ← %s.Get%s[%s];\n",
		    indent, endFlag > 0 ? "  " : "",
		    varName, decName, modifier, SunRPC, srcMethod, h);
	}
	else
	    f←print(fout, "%s%s    %s%s%s ← %s.Get%s[%s];\n",
		    indent, endFlag > 0 ? "  " : "",
		    varName, decName, modifier, SunRPC, srcMethod, h);
    }
    else {
	mName = ModuleName(dec->type);
	f←print(fout, "%s%s    %s%s%s ← %sGet%s[%s];\n",
		indent, endFlag > 0 ? "  " : "",
		varName, decName, modifier,
		GetPutModuleName(dec->type, defaultFlag),
		StripPrefix(mName, dec->type),
		h);
    }

    if (endFlag == 1) {
	f←print(fout, "%s      }\n", indent);
	f←print(fout, "%s    ELSE\n", indent);
	f←print(fout, "%s      %s%s ← NIL;\n", indent, varName, decName);
    }
    else if (endFlag == 2) {
	f←print(fout, "%s      ENDLOOP;\n", indent);
    }
    else if (endFlag == 3) {
	f←print(fout, "%s      ENDLOOP;\n", indent);
	f←print(fout, "%s    };\n", indent);
    }
}

static
emit←struct←put(def)
	definition *def;
{
    decl←list *dl;
    char *srcMethod;
    
    for (dl = def->def.st.decls; dl != NULL; dl = dl->next) {
	emit←put←stat(&(dl->decl), "", "in", TRUE, "h");
    }
}

emit←put←stat(dec, indent, varName, defaultFlag, h)
     declaration *dec;
     char *indent;
     char *varName;
     int defaultFlag;		/* TRUE => svcName is default in the
				   output file. */
     char *h;
{
    char decName[128];
    char *srcMethod;
    char modifier[8];
    int endFlag = 0;
    relation rel;
    char *mName;

    if (dec->name == NULL || streq(dec->name, ""))
	strcpy(decName, "");
    else if (streq(varName, ""))
	strcpy(decName, dec->name);
    else
	sprintf(decName, ".%s", dec->name);

    if (streq(dec->type, "opaque")) {
	if (dec->rel == REL←VECTOR) {
	    f←print(fout, "%s    FOR i: INT IN [0..%s) DO\n",
		    indent, MapToCedarType(dec->array←max, FALSE));
	    f←print(fout, "%s      %s.PutByte[%s, %s%s[i]];\n",
		    indent, SunRPC, h, varName, decName);
	    f←print(fout, "%s      ENDLOOP;\n", indent);
	    f←print(fout, "%s    %s.PutAlign[%s];\n", indent, SunRPC, h);
	}
	else {
	    f←print(fout, "%s    %s.PutRefText[%s, %s%s];\n",
		    indent, SunRPC, h, varName, decName);
	}
	return;
    }

    srcMethod = rpcgenBaseType(dec->type);
    rel = streq(dec->type, "string") ? REL←ALIAS : dec->rel;
    switch (rel) {
      case REL←POINTER:
	strcpy(modifier, "↑");
	f←print(fout, "%s    IF %s%s = NIL THEN {\n", indent, varName, decName);
	f←print(fout, "%s      %s.PutInt32[%s, 0];\n", indent, SunRPC, h);
	f←print(fout, "%s    }\n", indent);
	f←print(fout, "%s    ELSE {\n", indent);
	f←print(fout, "%s      %s.PutInt32[%s, 1];\n", indent, SunRPC, h);
	endFlag = 1;
	break;
      case REL←VECTOR:
	strcpy(modifier, "[i]");
	f←print(fout, "%s    FOR i: INT IN [0..%s) DO\n",
		indent, ArrayMax(dec->array←max, FALSE));
	endFlag = 2;
	break;
      case REL←ARRAY:
	strcpy(modifier, "[i]");
	f←print(fout, "%s    %s.PutInt32[%s, %s%s.size];\n",
		indent, SunRPC, h, varName, decName);
	f←print(fout, "%s    FOR i: INT IN [0..%s%s.size) DO\n",
		indent, varName, decName);
	endFlag = 2;
	break;
      case REL←ALIAS:
	strcpy(modifier, "");
	break;
    }

    if (srcMethod != NULL) {
	if (streq(dec->type, "bool"))
	    f←print(fout, "%s%s    %s.Put%s[%s, IF %s%s%s THEN 1 ELSE 0];\n",
		    indent, endFlag > 0 ? "  " : "", SunRPC,
		    srcMethod, h, varName, decName, modifier);
	else if (streq(dec->type, "float") || streq(dec->type, "double")) {
	    if (PCedarUDP)
		f←print(fout, "%s%s    SunRPCNumbers.Put%s[%s, %s%s%s];\n",
			indent, endFlag > 0 ? "  " : "",
			srcMethod, h, varName, decName, modifier);
	    else		/* PCedarTCP or Cedar10 */
		f←print(fout, "%s%s    %s.Put%s[%s, %s%s%s];\n",
			indent, endFlag > 0 ? "  " : "", SunRPC,
			srcMethod, h, varName, decName, modifier);
	}
	else if (streq(srcMethod, "Byte"))
	    f←print(fout, "%s%s    %s.PutInt32[%s, %s%s%s];\n",
		    indent, endFlag > 0 ? "  " : "", SunRPC, h,
		    varName, decName, modifier);
	else
	    f←print(fout, "%s%s    %s.Put%s[%s, %s%s%s];\n",
		    indent, endFlag > 0 ? "  " : "", SunRPC,
		    srcMethod, h, varName, decName, modifier);
    }
    else {
	mName = ModuleName(dec->type);
	f←print(fout, "%s%s    %sPut%s[%s, %s%s%s];\n",
		indent, endFlag > 0 ? "  " : "",
		GetPutModuleName(dec->type, defaultFlag),
		StripPrefix(mName, dec->type), h,
		varName, decName, modifier);
    }

    if (endFlag == 1) f←print(fout, "%s    };\n", indent);
    else if (endFlag == 2) f←print(fout, "%s      ENDLOOP;\n", indent);
}

static
emit←typedef←get(def)
	definition *def;
{
    declaration decRec;

    decRec.prefix = def->def.ty.old←prefix;
    decRec.type = def->def.ty.old←type;
    decRec.name = NULL;
    decRec.rel = def->def.ty.rel;
    decRec.array←max = def->def.ty.array←max;
    emit←get←stat(&decRec, "", "res", TRUE, "h", decRec.type);
}

static
emit←typedef←put(def)
	definition *def;
{
    declaration decRec;

    decRec.prefix = def->def.ty.old←prefix;
    decRec.type = def->def.ty.old←type;
    decRec.name = NULL;
    decRec.rel = def->def.ty.rel;
    decRec.array←max = def->def.ty.array←max;
    emit←put←stat(&decRec, "", "in", TRUE, "h");
}