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

/*
 * rpc←main.c, Top level of the RPC protocol compiler. 
 * Copyright (C) 1987, Sun Microsystems, Inc. 
 */

#include <stdio.h>
#include <strings.h>
#include <string.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "rpc←util.h"
#include "rpc←parse.h"
#include "rpc←scan.h"

#define EXTEND	1		/* alias for TRUE */

struct commandline {
	int cflag;		/* xdr C routines */
	int hflag;		/* header file */
	int lflag;		/* client side stubs */
	int sflag;		/* server stubs for the given transport */
	char *infile;		/* input module name */
	char *outfile;		/* output module name */
};

static char *cmdname;
static char CPP[] = "/lib/cpp";
static char CPPFLAGS[] = "-C";

int PFlag = FALSE;		/* Strip prefix module names */
int ExpandProcArgs = FALSE;	/* TRUE => expand proc struct parameters */
int RecoveryFlag = FALSE;	/* TRUE => add RPC.Failed recovery loop to
				   client stubs and RecoveryProc parm. to
				   client import routines. */

char *svcName;			/* Name of service being compiled. */
char *ServiceName();
void MakeSymbolTables();

int BadProgNameFlag[32];	/* Array of flags indicating if a program
				   defn. has a name that can't be translated
				   to Cedar by the standard means when prefix
				   stripping being done.  The nth flag
				   corresponds to the nth program defn.
				   TRUE => Need to use an altered form of 
				   progName with prefix stripping. */
int CurrentProgNameFlag = -1;	/* Index in BadProgNameFlag for the program
				   defn. currently being processed. */

int PCedar = 0;			/* Flag to signal that PCedar2.0 is the
				   target environment. */
int PCedarUDP = 0;		/* Flag to signal that SunRPC should be used. */
int PCedarTCP = 0;		/* Flag to signal that SunRPCStream should
				   be used. */
char *SunRPC = "SunRPC";	/* Name of SunRPC module to use.  For PCedarTCP,
				   the name to use will be SunRPCStream. */

int SeqTypeId = 0;		/* Global constant used to create unique
				   names for sequence types we need to define. */

Context *CurrentContext = NULL;

int TypeDefsExist = FALSE;	/* Used to signal if an XDR file should
				   exist for the .x file. */
int ProgramsExist = FALSE;	/* Used to signal if a program is defined
				   in the .x file. */

enum {noFile, svcInterface, clientImpl, serverImpl, xdrInterface, xdrImpl}
OutFileType;

list *Imports;			/* List of IMPORT module names. */
list *GetPutImports;		/* List of IMPORT GetPut module names. */
StringHashTableHandle ImportNames; /* Hash table of imported names.
				      Hash table elem values are module
				      names. */
/* IMPORTed .x files are searched for on a path defined by an environment var */
#define PATH←ENV←VAR	"DOTXPATH"
#define PATH←DEFAULT	".:/project/ubi/src/x"

StringHashTableHandle SymbolNames; /* Hash table of struct definitions.
				      Used to decide whether or not to expand
				      procedure input parameters. */
StringHashTableHandle SeqTypes;	/* Hash table of Cedar sequence types for
				   variable-length arrays. */
StringHashTableHandle EnumTypes; /* Hash table of enum types.  Contains lists
				    of their element names. */


main(argc, argv)
	int argc;
	char *argv[];
{
	struct commandline cmd;
	char *interfaceName;
	char *getPutName;
	char *getPutImplName;
	char *clientImplName;
	char *serverImplName;
	char *extendfile();

	bzero((char *)&cmd, sizeof (struct commandline));
	if (!parseargs(argc, argv, &cmd)) {
	    f←print(stderr,
		    "usage: %s [-p] [-e] infile\t\t-- generates Cedar10.0 stubs\n",
		    cmdname);
	    f←print(stderr,
		    "\t%s [-p] [-e] [-PCedar] infile\t\t-- generates PCedar UDP stubs\n",
		    cmdname);
	    f←print(stderr,
		    "\t%s [-p] [-e] [-PCedarStream] infile\t\t-- generates PCedar TCP stubs\n",
		    cmdname);
	    exit(1);
	}

	CurrentContext = (Context *) malloc(sizeof(Context));
	reinitialize();
	svcName = ServiceName(cmd.infile);

	MakeSymbolTables(cmd.infile);

	if (cmd.cflag || cmd.hflag || cmd.lflag || cmd.sflag) {
	    fprintf(stderr, "options not available for Cedar\n");
	    exit(-1);
	} else {
	    /* the rescans are required, since cpp may effect input */
	    interfaceName = extendfile(cmd.infile, "");
	    getPutName = extendfile(cmd.infile, "GetPut");
	    getPutImplName = extendfile(cmd.infile, "GetPutImpl");
	    clientImplName = extendfile(cmd.infile, "ClientImpl");
	    serverImplName = extendfile(cmd.infile, "ServerImpl");
	    if (PCedar && PCedarTCP) {
		SunRPC = "SunRPCStream";
	    }
	    /* *** Note *** h←output must be called before the others. */
	    h←output(cmd.infile, interfaceName, "-DRPC←HDR");
	    reinitialize();
	    c←output(cmd.infile, getPutName, getPutImplName,
		     "-DRPC←XDR");
	    reinitialize();
	    l←output(cmd.infile, clientImplName, "-DRPC←CLNT");
	    reinitialize();
	    s←output(cmd.infile, serverImplName, "-DRPC←SVC");
	    reinitialize();
	}
	exit(0);
	/* NOTREACHED */
}

/*
 * add extension to filename 
 */
static char *
extendfile(file, ext)
	char *file;
	char *ext;
{
	char *res;
	char *p;

	res = alloc(strlen(file) + strlen(ext) + 1);
	if (res == NULL) {
		abort();
	}
	p = rindex(file, '.');
	if (p == NULL) {
		p = file + strlen(file);
	}
	(void) strcpy(res, file);
	(void) strcpy(res + (p - file), ext);
	return (res);
}

/*
 * Return the basename of a file.
 */
static
char *ServiceName(filename)
     char *filename;
{
    char *s;
    char *p;
    char *svcName = alloc(strlen(filename)+1);

    p = rindex(filename, '/');
    if (p != NULL)
	s = p + 1;
    else
	s = filename;
    strcpy(svcName, s);
    p = rindex(svcName, '.');
    if (p != NULL) {
	*p = '\0';
    }
    return (svcName);
}

/*
 * Open output file with given extension 
 */
static
open←output(infile, outfile)
	char *infile;
	char *outfile;
{
	if (outfile == NULL) {
		fout = stdout;
		EmitNotice();
		return;
	}
	if (infile != NULL && streq(outfile, infile)) {
		f←print(stderr, "%s: output would overwrite %s\n", cmdname,
			infile);
		crash();
	}
	if (access(outfile, F←OK) == 0) {
	    (void )unlink(outfile); /* Get rid of the old version; which - if
				       it's a symbolic link - would cause us
				       problems during subsequent writing. */
	}
	fout = fopen(outfile, "w");
	if (fout == NULL) {
		f←print(stderr, "%s: unable to open ", cmdname);
		perror(outfile);
		crash();
	}
	record←open(outfile);
	f←print(fout, "-- %s\n", outfile);
	EmitNotice();
}

static
EmitNotice()
{
	f←print(fout, "-- Please do not edit this file.\n");
	f←print(fout, "-- It was generated using CedarRPCGen.\n\n");
}


/*
 * FIndFileOnPath
 */
static
char *
FindFileOnPath (name, varname, defaultpath)
     char *name;		/* Filename to find */
     char *varname;		/* Name of env var that contains path */
     char *defaultpath;		/* Default path if no environment variable */
{
    char *prefix;
    char *getenv();
    char *copy;
    char buf[1000];
    char *base, *p;

    if (*name == '.' || *name == '/') {
	return (strdup(name));
    }
    if ((prefix = getenv (varname)) == NULL) {
	prefix = defaultpath;
    }
    copy = strdup(prefix);
    base = copy;

    do {
	p = strchr (base, ':');  
	if (p != NULL)
	    *p = (char) 0;
	sprintf (buf, "%s/%s", base, name);
	if (access (buf, R←OK) == 0) {
	    p = strdup (buf);
	    free (copy);
	    return (p);
	}
	if (p != NULL && *(p+1) != (char) 0) {
	    base = p + 1;
	} else {
	    base = NULL;
	}
    } while (base != NULL);
    free (copy);
    return (NULL);
}


/*
 * Open input file with given define for C-preprocessor 
 */
static
open←input(infile, define)
	char *infile;
	char *define;
{
	int pd[2];

	if (infile == NULL) {
	    CurrentContext->infilename = "<stdin>";
	} else {
	    CurrentContext->infilename = FindFileOnPath(infile,
						PATH←ENV←VAR, PATH←DEFAULT);
	    if (CurrentContext->infilename != NULL) {
		infile = CurrentContext->infilename;
	    }
	}
	(void) pipe(pd);
	switch (fork()) {
	case 0:
		(void) close(1);
		(void) dup2(pd[1], 1);
		(void) close(pd[0]);
		if (define != NULL && define[0] != '\0')
		    execl(CPP, CPP, CPPFLAGS, define, infile, NULL);
		else
		    execl(CPP, CPP, CPPFLAGS, infile, NULL);
		perror("execv");
		exit(1);
	case -1:
		perror("fork");
		exit(1);
	}
	(void) close(pd[1]);
	CurrentContext->fin = fdopen(pd[0], "r");
	if (CurrentContext->fin == NULL) {
		f←print(stderr, "%s: ", cmdname);
		perror(CurrentContext->infilename);
		crash();
	}
}

/*
 * Compile into a marshalling routines output file
 */
static
c←output(infile, getputFile, getputImplFile, define)
	char *infile;
        char *getputFile, *getputImplFile;
	char *define;
{
        char outputFileName[128];
	definition *def;
	char *include;
	long tell;
	list *l;

	/*
	 * Emit the interface file.
	 */
	sleep(1);		/* Do this to avoid having the same creation
				 time on two different files - something that
				 Cedar can't handle. */
	OutFileType = xdrInterface;
	open←input(infile, define);
	sprintf(outputFileName, "%s.mesa", getputFile);
	open←output(infile, outputFileName);

	f←print(fout, "DIRECTORY\n");
	f←print(fout, "  %s,\n", SunRPC);
	f←print(fout, "  %s;\n\n", svcName);
	f←print(fout, "%s: CEDAR DEFINITIONS =\n", getputFile);
	f←print(fout, "BEGIN\n\n");
	f←print(fout, "Handle: TYPE = %s.Handle;\n\n", SunRPC);

	tell = ftell(fout);
	while (def = get←definition()) {
		emitDefn(def);
	}

	if (tell == ftell(fout)) {
	    (void) unlink(outputFileName);
	    return;
	}

	f←print(fout, "END.\n");

	fclose(fout);


	/*
	 * Emit the module file.
	 */
	sleep(1);		/* Do this to avoid having the same creation
				 time on two different files - something that
				 Cedar can't handle. */
	OutFileType = xdrImpl;

	open←input(infile, define);
	sprintf(outputFileName, "%s.mesa", getputImplFile);
	open←output(infile, outputFileName);

	f←print(fout, "DIRECTORY\n");
	if (PCedar) f←print(fout, "  Basics,\n");
	f←print(fout, "  Rope,\n");
	f←print(fout, "  %s,\n", SunRPC);
	if (PCedarUDP) f←print(fout, "  SunRPCNumbers,\n");
	if (Imports != NULL) {
	    for (l = Imports; l != NULL; l = l->next) {
		f←print(fout, "  %s,\n", l->val);
	    }
	}
	if (GetPutImports != NULL) {
	    for (l = GetPutImports; l != NULL; l = l->next) {
		f←print(fout, "  %s,\n", l->val);
	    }
	}
	f←print(fout, "  %s,\n", svcName);
	f←print(fout, "  %sGetPut;\n\n", svcName);

	f←print(fout, "%s: CEDAR PROGRAM\n", getputImplFile);
	f←print(fout, "  IMPORTS %s", SunRPC);
	if (PCedarUDP) f←print(fout, ", SunRPCNumbers");
	if (GetPutImports != NULL) {
	    for (l = GetPutImports; l != NULL; l = l->next) {
		f←print(fout, ", %s", l->val);
	    }
	}
	f←print(fout, "  EXPORTS %s, %sGetPut =\n", svcName, svcName);
	f←print(fout, "BEGIN\n");
	f←print(fout, "Handle: TYPE = %s.Handle;\n\n", SunRPC);
	f←print(fout, "ROPE: TYPE = Rope.ROPE;\n\n");

	tell = ftell(fout);
	while (def = get←definition()) {
		emitImpl(def);
	}

	if (tell == ftell(fout)) {
	    (void) unlink(outputFileName);
	    return;
	}


	f←print(fout, "END.\n");

	fclose(fout);
}

/*
 * Compile into an M3 interface file for the service.
 */
static
h←output(infile, outfile, define)
     char *infile;
     char *outfile;
     char *define;
{
    definition *def;
    char outfilename[128];
    list *l;

    sleep(1);			/* Do this to avoid having the same creation
				   time on two different files - something that
				   Cedar can't handle. */

    OutFileType = svcInterface;
    SeqTypeId = 0;

    /* Get open connection to (C-preprocessed) input file
       and an open output file. */
    open←input(infile, define);
    sprintf(outfilename, "%s.mesa", outfile);
    open←output(infile, outfilename);
    
    /* Emit preamble. */
    f←print(fout, "DIRECTORY\n");
    f←print(fout, "  Rope,\n");
    f←print(fout, "  Arpa,\n");
    if (Imports != NULL) {
	for (l = Imports; l != NULL; l = l->next)
	    f←print(fout, "  %s,\n", l->val);
    }
    f←print(fout, "  SunRPCAuth,\n");
    if (PCedar) {
	if (PCedarUDP)
	    f←print(fout, "  SunRPC;\n\n");
	else			/* PCedarTCP */
	    f←print(fout, "  SunRPCStream;\n\n");
    }
    else {
	f←print(fout, "  %s;\n\n", SunRPC);
    }

    f←print(fout, "%s: CEDAR DEFINITIONS =\n", svcName);
    f←print(fout, "BEGIN\n\n");
    f←print(fout, "ROPE: TYPE = Rope.ROPE;\n\n");
      
    /* Emit definitions. */
    while (def = get←definition()) {
	print←datadef(def);
    }

    /* Print postamble. */
    f←print(fout, "\nEND.");

    fclose(fout);
}

/*
 * Compile into an RPC service
 */
static
s←output(infile, outfile, define)
	char *infile;
	char *outfile;
	char *define;
{
    definition *def;
    char outfilename[128];
    version←list *vers;
    proc←list *proc;
    list *l;
    long tell;

    sleep(1);			/* Do this to avoid having the same creation
				   time on two different files - something that
				   Cedar can't handle. */
    
    OutFileType = serverImpl;

    open←input(infile, define);
    sprintf(outfilename, "%s.mesa", outfile);
    open←output(infile, outfilename);
    
    f←print(fout, "DIRECTORY\n");
    if (PCedarUDP) f←print(fout, "  Basics,\n");
    if (PCedarTCP) f←print(fout, "  Convert,\n");
    f←print(fout, "  %s,\n", SunRPC);
    if (PCedarUDP) f←print(fout, "  SunRPCNumbers,\n");
    if (PCedar) {
	f←print(fout, "  SunPMap,\n");
	f←print(fout, "  SunPMapLocal,\n");
    }
    f←print(fout, "  SunRPCAuth,\n");
    f←print(fout, "  Rope,\n");
    if (Imports != NULL) {
	for (l = Imports; l != NULL; l = l->next)
	    f←print(fout, "  %s,\n", l->val);
    }
    if (GetPutImports != NULL) {
	for (l = GetPutImports; l != NULL; l = l->next)
	    f←print(fout, "  %s,\n", l->val);
    }
    if (TypeDefsExist) f←print(fout, "  %sGetPut,\n", svcName);
    f←print(fout, "  %s;\n\n", svcName);

    f←print(fout, "%s: CEDAR PROGRAM\n", outfile);
    if (PCedarUDP)
	f←print(fout, "  IMPORTS Basics, Rope, SunPMapLocal, %s, SunRPCNumbers", SunRPC);
    else if (PCedarTCP)
	f←print(fout, "  IMPORTS Convert, Rope, SunPMapLocal, %s", SunRPC);
    else
	f←print(fout, "  IMPORTS Rope, %s", SunRPC);
    if (GetPutImports != NULL) {
	for (l = GetPutImports; l != NULL; l = l->next)
	    f←print(fout, ", %s", l->val);
    }
    if (TypeDefsExist) f←print(fout, ", %sGetPut\n", svcName);
    else f←print(fout, "\n");
    f←print(fout, "  EXPORTS %s =\n", svcName);

    f←print(fout, "BEGIN\n");

    f←print(fout, "ROPE: TYPE = Rope.ROPE;\n\n");
    f←print(fout, "Handle: TYPE = %s.Handle;\n", SunRPC);
    f←print(fout, "Conversation: TYPE = SunRPCAuth.Conversation;\n\n");
    f←print(fout, "defaultReplyTTL: CARDINAL ← 10;\n\n");
    
    tell = ftell(fout);
    while (def = get←definition()) {
	if (def->def←kind == DEF←PROGRAM) {
	    for (vers = def->def.pr.versions; vers != NULL; vers = vers->next) {
		write←server←prog(svcName, def->def←name, vers);
	    }
	}
    }
    
    if (tell == ftell(fout)) {
	(void) unlink(outfilename);
	return;
    }

    f←print(fout, "END.\n");

    fclose(fout);
}

/*
 * generate client side stubs
 */
static
l←output(infile, outfile, define)
	char *infile;
	char *outfile;
	char *define;
{
	char *include;
	definition *def;
	char outfilename[128];
	int foundprogram = 0;
	list *l;

	sleep(1);		/* Do this to avoid having the same creation
				   time on two different files - something that
				   Cedar can't handle. */

	OutFileType = clientImpl;

	open←input(infile, define);
	sprintf(outfilename, "%s.mesa", outfile);
	open←output(infile, outfilename);

	f←print(fout, "DIRECTORY\n");
	if (PCedar) f←print(fout, "  Basics,\n");
	if (PCedarTCP) f←print(fout, "  Convert,\n");
	f←print(fout, "  Rope,\n");
	f←print(fout, "  SunRPC,\n"); /* We always use this because we need to
				         talk to the PortMapper. */
	if (PCedarUDP) f←print(fout, "  SunRPCNumbers,\n");
	if (PCedarTCP) f←print(fout, "  SunRPCStream,\n");
	if (PCedar) {
	    f←print(fout, "  SunPMap,\n");
	    f←print(fout, "  SunPMapClient,\n");
	}
	f←print(fout, "  SunRPCAuth,\n");
	if (Imports != NULL) {
	    for (l = Imports; l != NULL; l = l->next)
		f←print(fout, "  %s,\n", l->val);
	}
	if (GetPutImports != NULL) {
	    for (l = GetPutImports; l != NULL; l = l->next)
		f←print(fout, "  %s,\n", l->val);
	}
	if (TypeDefsExist) f←print(fout, "  %sGetPut,\n",
				   svcName);
	f←print(fout, "  %s;\n\n", svcName);

	f←print(fout, "%s: CEDAR PROGRAM\n", outfile);
	if (PCedarUDP)
	    f←print(fout, "  IMPORTS Basics, SunPMapClient, %s, SunRPCNumbers, SunRPCAuth",
		    SunRPC);
	else if (PCedarTCP)
	    f←print(fout,
		    "  IMPORTS Basics, Convert, Rope, SunPMapClient, SunRPC, %s, SunRPCAuth",
		    SunRPC);
	else
	    f←print(fout, "  IMPORTS %s", SunRPC);
	if (GetPutImports != NULL) {
	    for (l = GetPutImports; l != NULL; l = l->next)
		f←print(fout, ", %s", l->val);
	}
	if (TypeDefsExist) f←print(fout, ", %sGetPut\n", svcName);
	else f←print(fout, "\n");
	f←print(fout, "  EXPORTS %s =\n", svcName);
	f←print(fout, "BEGIN\n");
	f←print(fout, "ROPE: TYPE = Rope.ROPE;\n\n");
	f←print(fout, "defaultTimeout: CARDINAL ← 2000;\n");
	f←print(fout, "defaultRetries: CARDINAL ← 5;\n\n");

	while (def = get←definition()) {
		foundprogram |= (def->def←kind == DEF←PROGRAM);
	}
	if (!foundprogram) {
		(void) unlink(outfilename);
		return;
	}

	write←stubs(svcName);

	f←print(fout, "END.\n");

	fclose(fout);
}

/*
 * Construct a list of IMPORT statements that must go into each output file.
 * Also construct a hash table of symbol definitions encountered.
 */
void MakeSymbolTables(name)
     char *name;
{
    definition *def;
    StringHashElement elem;
    case←list *c;
    decl←list *d;

    Imports = NULL;
    GetPutImports = NULL;
    ImportNames = StringHashCreate(128, NULL, NULL);
    if (ImportNames == NULL) {
	fprintf(stderr,
		"ERROR: couldn't create hash table for import names.\n");
	exit(-1);
    }

    SymbolNames = StringHashCreate(128, NULL, NULL);
    if (SymbolNames == NULL) {
	fprintf(stderr,
		"ERROR: couldn't create hash table for struct names.\n");
	exit(-1);
    }

    SeqTypes = StringHashCreate(128, NULL, NULL);
    if (SeqTypes == NULL) {
	fprintf(stderr,
		"ERROR: couldn't create hash table for sequence type names.\n");
	exit(-1);
    }

    EnumTypes = StringHashCreate(128, NULL, NULL);
    if (EnumTypes == NULL) {
	fprintf(stderr,
		"ERROR: couldn't create hash table for enum type names.\n");
	exit(-1);
    }

    OutFileType = noFile;

    CurrentContext->printDirectives = FALSE;
    open←input(name, "-DRPC←HDR");
    /* Just run through the file so that all IMPORT directives get seen
       by the scanner. */
    TypeDefsExist = FALSE;
    ProgramsExist = FALSE;
    while (def = get←definition()) {
	elem = StringHashFind(SymbolNames, def->def←name, insert, NULL);
	elem->userData = (char *) def;
	switch (def->def←kind) {
	  case DEF←STRUCT:
	  case DEF←UNION:
	  case DEF←TYPEDEF:
	  case DEF←ENUM:
	    TypeDefsExist = TRUE;
	    break;
	  case DEF←PROGRAM:
	    ProgramsExist = TRUE;
	    break;
	  default:
	    break;
	}
    }
    reinitialize();
}

/*
 * Process IMPORT statements for imported .x files.
 */
void ProcessImport(name)
     char *name;
{
    Context *save = CurrentContext;
    Context import;
    char infile[256];
    definition *def;
    char buf[512];
    list *l;
    StringHashElement elem;
    int inserted;
    ImportModuleRec *m;
    int foundDefnsFlag = FALSE;
    int foundTypeDefsFlag = FALSE;

    if (OutFileType != noFile) return;

    sprintf(infile, "%s.x", name);
    SeqTypeId = 0;
    CurrentContext = &import;
    reinitialize();
    CurrentContext->printDirectives = FALSE;
    open←input(infile, "");
    name = strdup(name);
    while (def = get←definition()) {
	elem = StringHashFind(ImportNames, def->def←name, insert, &inserted);
	elem->userData = (char *) malloc(sizeof(ImportModuleRec));
	m = (ImportModuleRec *) (elem->userData);
	m->moduleName = name;
	sprintf(buf, "%s.%s", name, StripPrefix(name, def->def←name));
	m->defnName = strdup(buf);
	elem = StringHashFind(SymbolNames, def->def←name, insert, NULL);
	elem->userData = (char *) def;
	switch (def->def←kind) {
	  case DEF←STRUCT:
	  case DEF←UNION:
	  case DEF←TYPEDEF:
	  case DEF←ENUM:
	    foundDefnsFlag = TRUE;
	    foundTypeDefsFlag = TRUE;
	    break;
	  case DEF←CONST:
	    foundDefnsFlag = TRUE;
	    break;
	  default:
	    break;
	}
	CheckForSeqTypes(def, name);
    }
    if (foundDefnsFlag) {
	l = (list *) malloc(sizeof(list));
	l->val = name;
	l->next = Imports;
	Imports = l;
    }
    if (foundTypeDefsFlag) {
	l = (list *) malloc(sizeof(list));
	sprintf(buf, "%sGetPut", name);
	l->val = strdup(buf);
	l->next = GetPutImports;
	GetPutImports = l;
    }

    CurrentContext = save;
}


/*
 * Add any SeqType data definitions to the SeqTypes table.
 */
CheckForSeqTypes(def, importName)
     definition *def;
     char *importName;
{
    switch (def->def←kind) {
      case DEF←STRUCT:
	CheckStructDef(def, importName);
	break;
      case DEF←UNION:
	CheckUnionDef(def, importName);
	break;
      case DEF←TYPEDEF:
	CheckTypeDef(def, importName);
	break;
      default:
	break;
    }
}


CheckStructDef(def, importName)
     definition *def;
     char *importName;
{
    decl←list *l;
    char *name = def->def←name;
    
    for (l = def->def.st.decls; l != NULL; l = l->next) {
	CheckDeclaration(name, &l->decl, importName);
    }
}


CheckUnionDef(def, importName)
     definition *def;
     char *importName;
{
    case←list *l;
    char *name = def->def←name;

    for (l = def->def.un.cases; l != NULL; l = l->next) {
	if (!streq(l->case←decl.type, "void")) {
	    CheckDeclaration(name, &l->case←decl, importName);
	    }
    }
}


CheckTypeDef(def, importName)
     definition *def;
     char *importName;
{
    char *name = def->def←name;
    char *old = def->def.ty.old←type;
    relation rel = def->def.ty.rel;
    char buf[256];
    int opaqueFlag = FALSE;
    StringHashElement elem;
    int inserted;

    if (!streq(name, old)) {
	if (streq(old, "string")) {
	    rel = REL←ALIAS;
	}
	else if (streq(old, "opaque")) {
	    opaqueFlag = TRUE;
	}
	if (rel == REL←ARRAY) {
	    if (!opaqueFlag) {
		elem = StringHashFind(SeqTypes,
				      def->def.ty.old←type,
				      insert, &inserted);
		if (inserted) {
		    sprintf(buf, "%s.SeqType%d", importName, SeqTypeId);
		    SeqTypeId++;
		    elem->userData = strdup(buf);
		}
	    }
	}
    }
}


CheckDeclaration(name, dec, importName)
     char *name;
     declaration *dec;
     char *importName;
{
    StringHashElement elem;
    int inserted;
    char buf[256];
    
    if (streq(dec->type, "void")) {
	return;
    }
    if (!streq(dec->type, "string")) {
	if (dec->rel == REL←ARRAY) {
	    if (!streq(dec->type, "opaque")) {
		sprintf(buf, "%s.%s", name, dec->name);
		elem = StringHashFind(SeqTypes, buf,
				      insert, &inserted);
		if (inserted) {
		    sprintf(buf, "%s.SeqType%d", importName, SeqTypeId);
		    SeqTypeId++;
		    elem->userData = strdup(buf);
		}
	    }
	}
    }
}


/*
 * Parse command line arguments 
 */
static
parseargs(argc, argv, cmd)
	int argc;
	char *argv[];
	struct commandline *cmd;

{
	int i;
	int j;
	char c;
	char flag[(1 << 8 * sizeof(char))];
	int nflags;

	cmdname = argv[0];
	cmd->infile = cmd->outfile = NULL;
	if (argc < 2) {
		return (0);
	}
	flag['c'] = 0;
	flag['h'] = 0;
	flag['l'] = 0;
	flag['s'] = 0;
	for (i = 1; i < argc; i++) {
		if (argv[i][0] != '-') {
			if (cmd->infile) {
				return (0);
			}
			cmd->infile = argv[i];
		} else {
			for (j = 1; argv[i][j] != 0; j++) {
				c = argv[i][j];
				switch (c) {
				case 'c':
				case 'h':
				case 'l':
					if (flag[c]) {
						return (0);
					}
					flag[c] = 1;
					break;
				case 'P':
					if (strcmp(argv[i], "-PCedar") == 0) {
					    PCedar = 1;
					    PCedarUDP = 1;
					}
					else if (strcmp(argv[i], "-PCedarStream") == 0) {
					    PCedar = 1;
					    PCedarTCP = 1;
					}
					else {
					    return (0);
					}
					if (PCedarUDP && PCedarTCP) {
					    fprintf(stderr, "can't specify both PCedar and PCedarStream at the same time\n");
					    exit(-1);
					}
					goto nextarg;
				case 'o':
					if (argv[i][j - 1] != '-' || 
					    argv[i][j + 1] != 0) {
						return (0);
					}
					if (++i == argc) {
						return (0);
					}
					if (cmd->outfile) {
					    return (0);
					}
					cmd->outfile = argv[i];
					goto nextarg;
			        case 'p':
					PFlag = TRUE;
					break;
			        case 'e':
					ExpandProcArgs = TRUE;
					break;
			        case 'r':
					RecoveryFlag = TRUE;
					break;
				default:
					return (0);
				}
			}
	nextarg:
			;
		}
	}
	cmd->cflag = flag['c'];
	cmd->hflag = flag['h'];
	cmd->sflag = flag['s'];
	cmd->lflag = flag['l'];
	nflags = cmd->cflag + cmd->hflag + cmd->sflag + cmd->lflag;
	if (nflags == 0) {
		if (cmd->outfile != NULL || cmd->infile == NULL) {
			return (0);
		}
	} else if (nflags > 1) {
		return (0);
	}
	return (1);
}