/*
 * generator.c -- phase 2 of the generator.
 *
 * takes a file, which has a list of signature as input,
 * and outputs the client, server, .xg, and makefiles
 * This file is normally created by running tableGen.c,
 * but theoretically could be generated by a human.
 *
 * Format of the signatures : no_of_args list_of_arg_signs
 *  arg_sign : (dir type)
 *
 *
 * This program is used to generate client/server pairs that
 * make and verify random rpcs.  It can be used to generate
 * both pthread and lwp versions of the client and server.
 * It was designed to verify that the new pthread rx library
 * was functioning correctly.
 *
 * The key to understanding how this program works is to realize
 * that all the data necessary to generate the client/server
 * code is generated first, in char string format.
 * For example, if you are generating 10 rpc's, the input and
 * output values of all the parameters are generated - and
 * stored as strings.  This allows you to write out the values
 * as the client and server are created without having to worry
 * about data type conversion.
 *
 * In addition, these strings (along with other information) are
 * stored in a structure that is iterated over many times during
 * the construction of the client and server.
 *
 * Often this iteration takes the form of:
 *     for (i=0; i < argsP->argCount; i++)
 *
 * It may be possible to eliminate some of these iterations for
 * efficiency sake, but this was not done - allowing an easier
 * implementation.
 *
 * The bulk of this code was lifted from the delight project.
 *
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <float.h>
#include <malloc.h>
#include <assert.h>
#include "generator.h"

static char *unix_symbols[] = {
    "make -f",
    "include ../../../config/Makefile.$(SYS_NAME)\n",
    "o",
    "$(CC) $(CFLAGS) -c",
    "#include <netdb.h>\n",
    "",

/* all one string */
    "LIBS = $(DESTDIR)lib/libafsrpc.so \\\n"
    "\t$(DESTDIR)lib/afs/libcmd.a \\\n"
    "\t$(DESTDIR)lib/afs/libcom_err.a \\\n"
    "\t$(DESTDIR)lib/afs/util.a \n",

/* all one string */
    "LIBS = $(DESTDIR)lib/librxkad.a \\\n"
    "\t$(DESTDIR)lib/libdes.a \\\n"
    "\t$(DESTDIR)lib/librx.a \\\n"
    "\t$(DESTDIR)lib/liblwp.a \\\n"
    "\t$(DESTDIR)lib/afs/libcmd.a \\\n"
    "\t$(DESTDIR)lib/afs/libcom_err.a \\\n"
    "\t$(DESTDIR)lib/afs/util.a \\\n"
    "\t/usr/ucblib/libucb.a \n",

/* all one string */
    "\t@if [ ! -r DEST ] ; \\\n"
    "\tthen \\\n"
    "\t\techo \"Must create DEST link by hand before building tests\"; \\\n"
    "\t\texit 1; \\\n"
    "\tfi\n"
};

static char *nt_symbols[] = {
    "nmake /f",
    "!INCLUDE ../../../config/NTMakefile.$(SYS_NAME)\n",
    "obj",
    "$(C2OBJ)",
    "#include <windows.h>\n",
    "all",

/* all one string */
    "LIBS = $(DESTDIR)/lib/afsrpc.lib \\\n"
    "\t$(DESTDIR)/lib/afs/afscmd.lib \\\n"
    "\t$(DESTDIR)/lib/afs/afscom_err.lib \\\n"
    "\t$(DESTDIR)/lib/afs/afsutil.lib \\\n"
    "\t$(DESTDIR)/lib/pthread.lib \\\n"
    "\t$(DESTDIR)/lib/afsreg.lib \\\n"
    "\t$(XLIBS)\n",

/* all one string */
    "LIBS = $(DESTDIR)/lib/afsrxkad.lib \\\n"
    "\t$(DESTDIR)/lib/afsdes.lib \\\n"
    "\t$(DESTDIR)/lib/afsrx.lib \\\n"
    "\t$(DESTDIR)/lib/afslwp.lib \\\n"
    "\t$(DESTDIR)/lib/afs/afscmd.lib \\\n"
    "\t$(DESTDIR)/lib/afs/afscom_err.lib \\\n"
    "\t$(DESTDIR)/lib/afs/afsutil.lib \\\n"
    "\t$(DESTDIR)/lib/afsreg.lib \n",

/* all one string */
    "!\tIF (!EXIST(DEST))\n"
    "!\t\tERROR Must create DEST link by hand before building tests\n"
    "!\tENDIF\n",
};

static char **platform = nt_symbols;

#if defined(WIN32)
#define strdup(x)		_strdup(x)
#endif /* WIN32 */

/*
 * 31 bit random number generator, we don't really care how random
 * these numbers are, it is more important that identical rx files
 * are generated no matter what platform the generator runs on
 */
static unsigned long randVal = 0x330E16;

typedef enum {
   PTHREADS,
   LWPS
} threadModel_t;

static threadModel_t threadModel = PTHREADS;

PRIVATE double drand32(void)
{
    randVal = ((randVal * 0xEECE66D) + 0xB) & 0xFFFFFFFF;
    return ((double)(randVal) / 4294967296.0);
}

/*
 * GetDescription writes a one line comment describing the parameters
 * used in an rpc in the client program
 */
PRIVATE char *GetDescription(rpcArgs *argsP)
{
    char *bufP2;
    int i;

    bufP2 = (char *) calloc((MAX_TYP_STR+MAX_DIR_STR+ATTRIB_LEN*ATTRIB_NO)
		    *argsP->argCount+3, sizeof(char));
    MEM_CHK(bufP2, "GetDescription: out of mem bufP2\n");

    for (i=0; i < argsP->argCount; i++) {
	strcat(bufP2, argsP->argDescr[i].direction);
	strcat(bufP2, argsP->argDescr[i].type);
    }
    return(bufP2);
}

/*
 * GetName -- get the name of the current server/instance
 */
PRIVATE char *GetName(char *serverName, int sign_no)
{
    /* itl file name 8.3 format*/
    char *bufP;

    bufP = (char *) malloc(32 * sizeof(char));
    MEM_CHK(bufP, "GetName: bufP out of mem\n");

    sprintf(bufP, "%s%d", serverName, sign_no);
    return bufP;
}

/*
 * OpenOutFile -- open a file in the output directory
 */
PRIVATE FILE *OpenOutFile(char *outDir, char *fileName, char *tailStr)
{
    char *bufP;
    FILE *fP;

    if (outDir) {
	bufP = (char *) malloc(sizeof(char)*(strlen(fileName)+
			    strlen(tailStr)+strlen(outDir)+2));
	MEM_CHK(bufP, "OpenOutFile : Out of mem -- bufP\n");
	sprintf(bufP, "%s/%s%s", outDir, fileName, tailStr);
    } else {
	bufP = (char *) malloc(sizeof(char)*(strlen(fileName)+
			    strlen(tailStr)+1));
	MEM_CHK(bufP, "OpenOutFile : Out of mem -- bufP\n");
	sprintf(bufP, "%s%s", fileName, tailStr);
    }
    fP = fopen(bufP, "w");
    MEM_CHK(fP, "main: Unable to open output file\n");
    free(bufP);
    return (fP);
}

/*
 * WriteXGDecl -- write the declaration for a parameter
 */
PRIVATE void WriteXGDecl(arg_tuple *arg, FILE *xg_h, int i, 
			  int structFlag, int lastFlag)
{
    if (structFlag) {
	if (!strcmp(arg->type, "varString")) {
	    fprintf(xg_h, "\tchar a%d[STR_MAX];\n", i);
	}
	else {
	    fprintf(xg_h, "\tdrpc_%s_t a%d;\n", arg->type, i);
	}
    } else {
	if (!strncmp(arg->type, "ar_", 2)) {
	    fprintf(xg_h, "\t%s drpc_out_%s_t *a%d",
		    arg->direction, arg->type, i);
	}
	else if (!strcmp(arg->type, "varString")) {
	    fprintf(xg_h, "\t%s string a%d<STR_MAX>",
		    arg->direction, i);
	}
	else if (!strcmp(arg->direction, "IN")) {
	    fprintf(xg_h, "\t%s drpc_%s_t a%d",
		    arg->direction, arg->type, i);
	} else {
	    fprintf(xg_h, "\t%s drpc_%s_t *a%d",
		    arg->direction, arg->type, i);
	}
	if (lastFlag) {
	    fputs(");\n", xg_h);
	} else {
	    fputs(",\n", xg_h);
	}
    }
}

/*
 * WriteXG -- write the body of the xg interface
 */
PRIVATE void WriteXG(rpcArgs *argsP, FILE *xg_h, char *serverName, int sign_no)
{
    int i;
    char *name = GetName(serverName, sign_no);

    /*
     * CASE 1: all arguments passed as parameters
     */

    fprintf(xg_h, "\nproc %s(\n", name);

    /* declare each arg */
    for (i=0; i < argsP->argCount; i++) {
	WriteXGDecl(&argsP->argDescr[i], xg_h, i, FALSE,
		     (i==argsP->argCount-1));
    }

    /* Now pass the paramaters inside a structure */
    fprintf(xg_h, "\nstruct %s_t {\n", name);
    for (i=0; i < argsP->argCount; i++) {
	WriteXGDecl(&argsP->argDescr[i], xg_h, i, TRUE,
		     (i==argsP->argCount-1));
    }
    fprintf(xg_h, "} ;\n");
    fprintf(xg_h, "\nproc %s_s(\n", name);
    fprintf(xg_h, "\tINOUT struct %s_t *s);\n", name);

    free(name);
}

/*
 * GetRandStr -- get a random string
 */
PRIVATE void GetRandStr(int strLen, char **ret, int *rP)
{
    int i, randI;
    char buf[5];

    strcpy(*ret, "\"");
    i = 0;
    while (i < strLen) {
	randI = 'A' + ('Z' - 'A')*drand32();
	sprintf(buf, "%c", randI);
	strcat(*ret, buf);
	i++;
    }
    strcat(*ret, "\"");
    *rP = randI;
}

/*
 * GetRandP -- sets a string to a random value of the data type
 * inthe case of a char type, ret holds "%c" and ret2 holds '%c'
 */
PRIVATE void GetRandP(char *typ, char **ret, char **ret2)
{
    int randI, strLen;
    float f;
    double d, d1;
    short shI;

    *ret = (char *) calloc(MAX_RAND_LENGTH+1, sizeof(char));
    *ret2 = (char *) calloc(MAX_RAND_LENGTH+1, sizeof(char));

    if (strstr(typ, "char")) {
	GetRandStr(1, ret2, &randI);
	if (randI == '\\')
	    strcpy(*ret2, "'\\\\'");
	else if(randI == '\'')
	    strcpy(*ret2, "'\\''");
	else
	    sprintf(*ret, "'%c'", randI);
	    return;
    } else if (strstr(typ, "short")) {
	shI = SHRT_MIN + (SHRT_MAX - SHRT_MIN) * drand32();
	sprintf(*ret, "%d", shI);
	strcpy(*ret2, *ret);
    } else if (strstr(typ, "afs_int32")) {
	randI = INT_MIN + UINT_MAX * drand32();
	if (randI < INT_MIN || randI > INT_MAX) {
	    fprintf(stderr, "GetRandP: afs_int32 %d out of bounds\n", randI);
	    exit(1);
	}
	sprintf(*ret, "%d", randI);
	strcpy(*ret2, *ret);
    } else if (strstr(typ, "float")) {
	d = drand32();
	f = d;
	sprintf(*ret, "%2.8e", f);
	strcpy(*ret2, *ret);

    } else if (strstr(typ, "double")) {
	d1 = drand32();
	sprintf(*ret, "%2.16e", d1);
	strcpy(*ret2, *ret);

    } else if (strstr(typ, "String")) {
	strLen = (MAX_RAND_LENGTH-2)*drand32();
	GetRandStr(strLen, ret, &randI);
	strcpy(*ret2, *ret);
    }
}


/*
 * GenParamValues -- generate the parameter values for an rpc
 */
PRIVATE void GenParamValues(rpcArgs *argsP)
{
    char *typ;
    int i, j;

    /* generate random values for each argument */
    for (i=0; i < argsP->argCount; i++) {

	argsP->argDescr[i].vc_low = 0;
	argsP->argDescr[i].vc_max = IDL_FIX_ARRAY_SIZE-1;
	argsP->argDescr[i].vc_high = IDL_FIX_ARRAY_SIZE-1;
	argsP->argDescr[i].ovc_low = 0;
	argsP->argDescr[i].ovc_high = IDL_FIX_ARRAY_SIZE-1;

	if (!strncmp(argsP->argDescr[i].type, "ar_", 3)) {
	    typ = argsP->argDescr[i].type + 3;
	    for (j = 0 ; j < IDL_FIX_ARRAY_SIZE ; j++) {
		GetRandP(typ, &argsP->argDescr[i].inValue[j],
			 &argsP->argDescr[i].inValue2[j]);
		GetRandP(typ, &argsP->argDescr[i].outValue[j],
			 &argsP->argDescr[i].outValue2[j]);
	    }
	} else {
	    GetRandP(argsP->argDescr[i].type,
		     &argsP->argDescr[i].inValue[0],
		     &argsP->argDescr[i].inValue2[0]);
	    GetRandP(argsP->argDescr[i].type,
		     &argsP->argDescr[i].outValue[0],
		     &argsP->argDescr[i].outValue2[0]);
	}
    }
}

/*
 * WriteServC -- write the rpcs in the server file
 */
PRIVATE void WriteServC(rpcArgs *argsP, FILE *srv_h, char *serverName, int sign_no)
{
    char *name, *typ;
    int i,j;

    name = GetName(serverName, sign_no);
    fprintf(srv_h, "\nint A%s(struct rx_call *c,\n", name);

    /* declare each arg */
    for (i=0; i < argsP->argCount; i++) {

        if (!strcmp(argsP->argDescr[i].type, "varString")) {
	    if (!strcmp(argsP->argDescr[i].direction, "IN")) {
		fprintf(srv_h, "\tchar *a%d", i);
	    }
	    else {
		fprintf(srv_h, "\tchar **a%d", i);
	    }
	}
	else if (strncmp(argsP->argDescr[i].type, "ar_", 3) &&
	        (!strcmp(argsP->argDescr[i].direction, "IN"))) {
	    fprintf(srv_h, "\tdrpc_%s_t a%d", argsP->argDescr[i].type, i);
	} else {
	    if (!strncmp(argsP->argDescr[i].type, "ar_", 3)) {
		fprintf(srv_h, "\tdrpc_out_%s_t *a%d", argsP->argDescr[i].type, i);
	    }
	    else {
		fprintf(srv_h, "\tdrpc_%s_t *a%d", argsP->argDescr[i].type, i);
	    }
	}
	if(i < (argsP->argCount - 1)) {
	    fprintf(srv_h, ",\n");
	}
    }
    fputs(")\n{\n", srv_h); /* balance the } */
    fputs("\tint errFlag = 0;", srv_h);

    /*
     * check the in and inout parameters
     */
    for (i=0; i < argsP->argCount; i++) {

	if (!strcmp(argsP->argDescr[i].direction, "IN") ||
		   (!strcmp(argsP->argDescr[i].direction, "INOUT") &&
		    !strcmp(argsP->argDescr[i].type, "varString"))) {

	    typ = argsP->argDescr[i].type;

	    if (strstr(typ, "String")) {
		if(!strcmp(argsP->argDescr[i].direction, "INOUT")) {
		    fprintf(srv_h, "\n\tCHECK%s((char *)*a%d, %s, \"%s\");",
			    typ, i, argsP->argDescr[i].inValue[0], name);
		} else {
		    fprintf(srv_h, "\n\tCHECK%s((char *)a%d, %s, \"%s\");",
			    typ, i, argsP->argDescr[i].inValue[0], name);
		}
	    } else if (!strcmp(typ, "afs_int32")) {
		fprintf(srv_h, "\n\tCHECK%s(a%d, %sL, \"%s\");",
			typ, i, argsP->argDescr[i].inValue[0], name);
	    } else {
		if(!strncmp(typ, "ar_", 3)) {
		    for(j=0;j<IDL_FIX_ARRAY_SIZE;j++)
			fprintf(srv_h, "\n\tCHECK%s(a%d->drpc_out_%s_t_val[%d], %s, \"%s\");",
				(typ+3), i, typ, j, argsP->argDescr[i].inValue[j],
				name);
		}
		else {
		    fprintf(srv_h, "\n\tCHECK%s(a%d, %s, \"%s\");",
			    typ, i, argsP->argDescr[i].inValue[0], name);
		}
	    }
	} else if (!strcmp(argsP->argDescr[i].direction, "INOUT")) {

	    typ = argsP->argDescr[i].type;

	    if (strstr(typ, "String")) {
		fprintf(srv_h, "\n\tCHECK%s((char *)*a%d, %s, \"%s\");",
			typ, i, argsP->argDescr[i].inValue[0], name);
	    } else if (!strcmp(typ, "afs_int32")) {
		fprintf(srv_h, "\n\tCHECK%s(*a%d, %sL, \"%s\");",
			typ, i, argsP->argDescr[i].inValue[0], name);
	    } else {
		if(!strncmp(typ, "ar_", 3)) {
		    for(j=0;j<IDL_FIX_ARRAY_SIZE;j++)
			fprintf(srv_h, "\n\tCHECK%s(a%d->drpc_out_%s_t_val[%d], %s, \"%s\");",
				(typ+3), i, typ, j, argsP->argDescr[i].inValue[j],
				name);
		}
		else {
		    fprintf(srv_h, "\n\tCHECK%s(*a%d, %s, \"%s\");",
			    typ, i, argsP->argDescr[i].inValue[0], name);
		}
	    }
	}
    }

    /*
     * free up memory for dynamic input args
     */
    for (i=0; i < argsP->argCount; i++) {
	if (!strncmp(argsP->argDescr[i].type, "ar_", 3)) {
	    typ = argsP->argDescr[i].type;
	    if(!strcmp(argsP->argDescr[i].direction, "INOUT")) {
		fprintf(srv_h, "\n\tif (a%d->drpc_out_%s_t_val) "
			"free(a%d->drpc_out_%s_t_val);", i, typ, i, typ);
	    }
	    if(!strcmp(argsP->argDescr[i].direction, "INOUT") ||
	       !strcmp(argsP->argDescr[i].direction, "OUT")) {
		fprintf(srv_h, "\n\ta%d->drpc_out_%s_t_val = (%s *) "
			"malloc(FIX_ARRAY_SIZE * sizeof(%s));",
			i, typ, (typ+3), (typ+3));
		fprintf(srv_h, "\n\ta%d->drpc_out_%s_t_len = FIX_ARRAY_SIZE;",
			i, typ);
	    }
	}
    }

    /*
     * set the inout and out parameters
     */
    for (i=0; i < argsP->argCount; i++) {

	if (!strcmp(argsP->argDescr[i].direction, "INOUT") ||
		   !strcmp(argsP->argDescr[i].direction, "OUT")) {

	    typ = argsP->argDescr[i].type;

	    if (!strncmp(typ, "ar_", 3)) {
		for(j=0;j<IDL_FIX_ARRAY_SIZE;j++)
		    fprintf(srv_h, "\n\ta%d->drpc_out_%s_t_val[%d] = %s;",
			    i, typ, j, argsP->argDescr[i].outValue[j]);
	    } else if (!strcmp(typ, "afs_int32")) {
		fprintf(srv_h, "\n\t*a%d = %sL;",
			i, argsP->argDescr[i].outValue[0]);
	    } else if (!strcmp(typ, "varString")) {
		if(!strcmp(argsP->argDescr[i].direction, "OUT")) {
		    fprintf(srv_h, "\n\t*a%d = (char *) malloc(STR_MAX);", i);
		}
		fprintf(srv_h, "\n\tstrcpy((char *)*a%d, %s);",
			i, argsP->argDescr[i].outValue[0]);
	    } else if (strstr(typ, "String")) {
		fprintf(srv_h,
			"\n\t*a%d = (drpc_%s_t)rpc_ss_allocate(%d);",
			i, typ, strlen(argsP->argDescr[i].outValue[0])-1);
		fprintf(srv_h, "\n\tstrcpy((char *)*a%d, %s);",
			i, argsP->argDescr[i].outValue[0]);
	    } else {
		fprintf(srv_h, "\n\t*a%d = %s;",
			i, argsP->argDescr[i].outValue[0]);
	    }
	}
    }

    fprintf(srv_h,
	    "\n\tif (errFlag) {"
	    "\n\t\tprintf(\"%s: failed\\n\");"
	    "\n\t\tfflush(stdout);"
	    "\n\t}"
	    "\n\treturn errFlag;\n}\n",
	    name);

    /*
     * second routine with arguments inside a structure
     */
    fprintf(srv_h, "\nint A%s_s(struct rx_call *c,\n\t%s_t *s)\n{"
		    "\n\tint errFlag = 0;", name, name);

    /*
     * check the in and inout parameters
     */
    for (i=0; i < argsP->argCount; i++) {

	if (!strcmp(argsP->argDescr[i].direction, "IN") ||
		   (!strstr(argsP->argDescr[i].type, "String") &&
		    !strcmp(argsP->argDescr[i].direction, "INOUT"))) {

	    typ = argsP->argDescr[i].type;

	    if(!strncmp(typ, "ar_", 3)) {
		for(j=0;j<IDL_FIX_ARRAY_SIZE;j++)
		    fprintf(srv_h, "\n\tCHECK%s(s->a%d[%d], %s, \"%s_s\");",
			    (typ+3), i, j, argsP->argDescr[i].inValue[j],
			    name);
	    }
	    else if (strstr(typ, "String")) {
		fprintf(srv_h, "\n\tCHECK%s((char *)s->a%d, %s, \"%s_s\");",
			typ, i, argsP->argDescr[i].inValue[0], name);
	    } else if (!strcmp(typ, "afs_int32")) {
		fprintf(srv_h, "\n\tCHECK%s(s->a%d, %sL, \"%s_s\");",
			typ, i, argsP->argDescr[i].inValue[0], name);
	    } else {
		fprintf(srv_h, "\n\tCHECK%s(s->a%d, %s, \"%s_s\");",
			typ, i, argsP->argDescr[i].inValue[0], name);
	    }
	}
    }

    /*
     * set the inout and out parameters
     */
    for (i=0; i < argsP->argCount; i++) {

	if (!strcmp(argsP->argDescr[i].direction, "INOUT") ||
		   !strcmp(argsP->argDescr[i].direction, "OUT")) {

	    typ = argsP->argDescr[i].type;

	    if(!strncmp(typ, "ar_", 3)) {
		for(j=0;j<IDL_FIX_ARRAY_SIZE;j++)
		    fprintf(srv_h, "\n\ts->a%d[%d]= %s;",
			    i, j, argsP->argDescr[i].outValue[j]);
	    }
	    else if (!strcmp(typ, "afs_int32")) {
		fprintf(srv_h, "\n\ts->a%d = %sL;",
			i, argsP->argDescr[i].outValue[0]);
	    } else if (!strcmp(typ, "varString")) {
		fprintf(srv_h, "\n\tstrcpy((char *)s->a%d, %s);",
			i, argsP->argDescr[i].outValue[0]);
	    } else if (strstr(typ, "String")) {
		fprintf(srv_h, "\n\ts->a%d = rpc_ss_allocate(%d);",
			i, strlen(argsP->argDescr[i].outValue[0])-1);
		fprintf(srv_h, "\n\tstrcpy((char *)s->a%d, %s);",
			i, argsP->argDescr[i].outValue[0]);
	    } else {
		fprintf(srv_h, "\n\ts->a%d = %s;",
			i, argsP->argDescr[i].outValue[0]);
	    }
	}
    }

    fprintf(srv_h,
	    "\n\tif (errFlag) {"
	    "\n\t\tprintf(\"%s_s: failed\\n\");"
	    "\n\t}"
	    "\n\tfflush(stdout);"
	    "\n\treturn errFlag;\n}\n",
	    name);


    free(name);
}

/*
 * WriteMake - write the makefile for a particular client/server pair
 */
PRIVATE void WriteMake(char *serverName, int srv_no, FILE *mak_h)
{
    char p[1024];
    char *obj=platform[2];

    sprintf(p,"%s%d", serverName, srv_no);
    fprintf(mak_h, "%s", platform[1]);
    if (threadModel == PTHREADS) {
	fprintf(mak_h, 
		"CFLAGS = -KPIC -g -mt -I$(DESTDIR)include\n"
		"%s",
		platform[6]
		);
    } else {
	fprintf(mak_h, 
		"CFLAGS = -g -I$(DESTDIR)include\n"
		"%s",
		platform[7]
		);
    }
    fprintf(mak_h,
	    "\n\ntest: all\ntests: all\nall: %sClt %sSrv\n\n"
	    "%sClt: %s.cs.%s %s.xdr.%s %sClt.%s $(LIBS)\n",
	    p,p,p,p,obj,p,obj,p,obj
	    );
    if(platform == nt_symbols) {
	fprintf(mak_h, "\t$(EXECONLINK)\n\n");
    } else {
	fprintf(mak_h,
		"\t$(CC) $(CFLAGS) -o $@ %sClt.%s %s.cs.%s %s.xdr.%s "
		"$(LIBS) $(XLIBS)\n\n",
		p,obj,p,obj,p,obj
		);
    }
    fprintf(mak_h,
	    "%s.xdr.c: %s.xg\n\n"
	    "%s.h %s.cs.c %s.ss.c: %s.xg\n"
	    "\t$(RXGEN) %s.xg\n"
	    "\t%s %s.cs.c\n"
	    "\t%s %s.ss.c\n\n",
	    p,p,p,p,p,p,p,platform[3],p,platform[3],p);
    fprintf(mak_h,
	    "%sSrv: %s.ss.%s %s.xdr.%s %sSrv.%s $(LIBS)\n",
	    p,p,obj,p,obj,p,obj);

    if(platform == nt_symbols) {
	fprintf(mak_h, "\t$(EXECONLINK)\n\n");
    } else {
	fprintf(mak_h,
		"\t$(CC) $(CFLAGS) -o $@ %sSrv.o %s.ss.o %s.xdr.o "
		"$(LIBS) $(XLIBS)\n\n",
		p,p,p
		);
    }

    if(platform == unix_symbols) {
	fprintf(mak_h,
		"clean:\n"
		"\t/bin/rm -f %sClt %s.h %s.cs.c %s.ss.c core "
		"%sSrv %sSrv.o \\\n"
		"\t%sClt.o %s.cs.o %s.ss.o %s.xdr.c %s.xdr.o\n",
		p,p,p,p,p,p,p,p,p,p,p);
    }

}

/*
 * WriteCltTrailer - finish writing client source
 */
PRIVATE void WriteCltTrailer(char *serverName, int first, int last, FILE *itl_h)
{
    int i=0;

    /*
     * Write out 1 function that calls all the manager functions
     */
    if (threadModel == PTHREADS) {
	fprintf(itl_h, "\n\nvoid *threadFunc(void *arg) {\n");
    } else {
	fprintf(itl_h, "\n\nint threadFunc(int *arg) {\n");
    }

    fprintf(itl_h, 
	    "\tstruct rx_connection *conn;\n"
	    "\tint error=0;\n\n"
	    "\tint i = (int) arg;\n\n"
	    );

    fprintf(itl_h, "\tconn = rx_GetCachedConnection(serverAddr,serverPort,4,secClass,secIndex);\n");

    fprintf(itl_h, "\twhile(i--) {\n");

    if (first == 0) first = 1;
    for(i=first;i<last;i++) {
	fprintf(itl_h, "\t\tC_%s%d(conn);\n",serverName, i);
	fprintf(itl_h, "\t\tC_%s%d_s(conn);\n",serverName, i);
    }
    fprintf(itl_h, "\t}\n");

    fprintf(itl_h, "\trx_ReleaseCachedConnection(conn);\n");

    if (threadModel == PTHREADS) {
	fprintf(itl_h, "\treturn (void *) 0;\n");
    } else {
	fprintf(itl_h, 
	    "\tLWP_CALL(LWP_SignalProcess,(&done));\n"
	    "\treturn 0;\n"
	    );
    }

    fprintf(itl_h, "}\n");

    fprintf(itl_h,
	    "\n\nstatic void DoRun(struct cmd_syndesc *as, char *arock) {\n"
	    );

    if (threadModel == PTHREADS) {
	fprintf(itl_h, "\tpthread_t *tid;\n");
    } else {
	fprintf(itl_h, "\tPROCESS *tid;\n");
    }
    
    fprintf(itl_h,
	    "\tint i,j;\n"
	    "\tunsigned int numThreads=0,numIterations=0;\n"
	    "\tint errflg=0;\n"
	    "\tstruct hostent *host=NULL;\n\n"
	    "\tnumThreads = atoi(as->parms[0].items->data);\n"
	    "\tnumIterations = atoi(as->parms[1].items->data);\n"
	    "\tsecType = atoi(as->parms[2].items->data);\n"
	    "\tencLevel = atoi(as->parms[3].items->data);\n"
	    "\tserverPort = htons(atoi(as->parms[4].items->data));\n"
	    "\tif ((host = hostutil_GetHostByName(as->parms[5].items->data))==0) {\n"
	    "\t\terrflg++;\n"
	    "\t}\n"
	    "\telse {\n"
	    "\t\tmemcpy((void *) &serverAddr, (const void *)host->h_addr, sizeof(serverAddr));\n"
	    "\t}\n"
	    "\tif ((secType<1) || (secType>2)) errflg++;\n"
	    "\tif ((encLevel<0) || (encLevel>2)) errflg++;\n"
	    "\tif ((serverPort == 0) ||(numThreads == 0) ||(numIterations == 0)) errflg++;\n"
	    "\tif (errflg) {\n"
	    "\t\tprintf(\"invalid argument\\n\");\n"
	    "\t\texit(1);\n"
	    "\t}\n\n"
	    "\tif(rx_Init(htons(0)) < 0) {\n"
	    "\t\texit(1);\n"
	    "\t}\n\n"
	    "\tswitch(secType) {\n"
	    "\t\tcase 1:\n"
	    "\t\t\tsecIndex = 0;\n"
	    "\t\t\tsecClass = rxnull_NewClientSecurityObject();\n"
	    "\t\t\tbreak;\n"
	    "\t\tcase 2:\n"
	    "\t\t\tif (GetTicket (&kvno, &Ksession, &ticketLen, ticket) == 0) {\n"
	    "\t\t\t\tsecIndex = 2;\n"
	    "\t\t\t\tsecClass = rxkad_NewClientSecurityObject(encLevel,\n"
	    "\t\t\t\t&Ksession, kvno, ticketLen, ticket);\n"
	    "\t\t\t}\n"
	    "\t\t\tbreak;\n"
	    "\t\tdefault:\n"
	    "\t\t\tbreak;\n"
	    "\t}\n"
	    "\tassert(secClass);\n\n"
	    );
    if (threadModel == PTHREADS) {
	fprintf(itl_h,
		"#if defined sun\n"
		"\tthr_setconcurrency(numThreads);\n"
		"#endif\n"
		"\ttid = (pthread_t *) "
		"malloc(numThreads*(sizeof(pthread_t)));\n"
		);
    } else {
	fprintf(itl_h,
		"\ttid = (PROCESS *) malloc(numThreads*(sizeof(PROCESS *)));\n"
		);
    }

    fprintf(itl_h,
	    "\tassert(tid);\n"
	    "\tfor(j=0;j<numThreads;j++) {\n"
	    );
    if (threadModel == PTHREADS) {
	fprintf(itl_h,
		"\t\ti = pthread_create(&tid[j], (const pthread_attr_t *) 0,\n"
		"\t\tthreadFunc, (void *) numIterations);\n"
		"\t\tassert(i==0);\n"
		);
    } else {
	fprintf(itl_h,
		"\t\tLWP_CALL(LWP_CreateProcess,(threadFunc, 10*4096, LWP_NORMAL_PRIORITY, numIterations, \"foo\", &tid[j]));\n"
		);
    }

    fprintf(itl_h,
	    "\t}\n"
	    "\tfor(j=0;j<numThreads;j++) {\n"
	    );
    if (threadModel == PTHREADS) {
	fprintf(itl_h, 
		"\t\ti = pthread_join(tid[j], (void **) 0);\n"
		"\t\tassert(i==0);\n"
		);
    } else {
	fprintf(itl_h, "\t\tLWP_CALL(LWP_WaitProcess,(&done));\n");
    }
    fprintf(itl_h,
	    "\t}\n"
	    "\trx_Finalize();\n"
	    "\tfree(tid);\n"
	    "}\n"
	    );

    /*
     * Write command syntax function
     */

    fprintf(itl_h,
	    "static void SetupRunCmd(void) {\n"
	    "\tstruct cmd_syndesc *ts;\n"
	    "\tts = cmd_CreateSyntax((char *) 0,DoRun, 0, \"run the test client program\");\n"
	    "\tcmd_AddParm(ts, \"-threadCount\", CMD_SINGLE, CMD_REQUIRED, \"number of threads to spawn\");\n"
	    "\tcmd_AddParm(ts, \"-iterationCount\", CMD_SINGLE, CMD_REQUIRED, \"number of iterations to make over entire interface for each thread\");\n"
	    "\tcmd_AddParm(ts, \"-secType\", CMD_SINGLE, CMD_REQUIRED, \"security level to use (1 -> unauthenticated, 2 -> authenticated)\");\n"
	    "\tcmd_AddParm(ts, \"-encLevel\", CMD_SINGLE, CMD_REQUIRED, \"encryption level to use, (0-> rxkad_clear, 1 -> rxkad_auth, 2 -> rxkad_crypt)\");\n"
	    "\tcmd_AddParm(ts, \"-serverPort\", CMD_SINGLE, CMD_REQUIRED, \"port that server is using\");\n"
	    "\tcmd_AddParm(ts, \"-serverHost\", CMD_SINGLE, CMD_REQUIRED, \"host where server is running\");\n"
	    "}\n"
	    );

    /*
     * Write client main
     */

    fprintf(itl_h,
	    "int main (int argc, char *argv[]) {\n"
	    "\tint code;\n\n"
	    "\tinitialize_cmd_error_table();\n"
	    "\tSetupRunCmd();\n"
	    "\tcode = cmd_Dispatch(argc, argv);\n\n"
	    "\treturn(code);\n"
	    "\t}\n"
	    );

}

/*
 * Create macros useful for checking input/output parameters
 */

PRIVATE void WriteTestMacros(FILE *f) {
    fprintf(f,
	"\n#define CHECKchar(x,y,z) if ((x)!=(y)) { printf(\"%%s: Got '%%c',"
	"expected '%%c'\\n\", z, (char)x, (char)(y)); fflush(stdout); "
	"errFlag = 1; } "
	"\n#define CHECKshort(x,y,z) if ((x)!=(y)) "
	"{ printf(\"%%s: Got 0x%%04x, "
	"expected 0x%%04x\\n\", z, x, y); fflush(stdout); "
	"errFlag = 1; } "
	"\n#define CHECKint32(x,y,z) if ((x)!=(y)) "
	"{ printf(\"%%s: Got 0x%%08x, "
	"expected 0x%%08x\\n\", z, x, y); fflush(stdout); "
	"errFlag = 1; } "
	"\n#define CHECKfloat(x,y,z) if (((x)/(y)<.999999) "
	"|| ((x)/(y)>1.000001)) "
	"{ printf(\"%%s: Got %%2.8e, expected %%2.8e\\n\", z, x, y); "
	"fflush(stdout); "
	"errFlag = 1; } "
	"\n#define CHECKdouble(x,y,z) if (((x)/(y)<.999999999999999) || "
	"((x)/(y)>1.000000000000001)) { printf(\"%%s: Got %%2.16e, "
	"expected %%2.16e\\n\", z, x, y); fflush(stdout); errFlag = 1; }"
	"\n#define CHECKvarString(x,y,z) if (strcmp((x),(y))) { printf(\"%%s: "
	"Got \\\"%%s\\\", expected \\\"%%s\\\"\\n\", z, x, y); fflush(stdout); "
	"errFlag = 1; } \n");
}

/*
 * WriteCltHeader - write the begining of the client code.
 */
PRIVATE void WriteCltHeader(char *serverName, int srv_no, FILE *itl_h)
{
    fprintf(itl_h, "#include <stdio.h>\n");
    if (threadModel == PTHREADS) {
	    fprintf(itl_h,"#include <pthread.h>\n");
    }
    fprintf(itl_h,
	    "%s"
	    "#include <assert.h>\n"
	    "#include <rx/rx.h>\n"
	    "#include <rx/rx_null.h>\n"
	    "#include <rx/rxkad.h>\n"
	    "#include <afs/cmd.h>\n"
	    "#include \"../../../permit_xprt.h\"\n"
	    "#include \"%s%d.h\"\n"
	    ,
	    platform[4], serverName, srv_no);
    if (threadModel == LWPS) {
	fprintf(itl_h,
	    "\n\n#define LWP_CALL(fnName, params) \\\n"
	    "{ \\\n"
	    "\tint rc; \\\n"
	    "\trc = fnName params; \\\n"
	    "\tif (rc != LWP_SUCCESS) { \\\n"
	    "\t\tprintf(\"call to %%s failed with %%d\\n\", # fnName, rc); \\\n"
	    "\t\texit(1); \\\n"
	    "\t} \\\n"
	    "};\n\n"
	    "char done;\n\n");
    }

    fprintf(itl_h,
	    "struct ktc_encryptionKey serviceKey =\n"
	    "\t{0x45, 0xe3, 0x3d, 0x16, 0x29, 0x64, 0x8a, 0x8f};\n"
	    "long serviceKeyVersion = 7;\n"
	    "\nextern struct hostent *hostutil_GetHostByName();\n\n"
	    "static long GetTicket (long *versionP,\n"
	    "\tstruct ktc_encryptionKey *session,\n"
	    "\tint * ticketLenP, char *ticket)\n"
	    "{\n"
	    "\tlong code;\n"
	    "\tdes_init_random_number_generator (&serviceKey);\n"
	    "\tcode = des_random_key (session);\n"
	    "\tif (code) return code;\n"
	    "\t*ticketLenP = 0;\n"
	    "\tcode = tkt_MakeTicket(ticket, ticketLenP, &serviceKey,\n"
	    "\t\t\"tclnt\", \"\", \"\",\n"
	    "\t\t0, 0xffffffff, session, 0,\n"
	    "\t\t\"tsrvr\", \"\");\n"
	    "\tif (code) return code;\n"
	    "\t*versionP = serviceKeyVersion;\n"
	    "\treturn 0;\n"
	    "}\n\n"
	    "static int secType,encLevel,serverPort,serverAddr;\n"
	    "struct rx_securityClass *secClass;\n"
	    "int secIndex;\n"
	    "long kvno;\n"
	    "char ticket[MAXKTCTICKETLEN];\n"
	    "int ticketLen;\n"
	    "struct ktc_encryptionKey Ksession;\n\n"
	    );
    WriteTestMacros(itl_h);

}

/*
 * WriteCltInit -- write the initialization for each param
 */
PRIVATE void WriteCltInit(arg_tuple *arg, FILE *itl_h, int i, 
			  int structFlag)
{
    int j;
    char *s = (structFlag) ? "s." : "";

    if ((!strncmp(arg->direction, "IN", 2) || structFlag)) {
	if (!strncmp(arg->type, "varString", 9)) {
	    fprintf(itl_h, "\tstrcpy(%sa%d, %s);\n", s, i, arg->inValue[0]);
	}
	else if (!strncmp(arg->type, "ar_", 3)) {
	    if(!structFlag) {
		fprintf(itl_h, "\t%sa%d.drpc_out_%s_t_len = FIX_ARRAY_SIZE;\n",
			s, i, arg->type);
		fprintf(itl_h, 
			"\t%sa%d.drpc_out_%s_t_val = "
			"(%s *) malloc(FIX_ARRAY_SIZE * sizeof(%s));\n",
			s, i, arg->type, (arg->type + 3), (arg->type + 3));
	    }
	    for(j=0;j<IDL_FIX_ARRAY_SIZE;j++) {
		if(structFlag) {
		    fprintf(itl_h, 
			    "\t%sa%d[%d] = %s;\n",
			    s, i, j,arg->inValue[j]);
		}
		else {
		    fprintf(itl_h, 
			    "\t%sa%d.drpc_out_%s_t_val[%d] = %s;\n",
			    s, i, arg->type,j,arg->inValue[j]);
		}
	    }
	}
	else {
	    fprintf(itl_h, "\t%sa%d = %s;\n", s, i, arg->inValue[0]);
	}
    }
}

/*
 * WriteCltCall -- write each parameter for a call
 */
PRIVATE void WriteCltCall(arg_tuple *arg, FILE *itl_h, int i) {

    if (!strncmp(arg->type, "varString", 9)) {
	if ((!strncmp(arg->direction, "OUT", 3)) ||
	    (!strncmp(arg->direction, "INOUT", 5))) {
	    fprintf(itl_h, "&a%d_p", i);
	}
	else {
	    fprintf(itl_h, "a%d_p", i);
	}
    }
    else {
	if ((!strncmp(arg->direction, "OUT", 3)) ||
	    (!strncmp(arg->direction, "INOUT", 5)) || 
	    (!strncmp(arg->type, "ar_", 3))) {
	    fprintf(itl_h, "&a%d", i);
	}
	else {
	    fprintf(itl_h, "a%d", i);
	}
    }
}

/*
 * WriteCltDecl -- write the declaration for a parameter
 */
PRIVATE void WriteCltDecl(arg_tuple *arg, FILE *itl_h, int i) {

    if (!strncmp(arg->type, "ar_", 3)) {
	if (!strncmp(arg->direction, "OUT", 3)) {
	    fprintf(itl_h, "\tdrpc_out_%s_t a%d = {0,0};\n", arg->type, i);
	}
	else {
	    fprintf(itl_h, "\tdrpc_out_%s_t a%d;\n", arg->type, i);
	}
    }
    else if (!strncmp(arg->type, "varString", 9)) {
	fprintf(itl_h, "\tchar a%d[STR_MAX];\n", i);
	fprintf(itl_h, "\tchar *a%d_p = a%d;\n", i, i);
    }
    else {
	fprintf(itl_h, "\t%s a%d;\n", arg->type, i);
    }
}


/*
 * WriteClt -- write the rpcs in the client
 */
PRIVATE void WriteClt(rpcArgs *argsP, char *serverName, int sign_no, FILE *itl_h)
{
    char *name, *name2, *typ;
    int i,j;

    name = GetName(serverName, sign_no);
    name2 = GetDescription(argsP);

    fprintf(itl_h, "\n/* %s */\n\n", name2);

    fprintf(itl_h, "\nvoid C_%s(struct rx_connection *conn) {\n", name);

    /* declare each arg */
    for (i=0; i < argsP->argCount; i++) {
	WriteCltDecl(&argsP->argDescr[i], itl_h, i);
    }
    fprintf(itl_h, "\tint error;\n\tint errFlag;\n\n");

    /* initialize IN/INOUT args */
    for (i=0; i < argsP->argCount; i++) {
	WriteCltInit(&argsP->argDescr[i], itl_h, i, FALSE);
    }

    /* call the server */
    fprintf(itl_h, "\terror = A%s(conn, ", name);

    /* pass each arg */
    for (i=0; i < argsP->argCount; i++) {
	WriteCltCall(&argsP->argDescr[i], itl_h, i);
	fprintf(itl_h, ((i < (argsP->argCount -1))) ? "," : ");\n");
    }

    fprintf(itl_h, 
	    "\tif (error != 0) {\n"
	    "\t\tprintf(\"C_%s failed %%d\\n\", error);\n"
	    "\t\tfflush(stdout);\n"
	    "\t\treturn;\n"
	    "\t}\n",
	    name);

    /*
     * check the in and inout parameters
     */
    for (i=0; i < argsP->argCount; i++) {

	if ((!strcmp(argsP->argDescr[i].direction, "OUT")) ||
		    (!strcmp(argsP->argDescr[i].direction, "INOUT"))) {

	    typ = argsP->argDescr[i].type;

	    if(!strncmp(typ, "ar_", 3)) {
		for(j=0;j<IDL_FIX_ARRAY_SIZE;j++)
		    fprintf(itl_h, "\n\tCHECK%s(a%d.drpc_out_%s_t_val[%d], %s, \"C_%s_s\");",
			    (typ+3), i, typ, j, argsP->argDescr[i].outValue[j],
			    name);
	    }
	    else if (strstr(typ, "String")) {
		fprintf(itl_h, "\n\tCHECK%s((char *)a%d, %s, \"C_%s_s\");",
			typ, i, argsP->argDescr[i].outValue[0], name);
	    } else if (!strcmp(typ, "afs_int32")) {
		fprintf(itl_h, "\n\tCHECK%s(a%d, %sL, \"C_%s_s\");",
			typ, i, argsP->argDescr[i].outValue[0], name);
	    } else {
		fprintf(itl_h, "\n\tCHECK%s(a%d, %s, \"C_%s_s\");",
			typ, i, argsP->argDescr[i].outValue[0], name);
	    }
	}
    }

    /*
     * free up memory for dynamic input args
     */
    for (i=0; i < argsP->argCount; i++) {
	if (!strncmp(argsP->argDescr[i].type, "ar_", 3)) {
	    typ = argsP->argDescr[i].type;
	    fprintf(itl_h, "\n\tif (a%d.drpc_out_%s_t_val) "
		    "free(a%d.drpc_out_%s_t_val);", i, typ, i, typ);
	}
    }


    fprintf(itl_h, "\n}\n");

    /*
     * Call the structure oriented rpc interface
     */

    fprintf(itl_h, "\nvoid C_%s_s(struct rx_connection *conn) {\n", name);
    fprintf(itl_h, "\tstruct %s_t s;\n", name);
    fprintf(itl_h, "\tint error;\n\tint errFlag;\n\n");

    /* initialize IN/INOUT args */
    for (i=0; i < argsP->argCount; i++) {
	WriteCltInit(&argsP->argDescr[i], itl_h, i, TRUE);
    }

    /* call the server */
    fprintf(itl_h, "\terror = A%s_s(conn, &s);\n", name);

    fprintf(itl_h, 
	    "\tif (error != 0) {\n"
	    "\t\tprintf(\"C_%s_s failed %%d\\n\", error);\n"
	    "\t\tfflush(stdout);\n"
	    "\t\treturn;\n"
	    "\t}\n",
	    name);

    /*
     * check the in and inout parameters
     */
    for (i=0; i < argsP->argCount; i++) {

	if ((!strcmp(argsP->argDescr[i].direction, "OUT")) ||
		    (!strcmp(argsP->argDescr[i].direction, "INOUT"))) {

	    typ = argsP->argDescr[i].type;

	    if(!strncmp(typ, "ar_", 3)) {
		for(j=0;j<IDL_FIX_ARRAY_SIZE;j++)
		    fprintf(itl_h, "\n\tCHECK%s(s.a%d[%d], %s, \"C_%s_s\");",
			    (typ+3), i, j, argsP->argDescr[i].outValue[j],
			    name);
	    }
	    else if (strstr(typ, "String")) {
		fprintf(itl_h, "\n\tCHECK%s((char *)s.a%d, %s, \"C_%s_s\");",
			typ, i, argsP->argDescr[i].outValue[0], name);
	    } else if (!strcmp(typ, "afs_int32")) {
		fprintf(itl_h, "\n\tCHECK%s(s.a%d, %sL, \"C_%s_s\");",
			typ, i, argsP->argDescr[i].outValue[0], name);
	    } else {
		fprintf(itl_h, "\n\tCHECK%s(s.a%d, %s, \"C_%s_s\");",
			typ, i, argsP->argDescr[i].outValue[0], name);
	    }
	}
    }


    fprintf(itl_h, "\n}\n");

    free(name);
    free(name2);
}

/*
 * WriteXGHeader -- fill in Header info in the xg file
 */
PRIVATE void WriteXGHeader(char *serverName, FILE *xg_h, int srv_no)
{

    /* create the interface header */

    fprintf(xg_h,
	    "\n/* Do not edit this file -- it was created by a generator */\n"
	    "\npackage A\n\n"
	    "const STR_MAX = %d; \n"
	    "const FIX_ARRAY_SIZE = %d; \n"
	    "typedef char drpc_char_t; \n"
	    "typedef short drpc_short_t; \n"
	    "typedef long drpc_int32_t; \n"
	    "typedef string drpc_varString_t<STR_MAX>; \n"
	    "typedef drpc_char_t drpc_ar_char_t[FIX_ARRAY_SIZE]; \n"
	    "typedef drpc_char_t drpc_out_ar_char_t<FIX_ARRAY_SIZE>; \n"
	    "typedef drpc_short_t drpc_ar_short_t[FIX_ARRAY_SIZE]; \n"
	    "typedef drpc_short_t drpc_out_ar_short_t<FIX_ARRAY_SIZE>; \n"
	    "typedef drpc_int32_t drpc_ar_int32_t[FIX_ARRAY_SIZE]; \n"
	    "typedef drpc_int32_t drpc_out_ar_int32_t<FIX_ARRAY_SIZE>; \n"
	    ,
	    IDL_STR_MAX, IDL_FIX_ARRAY_SIZE);
}

/*
 * WriteServHeader -- fill in the Header info in the server file
 */
PRIVATE void WriteServHeader(FILE *srv_h, char *serverName, int srv_no)
{
    char *name;

    name = GetName(serverName, srv_no);

    fprintf(srv_h,
	"\n/* Do not edit this file -- it's been created by a generator */\n\n" 
	"%s\n"
	"#include <stdio.h>\n"
	"#include <string.h>\n"
	"#include <stdlib.h>\n"
	"#include <rx/rx.h>\n"
	"#include <rx/rx_null.h>\n"
	"#include <rx/rxkad.h>\n"
	"#include <afs/cmd.h>\n"
	"#include \"../../../permit_xprt.h\"\n"
	"#include \"%s.h\"\n\n"
	"struct ktc_encryptionKey serviceKey =\n"
	"\t{0x45, 0xe3, 0x3d, 0x16, 0x29, 0x64, 0x8a, 0x8f};\n"
	"long serviceKeyVersion = 7;\n\n"
	"extern int AExecuteRequest();\n"
	"extern char *optarg;\n"
	"extern int optind, opterr, optopt;\n\n"
	"#define VERIFY(x,y) if (!(x)) { printf y ; exit(1); }\n"
	"#define CHECK(x) ASSERT(x)\n"
	,
	platform[4], name);

    WriteTestMacros(srv_h);
    free(name);
}

/*
 * WriteServTrailer -- finish up the server file
 */
PRIVATE void WriteServTrailer(FILE *srv_h) {

    fprintf(srv_h, 
	    "\nstatic long GetKey (char *rock, long kvno, struct ktc_encryptionKey *key) {\n"
	    "\tmemcpy ((void *) key, (void *) &serviceKey, sizeof(*key));\n"
	    "\treturn 0;\n"
	    "}\n\n"

	    "static void DoRun(struct cmd_syndesc *as, char *arock) {\n"
	    "\tstruct rx_service *serv;\n"
	    "\tstruct rx_securityClass *sc[3];\n\n"
	    "\tint port=0, errflg=0;\n"

	    "\tint lowThreads=4, highThreads=8;\n"
	    "\tlowThreads = atoi(as->parms[0].items->data);\n"
	    "\thighThreads = atoi(as->parms[1].items->data);\n"
	    "\tport = atoi(as->parms[2].items->data);\n"

	    "\tif (port == 0) errflg++;\n"

	    "\tif (errflg) {\n"
	    "\t\tprintf(\"invalid argument\\n\");\n"
	    "\t\texit(1);\n"
	    "\t}\n"

	    "\trx_Init(htons(port));\n"
	    "\tsc[0] = rxnull_NewServerSecurityObject();\n"
	    "\tsc[1] = 0;\n"
	    "\tsc[2] = rxkad_NewServerSecurityObject (rxkad_clear, 0, GetKey, 0);\n"
	    "\tif (serv = rx_NewService(0,4,\"foo\",sc,3,AExecuteRequest)) {\n"
	    "\t\trx_SetMinProcs(serv,lowThreads);\n"
	    "\t\trx_SetMaxProcs(serv,highThreads);\n"
	    "\t\trx_StartServer(1);\n"
	    "\t}\n"
	    "\texit(0);\n"
            "}\n\n"

	    "static void SetupRunCmd(void) {\n"
	    "\tstruct cmd_syndesc *ts;\n"
	    "\tts = cmd_CreateSyntax((char *) 0,DoRun, 0, \"run the test server program\");\n"
	    "\tcmd_AddParm(ts, \"-lowThreadCount\", CMD_SINGLE, CMD_REQUIRED, \"minimum number of threads to spawn\");\n"
	    "\tcmd_AddParm(ts, \"-highThreadCount\", CMD_SINGLE, CMD_REQUIRED, \"maximum number of threads to spawn\");\n"
	    "\tcmd_AddParm(ts, \"-serverPort\", CMD_SINGLE, CMD_REQUIRED, \"port that server is using\");\n"
	    "}\n"

	    "int main(int argc, char **argv) {\n"
	    "\tint code;\n"
	    "\tinitialize_cmd_error_table();\n"
	    "\tSetupRunCmd();\n"
	    "\tcode = cmd_Dispatch(argc, argv);\n"
	    "\treturn(code);\n"
	    "}\n"
	    );
}

/*
 * ProcessCmdLine -- processes the command line args
 */
PRIVATE void ProcessCmdLine(int argc, char **argv,  char **serverName,
			    char **ipFileName,  char **outputDir)
{
    if (argc == 1) {
	PrintShortUsage;
	exit(1);
    }
    /* command line processing */
    while (--argc > 0) {
	if ((*++argv)[0] == '-') {
	    switch ((*argv)[1]) {

		case 'f':
		case 'F':   /* input table file */
		    *ipFileName = *++argv;
		    --argc;
		    break;
		case 'h': /* display help */
		case 'H':
		    PrintLongUsage;
		    exit(0);
		    break;
		case 'l':
		case 'L':   /* use LWP threads */
		    threadModel = LWPS;
		    break;
		case 's':
		case 'S':   /* serverName */
		    *serverName = *++argv;
		    /* need 8 char file name, and so truncate the server name*/
		    if ((int)strlen(*serverName) > MAX_SERV_NAME)
			*(*serverName+MAX_SERV_NAME) = '\0';		
		    --argc;
		    break;
		case 'o':
		case 'O':
		    *outputDir = *++argv;
		    --argc;
		    break;
		case 'p':
		case 'P':
		     if(strcmp(*++argv,"NT")) platform = unix_symbols;
		    --argc;
		    break;
		default:
		    PrintLongUsage;
		    exit(1);
		    break;
	    }
	} else {
	    PrintShortUsage;
	    exit(1);
	}
    }
    if (!*serverName)
	FATAL("Please set server name using -s switch\n");
}


void main(int argc, char **argv)
{
    FILE *table_h, *srv_h, *xg_h, *clt_h, *mak_h;
    int i, j;
    rpcArgs args;
    char *name, *ifName;
    char *serverName = NULL;
    char *ipFileName = NULL;
    char *outputDir = NULL;
    int sign_no=0, start_no, srv_no;

    ProcessCmdLine(argc, argv, &serverName, &ipFileName, &outputDir);

    /* open input file */
    table_h = fopen(ipFileName, "r");
    MEM_CHK(table_h, "main: Unable to open input file\n");

    srv_no = 1;
    start_no = sign_no;

    /*
     * open the first set of output files
     */

    name = GetName(serverName, srv_no);

    srv_h = OpenOutFile(outputDir, name, "Srv.c");
    xg_h = OpenOutFile(outputDir, name, ".xg");
    clt_h = OpenOutFile(outputDir, name, "Clt.c");
    mak_h = OpenOutFile(outputDir, name, ".mak");

    WriteXGHeader(serverName, xg_h, srv_no);
    WriteServHeader(srv_h, serverName, srv_no);
    WriteCltHeader(serverName, srv_no, clt_h);
    WriteMake(serverName, srv_no, mak_h);

    ifName = name;

    /* read the table */
    while (fscanf(table_h, "%d", &(args.argCount)) != EOF) {

	/* increment signature number 8.3 format-- only 10^7 dif sign */
	sign_no++;
	if (sign_no > 1.0e+7)
	    FATAL("Max no: of signatures overflow\n");

	/* allocate for the arg struct */
	args.argDescr = (arg_tuple *)calloc(args.argCount, sizeof(arg_tuple));
	MEM_CHK(args.argDescr, "main: Out of memory -- args.argDescr\n");

	/* pick out the dirs and the types */
	for (i=0; i < args.argCount; i++) {
	    if(!fscanf(table_h, " ( %s %s )", args.argDescr[i].direction,
		       args.argDescr[i].type)) {
		FATAL("main: Incorrect input file format\n");
	    }
	}

	/*
	 * switch files when we hit TESTS_PER_FILE
	 */
	if (sign_no - start_no >= TESTS_PER_FILE) {
	    /*
	     * Finish up the current files
	     */
	    WriteServTrailer(srv_h);
	    WriteCltTrailer(serverName, start_no, sign_no, clt_h);
	    fclose(xg_h);
	    fclose(srv_h);
	    fclose(clt_h);
	    fclose(mak_h);

	    /*
	     * Open the next set of output files
	     */

	    srv_no++;
	    free(ifName);
	    name = GetName(serverName, srv_no);

	    srv_h = OpenOutFile(outputDir, name, "Srv.c");
	    xg_h = OpenOutFile(outputDir, name, ".xg");
	    clt_h = OpenOutFile(outputDir, name, "Clt.c");
	    mak_h = OpenOutFile(outputDir, name, ".mak");
	    WriteXGHeader(serverName, xg_h, srv_no);
	    WriteServHeader(srv_h, serverName, srv_no);
	    WriteCltHeader(serverName, srv_no, clt_h);
	    WriteMake(serverName, srv_no, mak_h);

	    start_no = sign_no;
	    ifName = name;
	}

	/* initialize parameter values */
	for (i=0; i<args.argCount; i++) {
	    for (j = 0 ; j < IDL_FIX_ARRAY_SIZE ; j++) {
		args.argDescr[i].inValue[j] = NULL;
		args.argDescr[i].inValue2[j] = NULL;
		args.argDescr[i].outValue[j] = NULL;
		args.argDescr[i].outValue2[j] = NULL;
	    }
	}
	GenParamValues(&args);

	/* write rpc desc into body of the interface */
	WriteXG(&args, xg_h, serverName, sign_no);

	/* write the rpc into the manager file */
	WriteServC(&args, srv_h, serverName, sign_no);

	/* write out ITL test */
	WriteClt(&args, serverName, sign_no, clt_h);

	/* free saved values */
	for (i=0; i<args.argCount; i++) {
	    for (j = 0 ; j < IDL_FIX_ARRAY_SIZE ; j++) {
		if (args.argDescr[i].inValue[j])
		    free(args.argDescr[i].inValue[j]);
		if (args.argDescr[i].inValue2[j])
		    free(args.argDescr[i].inValue2[j]);
		if (args.argDescr[i].outValue[j])
		    free(args.argDescr[i].outValue[j]);
		if (args.argDescr[i].outValue2[j])
		    free(args.argDescr[i].outValue2[j]);
	    }
	}
	free(args.argDescr);
    }

    WriteServTrailer(srv_h);
    WriteCltTrailer(serverName, start_no, (sign_no +1), clt_h);

    fclose(clt_h);

    fclose(table_h);
    fclose(xg_h);
    fclose(srv_h);
    fclose(mak_h);

    /*
     * create 1 makefile that drives all the rest
     */

    mak_h = OpenOutFile(outputDir, "Makefile", "");
    fprintf(mak_h, "\ntest:all\ntests:all\nall:\n");
    fprintf(mak_h, "%s", platform[8]);
    for(i=1;i<=srv_no;i++)
	fprintf(mak_h, "\t%s %s%d.mak %s\n", 
		platform[0], serverName, i, platform[5]);
    fprintf(mak_h, "\nclean:\n");
    for(i=1;i<=srv_no;i++)
	fprintf(mak_h, "\t%s %s%d.mak clean\n", platform[0], serverName, i);
    fclose(mak_h);

    exit(0);
}
