/*-=-=-=-=-=-=-=-=-=-=-=- RCCL/RCI Notice of Copyright -=-=-=-=-=-=-=-=-=-=-*
 *									    *
 *	Copyright (C) 1987, 1991 by John E. Lloyd.			    *
 *									    *
 *	Information on copyright permissions, as well as a complete	    *
 *	disclaimer of warranty, is given in the file COPYRIGHT.		    *
 *									    *
 *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*---------------------------------------------------------------------------
 *		
 * MODULE NAME:	parameterFile
 *
 * DESCRIPTION:	Support routines for the RCI 'parameters' package that
 *		do the actual reading of the parameter definition file.
 * 
 * CAVEATS:	The 'parameters' package should be made obsolete
 *		by a new 'database' package that I am working on.
 *
 * AUTHOR/DATE:	John Lloyd, Fall 1987
 *
 * MODIFICATION HISTORY:
 *
 *--------------------------------------------------------------------------*/

#ifndef VXWORKS_CFG
#include <stdio.h>
#else
#include <vxWorks.h>
#include <stdioLib.h>
#endif

#include <ctype.h>

#include "../h/cdefs.h"
#include "../h/errorCodes.h"

#include "../rci/parameters.h"
#include "../rci/link.h"

/* ----- Error Messages. Defined externally to save space ----- */

static char *outOfMemoryMsg	= "%s -- out of memory\n";
static char *parseErrMsg	= "%s %s, line %d\n";
static char *cantOpenFileMsg	= "%s -- can't open file %s\n";
static char *fileReadErrMsg	= "%s -- error reading file %d\n";

extern char *copyofstr();

#define IBUFSIZE 256
#define PARSE_ERROR(m)  errorMsg(ESyntax, parseErrMsg, fxnName, \
				 (m), lineNumber()); \
			return (-1);

readParameterFile(name, list)
char *name;
PARAMSPEC *list;
{
	char *fxnName = "readParameterFile()";
	char ibuf[IBUFSIZE];
	PARAMSPEC *spec;
	FILE *fp;
	int status;

	if ((fp = fopen(name, "r")) == NULL)
	 { errorMsg (EBadAccess, cantOpenFileMsg, fxnName, name);
	   return (-1);
	 }

	readInit (fp, "=");

	if (!readIdentifier (ibuf, IBUFSIZE) ||
	    strcmp (ibuf, "name") != 0)
	 { if (!endOfInput())
	    { fclose(fp);
	      PARSE_ERROR ("'name' keyword expected");
	    }
	   else
	    { fclose(fp);
	      return (1);
	    }
	 }
	while (1)
	 { PARAMSPEC *p;

	   if ((spec = ALLOC(PARAMSPEC)) == NULL)
	    { errorMsg (EMemory, outOfMemoryMsg, fxnName);
	      fclose(fp);
	      return (-1);
	    }
	   if ((status = readParameters(spec)) < 0)
	    { errorMsg (getErrorCode(0), fileReadErrMsg, fxnName, name);
	      fclose(fp);
	      return (-1);
	    }
	   for (p = list->next; p != list; p = p->next)
	    { if (strcmp (spec->name, p->name) == 0)
	       { break;
	       }
	    }
	   if (p == list)
	    { p = spec;
	      LINK (p, list);
	    }
	   else
	    { mergeParameters (&p->list, &spec->list);
	      free (spec);
	    }
	   if (status == 0)
	    { break;
	    }
	 }	
	fclose(fp);
	return (1);
}

readParameters (spec)
PARAMSPEC *spec;
{
	/* reads the next parameter spec from a file. Assumes that readInit()
	   has already been performed, and that we have already read the
	   keyword 'name'.
	 */

	char *fxnName = "readParameters()";
	char ibuf[IBUFSIZE];

	if (readCharacter() != '=')
	 { PARSE_ERROR ("'=' expected");
	 }
	if (!readIdentifier (ibuf, IBUFSIZE))
	 { PARSE_ERROR ("name expected");
	 }
	if (!(spec->name = copyofstr(ibuf)))
	 { errorMsg (EMemory, outOfMemoryMsg, fxnName);
	   return (-1);
	 }
	spec->list.next = spec->list.prev = &(spec->list);

	while (1)
	 { PARAM par;
	   PARAM *p;
	   PARAM *list = &spec->list;

	   if (!readIdentifier (ibuf, IBUFSIZE))
	    { if (endOfInput())
	       { return (0);
	       }
	      else
	       { PARSE_ERROR ("keyword expected");
	       }
	    }
	   if (strcmp (ibuf, "name") == 0)
	    { return (1);
	    }
	   if ((par.keyword = copyofstr (ibuf)) == NULL)
	    { errorMsg (EMemory, outOfMemoryMsg, fxnName);
	      return (-1);
	    }
	   if (readCharacter() != '=')
	    { free (par.keyword);
	      PARSE_ERROR ("'=' expected");
	    }
	   if (readInteger(&par.val.i))
	    { par.type = INTEGER_TYPE;
	    }
	   else if (readDouble(&par.val.d))
	    { par.type = FLOAT_TYPE;
	    }
	   else if (readIdentifier(ibuf, IBUFSIZE))
	    { par.type = IDENTIFIER_TYPE;
	      if ((par.val.s = copyofstr (ibuf)) == NULL)
	       { errorMsg (EMemory, outOfMemoryMsg, fxnName);
		 free (par.keyword);
		 return (-1);
	       }
	    }
	   else if (readString(ibuf, IBUFSIZE))
	    { par.type = STRING_TYPE;
	      if ((par.val.s = copyofstr (ibuf)) == NULL)
	       { errorMsg (EMemory, outOfMemoryMsg, fxnName);
		 free (par.keyword);
		 return (-1);
	       }
	    }
	   else
	    { free (par.keyword);
	      PARSE_ERROR ("cannot determine what follows '='");
	    }

	   for (p = list->next; p != list; p = p->next)
	    { if (strcmp (p->keyword, ibuf) == 0)
	       { break;
	       }
	    }
	   if (p == list)
	    { if ((p = ALLOC(PARAM)) == NULL)
	       { errorMsg (EMemory, outOfMemoryMsg, fxnName);
		 return (-1);
	       }
	      *p = par;
	      LINK (p, list);
	    }
	   else
	    { *p = par;
	      free (par.keyword);
	    }
	 }
}

mergeParameters (list1, list2)
PARAM *list1, *list2;
{
	PARAM *p1, *p2, *temp;
	PARAM *newlist;

	newlist = NULL;

	for (p2 = list2->next; p2 != list2; )
	 { for (p1 = list1->next; p1 != list1; p1 = p1->next)
	    { if (strcmp (p1->keyword, p2->keyword) == 0)
	       { break;
	       }
	    }
	   temp = p2->next;
	   UNLINK (p2);
	   if (p1 == list1)
	    { p2->next = newlist; newlist = p2;
	    }
	   else
	    { p1->type = p2->type;
	      p1->val = p2->val;
	      free (p2->keyword);
	      free (p2);
	    }
	   p2 = temp;
	 }
	for (p2 = newlist; p2 != NULL; )
	 { temp = p2->next;
	   LINK (p2, list1);
	   p2 = temp;
	 }
}

freeParameterSpec(spec)
PARAMSPEC *spec;
{
	PARAM *p, *p_next;
	for (p=spec->list.next; p!=spec->list.next; p=p_next)
	 { p_next = p->next;
	   FREE(p->keyword);
	   if (p->type == STRING_TYPE)
	    { FREE(p->val.s);
	    }
	   FREE(p);
	 }
	FREE(spec->name);
	FREE(spec);
}

#if TEST

main(argc, argv)
int argc;
char **argv;
{
	PARAMSPEC pars;
	PARAMSPEC *spec;

	if (argc != 2)
	 { usageMsg (-1, "pftest <fileName>\n");
	 }
	argc--; argv++;
	
	pars.next = pars.prev = &pars;

	if (readParameterFile(*argv, &pars) < 0)
	 { printErrors();
	 }
	for (spec = pars.next; spec != &pars; spec = spec->next)
	 { PARAM *p;
	   PARAM *list = &spec->list;

	   printf ("%s:\n", spec->name);
	   for (p = list->next; p != list; p = p->next)
	    { printc (-30, "%s =", p->keyword);
	      switch (p->type)
	       { case INTEGER_TYPE:
		  { printf ("%d (0x%x)\n", p->val.i, p->val.i);
		    break;
		  }
		 case FLOAT_TYPE:
		  { printf ("%f\n", p->val.d);
		    break;
		  }
		 case STRING_TYPE:
		  { printf ("%s (STRING)\n", p->val.s);
		    break;
		  }
		 case IDENTIFIER_TYPE:
		  { printf ("%s (IDENTIFIER)\n", p->val.s);
		    break;
		  }
	       }
	    }
	   putchar ('\n');	      
	 }
}
#endif
