/*
 *----------------------------------------------------------------------
 *
 * PROGRAM	: gksmc
 *
 * FILE		: defaults.c
 *
 * CONTENTS	: Routines to read in and interpret the specified defaults
 *		  file. This is used to tailor the output CGM, and supply
 *		  additional information, to improve the quality of
 *		  translation.
 *
 * GLOBALS USED : SepStrings, picture_name, mf_name, mf_descr, max_colr
 *		: colr_prec, DevSpM, DevScale, PointListForm, Encoding.
 *
 * DATE		: 24th April 1988
 *
 *----------------------------------------------------------------------
 */
#include <stdio.h>
#include "defns.h"
#include "tables.h"

/* Number of default file commands */
#define NUM_DEFAULTS	16

extern char *SepStrings[];

/* Count variables, used to record the types of character in a string */
int     sc_cnt,
        others_cnt,
        comma_cnt,
        slash_cnt,
        semic_cnt;

/* Holds command names - can be given in file in upper or lower case */
char   *DftTable[NUM_DEFAULTS] = {
	"softsep", "optsep", "hardsep", "sep", "term", "picname",
	"mfname", "mfdescr", "case", "maxcolr", "colrprec",
"devspec", "devscale", "pointlist", "encoding", "#"};


/*
 *-------------------------------------------------------------------------
 * read_defaults:
 *	This is called if the command line contains a -d flag followed by
 * a filename. The file contains various 'commands' to change defaults
 * used for certain of the global variables and other features of the
 * output metafile, letting the user tailor the output metafile.
 * The allowed commands are given above in table DftTable. The formats of
 * the commands are :
 *   + SOFTSEP   <string>     - <SOFTSEP> separator string
 *   + OPTSEP    <string>     - <OPTSEP> separator string
 *   + HARDSEP   <string>     - <HARDSEP> separator string
 *   + SEP       <string>     - <SEP> separator string
 *   + TERM      <string>     - <TERM> separator string
 *     PICNAME   <string>     - picture name string
 *     MFNAME    <string>     - metafile name string
 *     MFDESCR   <string>     - metafile descriptor string
 *   + CASE      <ctype>      - write CGM metafile in upper or lower case.
 *				<ctype> ::= LOWER | UPPER
 *     MAXCOLR   <integer>    - maximum colour index
 *     COLRPREC  <integer>    - specify colour precision
 *     DEVSPEC   <mode>       - Device Viewport Specification mode
 *				<mode> ::= FRACTION | MM | PHYDEVICEUNITS
 *     DEVSCALE  <real>       - the scaling factor if MM chosen for above
 *   + POINTLIST <ptype>      -	format for point lists in clear text
 *				<ptype> ::= INCREMENTAL | ABSOLUTE
 *				Line, Marker and Polygon items.
 *     ENCODING   <enc>       - Encoding format for CGM metafile.
 *				<enc> ::= CLEARTEXT | CHARACTER
 *     #		      - comment
 * Those commands above preceded by a '+' only apply to the clear text
 * encoding.
 * <string> ::= a sequence of chars enclosed in single or double quotes.
 * A string enclosed in single quotes can contain double quotes and vice
 * versa. See read_quoted.
 *-------------------------------------------------------------------------
 */
read_defaults(df_name)
	char   *df_name;
{
	FILE   *dfile,
	       *fopen();
	int     stat,
	        int_param,
	        dft_id,
	        ch,
	        i,
	        param_len;
	char    dft_str[20],
	        param_str[40];
	double  real_param;

	if ((dfile = fopen(df_name, "r")) == NULL)
		write_error(3);
	while ((stat = fscanf(dfile, "%20s", dft_str)) != 0 && stat != EOF)
	{
		dft_id = 0;
		conv_string_to_lcase(dft_str);
		for (i = 0; i < NUM_DEFAULTS; i++)
			dft_id = (!strcmp(dft_str, DftTable[i])) ? i + 1 : dft_id;
		switch (dft_id)
		{
			case 0:
				write_error(4);
				break;
			case 1:
				set_cgm_string(dfile, &SepStrings[SOFTSEP], 5, 1, 1, 0, 0, 0);
				break;
			case 2:
				set_cgm_string(dfile, &SepStrings[OPTSEP], 6, 0, 1, 0, 0, 0);
				break;
			case 3:
				set_cgm_string(dfile, &SepStrings[HARDSEP], 7, 0, 0, 0, 1, 0);
				break;
			case 4:
				set_cgm_string(dfile, &SepStrings[SEP], 8, 0, 0, 1, 1, 0);
				break;
			case 5:
				set_cgm_string(dfile, &SepStrings[TERMINATOR], 9, 0, 0, 1, 0, 1);
				break;
			case 6:
				set_cgm_string(dfile, &picture_name, 13, 0, 0, 0, 0, 0);
				break;
			case 7:
				set_cgm_string(dfile, &mf_name, 13, 0, 0, 0, 0, 0);
				break;
			case 8:
				set_cgm_string(dfile, &mf_descr, 13, 0, 0, 0, 0, 0);
				break;
			case 9:
				read_unquoted(dfile, param_str);
				if (!strcmp(param_str, "lower"))
					lower_tables();
				else if (strcmp(param_str, "upper"))
					write_error(10);
				break;
			case 10:
				if (fscanf(dfile, "%d", &int_param) != 1 || int_param < 1)
					write_error(11);
				else
					max_colr = int_param;
				break;
			case 11:
				if (fscanf(dfile, "%d", &int_param) != 1 || int_param < 1)
					write_error(12);
				else
					colr_prec = int_param;
				break;
			case 12:
				read_unquoted(dfile, param_str);
				if (!strcmp(param_str, "fraction"))
					DevSpM = FRACTION;
				else if (!strcmp(param_str, "mm"))
					DevSpM = MM;
				else if (!strcmp(param_str, "phydeviceunits"))
					DevSpM = PHYDEVICEUNITS;
				else
					write_error(15);
				break;
			case 13:
				if ((fscanf(dfile, "%f", &real_param)) != 1)
					write_error(16);
				else
					DevScale = int_param;
				break;
			case 14:
				read_unquoted(dfile, param_str);
				if (!strcmp(param_str, "incremental"))
					PointListForm = LIST_INCR;
				else if (!strcmp(param_str, "absolute"))
					PointListForm = LIST_ABSOLUTE;
				else
					write_error(17);
				break;
			case 15:
				read_unquoted(dfile, param_str);
				if (!strcmp(param_str, "cleartext"))
					Encoding = CLEAR_TEXT;
				else if (!strcmp(param_str, "character"))
					Encoding = CHARACTER;
				else
					write_error(18);
				break;
			case 16:
				break;
			default:
				write_error(13);
		}
		if (dft_id != 9 && dft_id != 12)
			read_to_eoln(dfile);
	}
	fclose(dfile);
}


/*
 *-------------------------------------------------------------------------
 * read_unquoted:
 *	Reads off any leading non letter characters then reads in a
 * sequence of upper and lower case letters, delimited by any non letter
 * character. The string is placed in the second parameter, having been
 * converted to lower case.
 *-------------------------------------------------------------------------
 */
read_unquoted(infile, string)
	FILE   *infile;
	char   *string;
{
	int     ch,
	        i;

	while ((ch = getc(infile)) != EOF && !is_letter(ch))
		;
	string[0] = ch;
	i = 1;
	while ((ch = getc(infile)) != EOF && is_letter(ch) && i < 39)
		string[i++] = ch;
	string[i] = '\0';
	if (ch != '\n')
		read_to_eoln(infile);
	conv_string_to_lcase(string);
}


/*
 *-------------------------------------------------------------------------
 * set_cgm_string:
 *	Reads in a quoted string, takes a pointer to the global string to
 * be changed and checks the read string according to the remaining
 * parameters to see if it is a legal string for the string variable
 * being set, and copies it in if everything is ok.
 * Parameters :-
 *   err_no      : error number if illegal string read in.
 *   sc_flg      : set <> 0 if at least one sepchar required.
 *   ot_flg      : set <> 0 if only sepchars allowed, ie no OThers.
 *   ot_val      : set = number of number of non sepchars expected.
 *   comma_flg   : set <> 0 if ONE comma expected in string.
 *   term_flg    : set <> 0 if terminator char (/ or ;) expected.
 *-------------------------------------------------------------------------
 */
set_cgm_string(infile, str_ptr, err_no, sc_flg, ot_flg, ot_val, comma_flg, term_flg)
	FILE   *infile;
	char  **str_ptr;
	int     err_no,
	        sc_flg,
	        ot_flg,
	        ot_val,
	        comma_flg,
	        term_flg;
{
	char    param_str[40];
	int     param_len;

	read_quoted(infile, param_str);
	param_len = strlen(param_str) + 1;
	if (sc_flg && !sc_cnt)
		write_error(err_no);
	if ((ot_flg && others_cnt) || (ot_val && others_cnt != ot_val))
		write_error(err_no);
	if (comma_flg && comma_cnt != 1 && others_cnt != 1)
		write_error(err_no);
	if (term_flg && (slash_cnt + semic_cnt) != 1)
		write_error(err_no);
	cfree(*str_ptr);
	*str_ptr = (char *) calloc(param_len, sizeof(char));
	strcpy(*str_ptr, param_str);
}


/*
 *-------------------------------------------------------------------------
 * read_quoted:
 *	Reads in a quoted string, which can be delimited with single or
 * double quotes (' or "). The delimiting character cannot appear within
 * the string.
 * Extra escape sequences have been defined for the separator characters,
 * the full set is :
 *	\n : Newline		\t : Tab		\l : linefeed
 *	\h : Horizontal Tab	\v : Vertical Tab	\f : Formfeed
 *-------------------------------------------------------------------------
 */
read_quoted(infile, string)
	FILE   *infile;
	char    string[40];
{
	int     ch,
	        i,
	        delim;

	sc_cnt = others_cnt = comma_cnt = slash_cnt = semic_cnt = 0;
	while ((ch = getc(infile)) != EOF && ch != '"' && ch != '\'' && ch != '\n')
		;
	if (ch == '\n')
		write_error(13);
	delim = ch;
	i = 0;
	while ((ch = getc(infile)) != EOF && ch != delim && ch != '\n' && i < 39)
	{
		if (ch == '\\')
		{
			ch = getc(infile);
			switch (ch)
			{
				case 'n':
					ch = '\n';	/* NEWLINE */
					break;
				case 't':
					ch = '\t';	/* HORIZONTAL TAB */
					break;
				case 'l':
					ch = '\012';	/* LINEFEED */
					break;
				case 'v':
					ch = '\013';	/* VERTICAL TAB */
					break;
				case 'f':
					ch = '\014';	/* FORMFEED */
					break;
				default:
					write_error(13);
			}
		}
		string[i++] = ch;
		if (ch == ' ' || ch == '\n' || ch == '\t' || (ch >= 9 && ch <= 12))
			sc_cnt++;
		else
		{
			others_cnt++;
			if (ch == ',')
				comma_cnt++;
			else if (ch == '/')
				slash_cnt++;
			else if (ch == ';')
				semic_cnt++;
		}
	}
	if (ch == '\n')
		write_error(13);
	string[i] = '\0';
}


/*
 *-------------------------------------------------------------------------
 * is_letter:
 *	Returns 1 if its argument is an upper or lower case letter, 0
 * returned otherwise.
 *-------------------------------------------------------------------------
 */
is_letter(letter)
	int     letter;
{
	if ((letter >= 'A' && letter <= 'Z') || (letter >= 'a' && letter <= 'z'))
		return (1);
	else
		return (0);
}


/*
 *-------------------------------------------------------------------------
 * read_to_eoln:
 *	Reads to the end of the current line, ready for next command to be
 * read.
 *-------------------------------------------------------------------------
 */
read_to_eoln(infile)
	FILE   *infile;
{
	int     ch;

	while ((ch = getc(infile)) != '\n' && ch != EOF)
		;
}
