/*
 *  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 <string.h>

#ifndef  UNIX
#ifdef MSVC
#include "msvcdir.h"
#else
#include <dir.h>
#endif
# ifdef	GCC1
#include <djgppstd.h>
# endif
#endif

#include "dd.h"
#include "option.h"
#include "err.h"
#include "inter.h"

#define OPTION_FLG '-'
#define MAX_LINE 0x3000
#define	COMMENT_CHAR '#'

#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 *);

/* tabctlr.c */
#ifdef WIN32
void ReserveIntPara(char *, int);
void ReserveStringPara(char *, char *);
void ReserveString(char *, char *, char *);
#endif

/* 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 int  f_init;

/* err.c */
extern int f_wait;

/* prtinit.c */
extern ARG_TABLE option[];
extern char *option_str[];

/* wndproc.c */
#ifdef	WIN32
int f_secure;
#endif

#if	defined(PC9801) && defined(DOSV)
extern int machine;
#endif

#ifdef VFD
extern char *vfd_search_path;
#endif

#ifdef	FLI
extern char *fli_search_path;
#endif	/* FLI */

#ifdef	TTFONT
extern char *tfm_search_path;
#endif	/* TTFONT */

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
	 */
{
	char *strend;
	
	*num = strtol(str, &strend, 0);
	if (*strend != '\0') return -1;
	return 0;
}

int strlcmp(const char *s, const char *t)
{
	return(strncmp(s, t, strlen(t)));
}

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 (strlcmp(unit, "bp") == 0) {
	  *sp = BP_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);
}

int var_set(ARG_TABLE *opt, char *arg)
	/* set args on variables directed in argment-table .
	 * Modifed by K.Yoshizawa  Dec. 26, 1992
	 */
{
	int  sts = 0;
	BOOL same = FALSE;
	long ltmp;

	ENTER("var_set");
	if (*arg == '=' || *arg == ':') arg++;

	switch (opt->type) {
	  case HIDDEN_BOOLEAN:
	  case BOOLTYPE:
		  if (*arg == '+') {
			  if(*(BOOL *)opt->var == TRUE)
				return 0;
			  *(BOOL *)opt->var = TRUE;
			  arg++;
		  } else {
			  if (*arg == '-') {
					if(*(BOOL *)opt->var == FALSE)
						return 0;
				  *(BOOL *)opt->var = FALSE;
				  arg++;
			  } else {
				  *(BOOL *)opt->var =
					  (*(BOOL *)opt->var == TRUE) ? FALSE : TRUE;
			  }
		  }
		  if (*arg != '\0'){
			same = (*(BOOL *)opt->var == (BOOL)opt->def);
			*(BOOL *)opt->var = (BOOL)opt->def;		/* default value */
			sts = -1;
		  }
		  break;

	  case HIDDEN_INTEGER:
	  case INTEGER:
#ifdef	WIN32
	  case HIDDEN_LONGINT:
	  case LONGINT:
#endif
		  sts = stol(arg, &ltmp);
		  if(ltmp != opt->def && (ltmp < opt->top || ltmp > opt->end) ){
			sts = -1;
			ltmp = opt->def;					/* default value */
			same = (*(int *)opt->var == ltmp);
		  }else if(*(int *)opt->var == ltmp)
			return 0;
		  *(int *)opt->var = ltmp;
		  break;

#ifndef	WIN32
	  case HIDDEN_LONGINT:
	  case LONGINT:
		  sts = stol(arg, (long *)opt->var);
		  if(*(long *)opt->var < opt->top || *(long *)opt->var > opt->end){
			sts = -1;
			*(long *)opt->var = opt->def;		/* default value */
		  }
		  break;
#endif

	  case HIDDEN_PROCEDURE:
	  case PROCEDURE:
		  if(arg != NULL){
			if((ltmp = opt->top) != 0){
				if(option_str[ltmp] != NULL){
					if(strcmp(option_str[ltmp], arg) == 0)
						return 0;
					Free(option_str[ltmp]);
				}
				option_str[ltmp] = dup_string(arg);
			}
		  }
		  (*(void (*)(char *))opt->var) (arg);
		  break;

	  case HIDDEN_STRING:
	  case STRING:
		  if(*(char **)opt->var == NULL){
				if(strcmp(arg, NULL_STR) == 0)
					return 0;
		  }else{
			if(strcmp(arg, *(char **)opt->var) == 0)
				return 0;
			Free(*(char **)opt->var);
		  }
		  *(char **)opt->var = strcmp(arg, NULL_STR)?dup_string(arg):NULL;
		  break;

	  case HIDDEN_LENGTH:
	  case LENGTH:
		  sts = length_to_sp(arg, (SCALED_PT *)&ltmp);
		  if( ltmp != opt->def && (ltmp < opt->top || ltmp > opt->end) ){
				ltmp = opt->def;
				sts = -1;
		  }else if( *(SCALED_PT *)opt->var == ltmp)
			return 0;
		  *(SCALED_PT *)opt->var = ltmp;
		  break;

	  case SKIP:
		  break;

	  default:
		  sts = -1;
		  break;
	}
	f_init |= opt->init;
	if(!same)
		f_Expand = TRUE;
	RETURN(sts);
}

char *show_option(ARG_TABLE *tbl, int mode)
	/* print out usage */
{
#define	tmp_msg		(common_work+1024)
#define	tmp_msg2	(common_work+2048)

	strcpy(tmp_msg, tbl->message);
	strcpy(common_work, tbl->option);

	if(tbl->type != BOOLTYPE){
		if(*common_work != '=') strcat(common_work, "=");
		sprintf(tmp_msg2, " -%-5s: %s\n", common_work, tmp_msg);
	}
	else{
		if (mode>0){
			sprintf(tmp_msg2, " -%-5s: %s [%s]\n", common_work,
				tmp_msg, (*(BOOL *)tbl->var == TRUE)?"ON":"OFF");
		}
		else{
#ifdef JAPANESE
			sprintf(tmp_msg2, " -%-5s: %s [デフォルト %s]\n", common_work,
#else
			sprintf(tmp_msg2, " -%-5s: %s [default %s]\n", common_work,
#endif
				tmp_msg, (*(BOOL *)tbl->var == TRUE)?"+":"-");
		}
	}
	return (char *)tmp_msg2;
}

static void *analyse(ARG_TABLE *tbl, char *line, int mode)
	/* option-analyse */
{
	char *arg;

	ENTER("analyse");

	if(*line == OPTION_FLG)
		line++;
	for (; tbl->option; tbl++)
		if ((arg = strchk(line, tbl->option)) != NULL) {
			if(mode == SHOW_OPTION)
				RETURN((void *)show_option(tbl, mode));
			if(mode == CHECK_OPTION || !var_set(tbl, arg))
				RETURN(tbl);
			error(ILLEGAL_ARGS, "bad parameter -%s", line);
			RETURN(NULL);
		}
	if(mode != CHECK_OPTION)
		error(ILLEGAL_ARGS, "no match -%s", line);
	RETURN(NULL);
}

void set_option(int argc, char **argv)
	/* analyse option-arguments of command-line */
{
	ENTER("set_option");

	while (--argc && **++argv == OPTION_FLG &&
		analyse(option, *argv, SET_OPTION));

	END();
}

static BOOL f_in_comment = FALSE;

static BOOL is_white(int c)
{

	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;
	}
}

static BOOL expand_env(char *line)
{
	char sub[MAX_LINE], *tmp, *tmpe, *penv;
	BOOL exp_env = FALSE;

	tmpe = NULL;
	for(tmp = line; *tmp; tmp++){
		if(*tmp == '%'){
			if(tmpe == NULL || tmp <= tmpe+1)
				tmpe = tmp;
			else{
				*tmp = 0;
				penv=getenv(tmpe+1);
				if(penv != NULL
				  && (tmp-line) + strlen(penv) +strlen(tmp+1) < MAX_LINE -1){
					strcpy(sub, tmp+1);
					strcpy(tmpe, penv);
					tmp = tmpe+strlen(tmpe);
					strcpy(tmp--, sub);
					tmpe = NULL;
					exp_env = TRUE;
break;
				}else
					*(tmpe = tmp) = '%';
			}
		}
	}
	return exp_env;
}

static char *EXPENV = "expandenv=";

void *SetPara(char *org_para, int mode)
{
	ARG_TABLE *tbl;
	char line[MAX_LINE], *tmp, *para;
#ifdef	WIN32
	char *pt;
#endif
	int code, f_skip, sep_ch, f_env;

	if(mode == GET_TOP)
		return((void *)&option[0]);
	para = org_para;
	tbl = NULL;
	f_env = 0;

	while(1) {
		f_skip = sep_ch = 0;
		do{
			code = *para++;
			if(code == ';'){
				if(strstr(para, EXPENV) == para)
					f_env = (*(para+strlen(EXPENV)) == '1')?1:0;
				f_skip = 1;
				ShowMessage(para,"TEST",SM_OKCONT);
			}else if(code == '[' || code == '#')
				f_skip = 1;
			else if(code == '\n')
				f_skip = 0;
		}while(is_white(code)||f_skip);
		for (tmp = line; code != 0  && (sep_ch > 0 ||!is_white(code))
			 && tmp - line < MAX_LINE - 1;) {
			*tmp++ = code;
			if(code == '=' && !sep_ch){
				if((code = *para++) == 0x22 || code == 0x27){  /* ' or " */
					sep_ch = code;
				}else{
					sep_ch = -1;
					continue;
				}
			}
			if((code = *para++) == sep_ch){
				if(sep_ch > 0)
					code = *para++;
				break;
			}
		}
		*tmp = '\0';
		if(*line == 0){
			f_in_comment = FALSE;
			return tbl;
		}
		if(f_env)
			expand_env(line);
#ifdef	WIN32
		if(mode == SAVE_REG){
			tbl = analyse(&option[0], line, SET_OPTION);
			if(tbl == NULL)
				continue;
			switch(tbl->type){
/*			  case HIDDEN_BOOLEAN: */
			  case BOOLTYPE:
/*			  case HIDDEN_INTEGER: */
			  case INTEGER:
/*			  case HIDDEN_LONGINT: */
			  case LONGINT:
				ReserveIntPara(tbl->option, GetParaInt(tbl->option));
				break;

/*			  case HIDDEN_PROCEDURE: */
			  case PROCEDURE:
/*			  case HIDDEN_STRING: */
			  case STRING: 
				pt = line;
				while(*++pt){
					if(*pt == '=' || *pt == ':'){
						if(strcmp(tbl->option, "src"))
							ReserveStringPara(tbl->option, pt+1);
						else
							ReserveString("Settings", "src", pt+1);
						break;
					}
				}
				break;

/*			  case HIDDEN_LENGTH: */
			  case LENGTH:
				ReserveIntPara(tbl->option, 
					(int)SP_TO_MM(GetParaInt(tbl->option)*10));
				break;

			  default:
				break;
			}
			continue;
		}
#endif
		if(    f_secure
			&& mode == SET_OPTION
			&& (tbl = analyse(&option[0], line, CHECK_OPTION)) != NULL
			&& (!strcmp(tbl->option, "dviprt") || !strcmp(tbl->option, "src"))
			&& !AskYes(line, "Accept the following?"))	/* security */
				continue;
		tbl = analyse(&option[0], line, mode);
		if(code == 0 || tbl == NULL || mode == CHECK_OPTION){
			f_in_comment = FALSE;
			return(tbl);
		}
	}
}

/*  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 != '/'){
			path[i] = PATH_SEP;
			path[i+1] = 0;
		}
	}
	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[MAXPATH];

#define	ABS_PATH 1

	for (trial=1; trial<=2; trial++) {
		switch(trial) {
		case 1:
			/* 1回目 parameter_file をそのまま使う。したがって、カレント・
			 * ディレクトリまたは絶対パスで指定されたディレクトリを探す
			 * ことになる。
			 */
			strncpy(path, config_file, MAXPATH-1);
			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 = fopenf(path, "rt")) == NULL) continue;
		return fp;
	}
	return NULL;
}

void set_config(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, f_skip, sep_ch, f_env;
	FILE *fptr;

	ENTER("set_config");

	if(config_file == NULL || !*config_file)
		return;
	if (f_file >= 8) {
		error(ILLEGAL_ARGS, "Nesting of parameter file %s", config_file);
		END();
	}
	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);
		return;
	}
	f_file++;
	f_env = 0;

	do {
		f_skip = sep_ch = 0;
		do{
			code = getc(fptr);
			if(code == ';'){
				tmp = EXPENV;
				while(*tmp && (code = getc(fptr)) == *tmp++);
				if(*tmp == 0)
					f_env = ((code = getc(fptr)) == '1')?1:0;
				f_skip = 1;
			}
			if(code == '\n')
				f_skip = 0;
			else if(code == EOF)
				goto end;
		}while(is_white(code)||f_skip);
		for (tmp = line; code != 0  && (sep_ch > 0 ||!is_white(code))
			 && tmp - line < MAX_LINE - 1;) {
			*tmp++ = code;
			if(code == '=' && !sep_ch){
				if((code = getc(fptr)) == 0x22 || code == 0x27){  /* ' or " */
					sep_ch = code;
				}else{
					sep_ch = -1;
					continue;
				}
			}
			if((code = getc(fptr)) == sep_ch){
				if(sep_ch > 0)
					getc(fptr);
				break;
			}
		}
		*tmp = '\0';
		if(f_env)
			expand_env(line);
		while (code = getc(fptr), is_white(code));

		if (code != EOF)
			ungetc(code, fptr);
	} while (analyse(option, line, SET_OPTION) != NULL && code != EOF);
end:
	f_in_comment = FALSE;
	fclose(fptr);
	f_file--;
	END();
}


/* end of file : option.c */
