
/*
 *  TEX Device Driver  ver 2.02-
 *  copyright(c) 1988, 1989 by TSG, 1990- by SHIMA
 *
 *  option.c : standard option-parse module
 *
 *	modified for non PC-9801 machines by sempa 1992
 *	very slightly modified against warnings by Oh-Yeah? 25 May 1992
 *	modified for `Hidden Options' by Naochan! 6 August 1992
 *	modified for `COMMENT LINE' by sempa 2 Sept 1992
 *	slightly modified by Yakumo Nov. 1992
 */

#include <stdio.h>
#define _DEF_STDIO_H_
#include <stdlib.h>
#include <ctype.h>
#include <alloc.h>
#include <string.h>
#include <dos.h>
#include <dir.h>
#include "dd.h"
#include "option.h"
#include "err.h"

#define OPTION_FLG '-'
#define	OFF_USG	4
#define MAX_LINE 0x1000
#define	COMMENT_CHAR '#'

/* #define is_white(c) (c==' '||c=='\t'||c=='\n') */
#define is_hex(c)   ((c>='0'&&c<='9')||(c>='a'&&c<='f')||(c>='A'&&c<='F'))
#define is_oct(c)   (c>='0'&&c<='7')
#define is_dig(c)   (c>='0'&&c<='9')
#define hex_to_dig(c)   ((c>='0'&&c<='9')?(c-'0'):(toupper(c)-('A'-10)))
#define to_dig(c)   (c-'0')

/* err.c */
void more(void);

/* buffer.c */
char *dup_string(char *);

/* init.c */
extern const char *const pk_env_name;
extern char *pk_search_path;
extern char *pkd_search_path;
extern char *knj_search_path;
extern BOOL f_use_new_size_option;
extern int ARGC;

/* err.c */
extern BOOL f_wait;

#ifdef VFD
extern char *vfd_search_path;

#endif

#ifdef	FLI
extern char *fli_search_path;
#endif	/* FLI */

int sum_option;
static int f_file;	/* nesting the function reading options from a file */

static char *
    strchk(char *line, char *token)
	/* If the head token of 'line' matchs 'token', return 'line'.
     * Otherwise, returning NULL .
     * Modifed by K.Yoshizawa  Dec. 26, 1992
     */
{
	int alpha_token;

	/* オプションのキーワードがアルファベットであるか否か */
	alpha_token = isalpha(*token);

	while (*token)
		if (*token++ != *line++)
			return (NULL);

	if (alpha_token && isalpha(*line)) return (NULL);

	return (line);
}

static int stol(char *str, long *num)
	/* set string-value to long-integer. Oct- or Hex-values in thought.
	 * Return value : 0:OK -1:ERROR
     * Modifed by K.Yoshizawa  Dec. 26, 1992
     */
{
	*num = strtol(str, &str, 0);
	if (*str != '\0') return -1;
	return 0;
}

int strlcmp(char *s, char *t)
{
	return(strncmp(s, t, strlen(t)));
}

#ifndef	NNSZ
int length_to_sp(char *str, SCALED_PT *sp)
	/* Set string-value and unit (ex. 12.3mm, 5.4in, etc.) to scaled point.
	 * Return value : 0:OK -1:ERROR
     * By K.Yoshizawa  Dec. 26, 1992
     */
{
	double val, dpi;
	char unit[32], unit2[32];

	if (sscanf(str, "%lf%30s", &val, &unit) != 2) return -1;

	if (strlcmp(unit, "mm") == 0) {
	  *sp = MM_TO_SP(val);
	} else if (strlcmp(unit, "cm") == 0) {
	  *sp = MM_TO_SP(val*10.0);
	} else if (strlcmp(unit, "in") == 0) {
	  *sp = IN_TO_SP(val);
	} else if (strlcmp(unit, "pt") == 0) {
	  *sp = PT_TO_SP(val);
	} else if (sscanf(unit, "dot/%lf%s", &dpi, unit2) == 2
           &&  strlcmp(unit2, "dpi") == 0) {
	  *sp = IN_TO_SP(val/dpi);
	} else {
	  return -1;
	}

	return 0;
}
/* int.c prtinit.c */
extern DIMENSION dviout_dimension;
extern int enlarge;

/*
 *  flag & 1: vertival in case of 長さ?
 *  flag & 2: enlarge (-e option)?
 */
int length_to_dot(char *s0, int *val, int flag)
{
	SCALED_PT pt;
	char *s;
	int	cond;

	s = s0;
	if ((cond = length_to_sp(s, &pt)) == 0){
		*val = ROUND(SP_TO_IN(pt)
				*((flag & 1)?dviout_dimension.DPI:dviout_dimension.dpi));
	}
	else if (isdigit(*s)){
		*val = atoi(s);
	}
	else
		return(*val = 0);
	if ((flag & 2) && enlarge)
		*val = ((long)*val)*enlarge/1000;
rept:
	while(isdigit(*++s));
	if (!cond){						/* 長さ */
		if (*s == '.') goto rept;
		while(isalpha(*++s));
		if (*s == '/' && *(s-1) == 't') goto rept;	/* <num>dot/<num>dpi */
	}
	return(s - s0);
}
#endif

static int var_set(ARG_TABLE *option, char *arg)
	/* set args on variables directed in argment-table .
     * Modifed by K.Yoshizawa  Dec. 26, 1992
	 */
{
    int  sts = 0;
    long ltmp;

	ENTER("var_set");

	if (*arg == '=' || *arg == ':') arg++;

	switch (option->type) {
	  case HIDDEN_BOOLEAN:
	  case BOOLEAN:
		  if (*arg == '+') {
			  *(BOOL *)option->var = TRUE;
			  arg++;
		  } else {
			  if (*arg == '-') {
				  *(BOOL *)option->var = FALSE;
				  arg++;
			  } else {
				  *(BOOL *)option->var =
					  (*(BOOL *)option->var == TRUE) ? FALSE : TRUE;
			  }
		  }
		  if (*arg != '\0') sts = -1;
		  break;
	  case HIDDEN_INTEGER:
	  case INTEGER:
		  sts = stol(arg, &ltmp);
		  *(int *)option->var = ltmp;
		  break;
	  case HIDDEN_LONGINT:
	  case LONGINT:
		  sts = stol(arg, (long *)option->var);
		  break;
	  case HIDDEN_PROCEDURE:
	  case PROCEDURE:
		  (*(void (*)(char *))option->var) (arg);
		  break;
	  case HIDDEN_STRING:
	  case STRING:
		  *(char **)option->var = dup_string(arg);
		  break;
	  case HIDDEN_LENGTH:
	  case LENGTH:
#ifndef	NNSZ
		  sts = length_to_sp(arg, (SCALED_PT *)option->var);
		  break;
#endif
	  default:
		  sts = -1;
		  break;
	}
    RETURN(sts);
}

static BOOL analyse(ARG_TABLE *tbl, char *line)
	/* option-analyse */
{
	char *arg;

	ENTER("analyse");

	if (*line++ != OPTION_FLG) {
		if ((arg = strchr(--line, '=:')) != NULL) {
			*arg++ = '\0';
			if (!strcmp(line, pk_env_name)) {
				pk_search_path = strdup(arg);
				RETURN(TRUE);
			}
			else if (!strcmp(line, "TEXPKD")) {
				pkd_search_path = strdup(arg);
				RETURN(TRUE);
			}
			else if (!strcmp(line, "TEXKNJ")) {
				knj_search_path = strdup(arg);
				RETURN(TRUE);
			}
#ifdef VFD
			else if (!strcmp(line, "TEXVFD")) {
				vfd_search_path = strdup(arg);
				RETURN(TRUE);
			}
#endif
#ifdef FLI
			else if (!strcmp(line, "TEXFLI")) {
				fli_search_path = strdup(arg);
				RETURN(TRUE);
			}
#endif
		}
		RETURN(FALSE);
	}
	for (arg = line; *arg;)
		sum_option += *arg++;
	for (; tbl->option; tbl++)
		if ((arg = strchk(line, tbl->option)) != NULL) {
			if(!var_set(tbl, arg))
				RETURN(TRUE);
			error(ILLEGAL_ARGS, "bad parameter -%s", line);
			RETURN(FALSE);
		}
	error(ILLEGAL_ARGS, "no match -%s", line);
	RETURN(FALSE);
}

void set_option(ARG_TABLE *tbl, int argc, char **argv)
	/* analyse option-arguments of command-line */
{
	ENTER("set_option");

	while (--argc && analyse(tbl, *++argv));

	END();
}

static BOOL is_white(int c)
{
	static BOOL f_in_comment = FALSE;

	if (c == EOF)
		return FALSE;
	if (!f_in_comment) {
		if (c == COMMENT_CHAR) {
			f_in_comment = TRUE;
			return TRUE;
		}
		else
			return (c == ' ' || c == '\t' || c == '\n');
	}
	else {
		if (c == '\n')
			f_in_comment = FALSE;
		return TRUE;
	}
}

/*  Make pathname
 *  path <-  dir [+ '/'] + name
 *		by changing extension by ext
 *           if (ext != NULL
 *				&& (*ext == '.' || (*ext != '.' && (name has no extention))
 *           )
 *  if (path == dir) path += dir [+ '/'] + name
 *
 *  if (dir == NULL) "dir [+ '/']" is omitted.
 *
 *  RETURN   0: usual path   1: absolute path
 *        +  2 if extension is changed
 */
int make_path(char *path, char *dir, char *name, char *ext)
{
	int i, ch, abspath;
	char *p;
								/* check if name is absolute path */
	if    (name[0] == '\\'
	  ||   name[0] == '/'
	  ||  (name[0] != '\0' && name[1] == ':')) {
		abspath = 1;
		*path = 0;
	}
	else{
		abspath = 0;
		if (path != dir){
			*path = 0;
			if (dir != NULL)
				strncpy(path, dir, MAXPATH);
		}
	}

	if ((i = strlen(path)) > 0){
		if ((ch = path[i-1]) != ':' && ch != '\\' && ch != '/')
		strcat(path, "\\");
	}
	strncat(path, name, MAXPATH-1);

	if (ext == NULL) goto quit;
	if ((p = strrchr(path, '.')) != NULL){
		i = 0;
		while(p[++i]){
			if (p[i] == '\\' || p[i] == '/') goto no_ext;
		}
		if (*ext != '.' || strcmp(p, ext) == 0) goto quit;
		*p = 0;
	}
no_ext:
	if (*ext != '.') strncat(path, ".", MAXPATH);
	strncat(path, ext, MAXPATH);
	abspath |= 2;
quit:
	return(abspath);
}

FILE *open_config(char *config_file)
	/* by K.Yoshizawa Dec. 26 1992
	 */
{
	int trial;
	FILE *fp;
	char *p, path[256];

#define	ABS_PATH 1

	for (trial=1; trial<=2; trial++) {
		switch(trial) {
		case 1:
			/* 1回目 parameter_file をそのまま使う。したがって、カレント・
			 * ディレクトリまたは絶対パスで指定されたディレクトリを探す
			 * ことになる。
			 */
			strcpy(path, config_file);
			break;
		case 2:
			/* 2回目 環境変数 TEXCFG で指定されたディレクトリを探す。
			 */
			if ((p=getenv("TEXCFG")) == NULL) continue;
			if (make_path(path, p, config_file, NULL) & ABS_PATH) continue;
			break;
		}
		if ((fp = fopen(path, "rt")) == NULL) continue;
		return fp;
	}
	return NULL;
}

void set_config(ARG_TABLE *tbl, char *config_file)
	/* analyse option-arguments of parameter-file */
	/* Modified by K.Yoshizawa Dec. 26 1992
	 * For more effective file search method.
	 */
{
	char line[MAX_LINE], *tmp;
	int code;
	FILE *fptr;

	ENTER("set_config");

	if (f_file >= 2) {
		error(ILLEGAL_ARGS, "Nesting of parameter file %s", config_file);
	}
	if ((fptr = open_config(config_file)) == NULL){
		if (!(make_path(line, NULL, config_file, "par") & 2)
		  || (fptr = open_config(line)) == NULL)
		error(WARNING, "can't find parameter file '%s'", config_file);
	}
	f_file++;

	do {
		while (code = getc(fptr), is_white(code));
		for (tmp = line; code != EOF && !is_white(code)
			 && tmp - line < MAX_LINE - 1;) {
			*tmp++ = code;
			code = getc(fptr);
		}

		*tmp = '\0';
		while (code = getc(fptr), is_white(code));

		if (code != EOF)
			ungetc(code, fptr);
	} while (analyse(tbl, line) && code != EOF);

	fclose(fptr);
	f_file--;
	END();
}

void option_usage(ARG_TABLE *tbl)
	/* print out usage */
{
	int i, j, lines, len;

#ifdef	PC9801
	lines = 15;					/* 画面構成の問題上...by Naochan! & OkI*/
#else
	lines = peekb(0x40, 0x84) - 8;
#endif

#define	tmp_msg		(common_work+1024)
#define	tmp_msg1	(common_work+1024+512)

	for (i = OFF_USG; tbl->option; i++, tbl++) {
		if (tbl->type < HIDDEN_BOOLEAN){
			/* enable the `Hidden option' (Naochan!)
             */
			if ((j = tbl->message[0]) <= 0 || j >= 0x20)
				strcpy(tmp_msg, tbl->message);
			else
				strcpy(tmp_msg+j, tbl->message + 1);
			if(tmp_msg[len = strlen(tmp_msg)-1] == 1)
				*((int *)(tmp_msg + len)) = *((int *)(tmp_msg1 + len));
			strcpy(tmp_msg1, tmp_msg);

			strcpy(common_work, tbl->option);

			if(tbl->type != BOOLEAN){
				if(*common_work != '=') strcat(common_work, "=");
				printf(" -%-5s: %s\n", common_work, tmp_msg);
			}
			else{
				if (ARGC>1){
					printf(" -%-5s: %s [%s]\n", common_work,
						tmp_msg, (*(BOOL *)tbl->var == TRUE)?"ON":"OFF");
				}
				else{
#ifdef JAPANESE
					printf(" -%-5s: %s [デフォルト %s]\n", common_work,
#else
					printf(" -%-5s: %s [default %s]\n", common_work,
#endif
						tmp_msg, (*(BOOL *)tbl->var == TRUE)?"+":"-");
				}
			}
		}
		if (i % lines == 0 && f_wait) {
			more();
#if		defined(PC9801)
			printf("\x0b");
#elif	defined(J3100)
			printf("\x1b[l" "\n" "\x1b[2A");
#elif	defined(DOSV)
			printf("\n\x1b[2A");
#endif
		}
	}
}

/* end of file : option.c */
