/* default.c
 * (c) 2002 Mikulas Patocka, Petr 'Brain' Kulhavy
 * This file is a part of the Links program, released under GPL
 */

#include "links.h"

void get_system_name()
{
	FILE *f;
	unsigned char *p;
#if defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME)
	struct utsname name;
	memset(&name, 0, sizeof name);
	if (!uname(&name)) {
		unsigned char *str = init_str();
		int l = 0;
		add_to_str(&str, &l, name.sysname);
		add_to_str(&str, &l, " ");
		add_to_str(&str, &l, name.release);
		add_to_str(&str, &l, " ");
		add_to_str(&str, &l, name.machine);
		if (l >= MAX_STR_LEN) str[MAX_STR_LEN - 1] = 0;
		strcpy(system_name, str);
		mem_free(str);
		return;
	}
#endif
#ifdef HAVE_POPEN
	memset(system_name, 0, MAX_STR_LEN);
	if (!(f = popen("uname -srm", "r"))) goto fail;
	if (fread(system_name, 1, MAX_STR_LEN - 1, f) <= 0) {
		pclose(f);
		goto fail;
	}
	pclose(f);
	for (p = system_name; *p; p++) if (*p < ' ') {
		*p = 0;
		break;
	}
	if (system_name[0]) return;
	fail:
#endif
	strcpy(system_name, SYSTEM_NAME);
}

struct option {
	int p;
	unsigned char *(*rd_cmd)(struct option *, unsigned char ***, int *);
	unsigned char *(*rd_cfg)(struct option *, unsigned char *);
	void (*wr_cfg)(struct option *, unsigned char **, int *);
	int min, max;	/* for double min and max are in 1/100's (e.g. 0.1 is min==10) */
	void *ptr;
	unsigned char *cfg_name;
	unsigned char *cmd_name;
};

extern struct option links_options[];

struct option *all_options[] = { links_options, NULL, };

unsigned char *_parse_options(int argc, unsigned char *argv[], struct option **opt)
{
	unsigned char *e, *u = NULL;
	while (argc) {
		int i;
		argv++, argc--;
		if (argv[-1][0] == '-') {
			struct option *options;
			struct option **op;
			for (op = opt; (options = *op); op++) for (i = 0; options[i].p; i++)
				if (options[i].rd_cmd && options[i].cmd_name &&
				    !strcasecmp(options[i].cmd_name, &argv[-1][1])) {
					if ((e = options[i].rd_cmd(&options[i], &argv, &argc))) {
						if (e[0]) fprintf(stderr, "Error parsing option %s: %s\n", argv[-1], e);
						return NULL;
					}
					goto found;
				}
			uu:
#ifdef GRDRV_DIRECTFB
                        if (!strncmp(argv[-1], "--dfb:", 6))
                                goto found;
#endif
			fprintf(stderr, "Unknown option %s\n", argv[-1]);
			return NULL;
		} else if (!u) u = argv[-1];
		else goto uu;
		found:;
	}
	if (u) return u;
	return "";
}

unsigned char *parse_options(int argc, unsigned char *argv[])
{
	return _parse_options(argc, argv, all_options);
}

unsigned char *get_token(unsigned char **line)
{
	unsigned char *s = NULL;
	int l = 0;
	int escape = 0;
	int quote = 0;
	
	while (**line == ' ' || **line == 9) (*line)++;
	if (**line) {
		for (s = init_str(); **line; (*line)++) {
			if (escape) 
				escape = 0;
			else if (**line == '\\') {
				escape = 1; 
				continue;
			}	
			else if (**line == '"') {
				quote = !quote;
			    	continue;
			}
			else if ((**line == ' ' || **line == 9) && !quote)
				break;
			add_chr_to_str(&s, &l, **line);
		}
	}
	return s;
}

void parse_config_file(unsigned char *name, unsigned char *file, struct option **opt)
{
	struct option *options;
	struct option **op;
	int err = 0;
	int line = 0;
	unsigned char *e;
	int i;
	unsigned char *n, *p;
	unsigned char *tok;
	int nl, pl;
	while (file[0]) {
		line++;
		while (file[0] && (file[0] == ' ' || file[0] == 9)) file++;
		n = file;
		while (file[0] && file[0] > ' ') file++;
		if (file == n) {
			if (file[0]) file++;
			continue;
		}
		nl = file - n;
		while (file[0] == 9 || file[0] == ' ') file++;
		p = file;
		while (file[0] && file[0] != 10 && file[0] != 13) file++;
		pl = file - p;
		if (file[0]) {
			if ((file[1] == 10 || file[1] == 13) && file[0] != file[1]) file++;
			file++;
		}
		tok = NULL;
		if (n[0] == '#') goto f;
		if (!(tok = get_token(&n))) goto f;
		nl = strlen(tok);
		for (op = opt; (options = *op); op++) 
		    	for (i = 0; options[i].p; i++) if (options[i].cfg_name && nl == strlen(options[i].cfg_name) && !casecmp(tok, options[i].cfg_name, nl)) {
				unsigned char *o = memacpy(p, pl);
				if ((e = options[i].rd_cfg(&options[i], o))) {
					if (e[0]) fprintf(stderr, "Error parsing config file %s, line %d: %s\n", name, line, e), err = 1;
				}
				mem_free(o);
				goto f;
			}
		fprintf(stderr, "Unknown option in config file %s, line %d\n", name, line);
		err = 1;
		f:
		if (tok) mem_free(tok);
	}
	if (err) fprintf(stderr, "\007"), sleep(1);
}

unsigned char *create_config_string(struct option *options)
{
	unsigned char *s = init_str();
	int l = 0;
	int i;
	add_to_str(&s, &l, "# This file is automatically generated by Links -- please do not edit.");
	for (i = 0; options[i].p; i++) if (options[i].wr_cfg)
		options[i].wr_cfg(&options[i], &s, &l);
	add_to_str(&s, &l, NEWLINE);
	return s;
}

#define FILE_BUF	1024

unsigned char cfg_buffer[FILE_BUF];

unsigned char *read_config_file(unsigned char *name)
{
	int h, r;
	int l = 0;
	unsigned char *s;
	if ((h = open(name, O_RDONLY | O_NOCTTY)) == -1) return NULL;
	set_bin(h);
	s = init_str();
	while ((r = read(h, cfg_buffer, FILE_BUF)) > 0) {
		int i;
		for (i = 0; i < r; i++) if (!cfg_buffer[i]) cfg_buffer[i] = ' ';
		add_bytes_to_str(&s, &l, cfg_buffer, r);
	}
	if (r == -1) mem_free(s), s = NULL;
	close(h);
	return s;
}

int write_to_config_file(unsigned char *name, unsigned char *c)
{
	int rr = strlen(c);
	int r = rr;
	int h, w;
	if ((h = open(name, O_WRONLY | O_NOCTTY | O_CREAT | O_TRUNC, 0666)) == -1) return -1;
	set_bin(h);
	while (r > 0) {
		if ((w = write(h, c + rr - r, r)) <= 0) {
			close(h);
			return -1;
		}
		r -= w;
	}
	close(h);
	return 0;
}

unsigned char *get_home(int *n)
{
	struct stat st;
	unsigned char *home = stracpy(getenv("HOME"));
	unsigned char *home_links;
	unsigned char *config_dir = stracpy(getenv("CONFIG_DIR"));

	if (n) *n = 1;
	if (!home) {
		int i;
		home = stracpy(path_to_exe);
		if (!home) {
			if (config_dir) mem_free(config_dir);
			return NULL;
		}
		for (i = strlen(home) - 1; i >= 0; i--) if (dir_sep(home[i])) {
			home[i + 1] = 0;
			goto br;
		}
		home[0] = 0;
		br:;
	}
	while (home[0] && dir_sep(home[strlen(home) - 1])) home[strlen(home) - 1] = 0;
	if (home[0]) add_to_strn(&home, "/");
	home_links = stracpy(home);
	if (config_dir) 	
	{
		add_to_strn(&home_links, config_dir);
		while (home_links[0] && dir_sep(home_links[strlen(home_links) - 1])) home_links[strlen(home_links) - 1] = 0;
		if (stat(home_links, &st) != -1 && S_ISDIR(st.st_mode)) {
			add_to_strn(&home_links, "/links");
	    	} else {
			fprintf(stderr, "CONFIG_DIR set to %s. But directory %s doesn't exist.\n\007", config_dir, home_links);
			sleep(3);
			mem_free(home_links);
			home_links = stracpy(home);
			add_to_strn(&home_links, ".links");
		}
		mem_free(config_dir);
	} else add_to_strn(&home_links, ".links");
	if (stat(home_links, &st)) {
		if (!mkdir(home_links, 0777)) goto home_creat;
		if (config_dir) goto failed;
		goto first_failed;
	}
	if (S_ISDIR(st.st_mode)) goto home_ok;
	first_failed:
	mem_free(home_links);
	home_links = stracpy(home);
	add_to_strn(&home_links, "links");
	if (stat(home_links, &st)) {
		if (!mkdir(home_links, 0777)) goto home_creat;
		goto failed;
	}
	if (S_ISDIR(st.st_mode)) goto home_ok;
	failed:
	mem_free(home_links);
	mem_free(home);
	return NULL;

	home_ok:
	if (n) *n = 0;
	home_creat:
#ifdef HAVE_CHMOD
	chmod(home_links, 0700);
#endif
	add_to_strn(&home_links, "/");
	mem_free(home);
	return home_links;
}

void init_home()
{
	get_system_name();
	links_home = get_home(&first_use);
	if (!links_home) {
		fprintf(stderr, "Unable to find or create links config directory. Please check, that you have $HOME variable set correctly and that you have write permission to your home directory.\n\007");
		sleep(3);
		return;
	}
}

void load_config_file(unsigned char *prefix, unsigned char *name)
{
	unsigned char *c, *config_file;
	config_file = stracpy(prefix);
	if (!config_file) return;
	add_to_strn(&config_file, name);
	if ((c = read_config_file(config_file))) goto ok;
	mem_free(config_file);
	config_file = stracpy(prefix);
	if (!config_file) return;
	add_to_strn(&config_file, ".");
	add_to_strn(&config_file, name);
	if ((c = read_config_file(config_file))) goto ok;
	mem_free(config_file);
	return;
	ok:
	parse_config_file(config_file, c, all_options);
	mem_free(c);
	mem_free(config_file);
}

void load_config()
{
	load_config_file("/etc/", "links.cfg");
	load_config_file(links_home, "links.cfg");
	load_config_file(links_home, "user.cfg");
}

int write_config_file(unsigned char *prefix, unsigned char *name, struct option *o, struct terminal *term)
{
	unsigned char *c, *config_file;
	if (!(c = create_config_string(o))) return -1;
	config_file = stracpy(prefix);
	if (!config_file) {
		mem_free(c);
		return -1;
	}
	add_to_strn(&config_file, name);
	if (write_to_config_file(config_file, c)) {
		if (term) msg_box(term, NULL, TEXT(T_CONFIG_ERROR), AL_CENTER, TEXT(T_UNABLE_TO_WRITE_TO_CONFIG_FILE), NULL, 1, TEXT(T_CANCEL), NULL, B_ENTER | B_ESC);
		mem_free(c);
		mem_free(config_file);
		return -1;
	}
	mem_free(c);
	mem_free(config_file);
	return 0;
}

void write_config(struct terminal *term)
{
#ifdef G
        if (F) update_driver_param();
#endif
        write_config_file(links_home, "links.cfg", links_options, term);

        /* And new style options too */
        save_options();
}

void add_nm(struct option *o, unsigned char **s, int *l)
{
	if (*l) add_to_str(s, l, NEWLINE);
	add_to_str(s, l, o->cfg_name);
	add_to_str(s, l, " ");
}

void add_quoted_to_str(unsigned char **s, int *l, unsigned char *q)
{
	add_chr_to_str(s, l, '"');
	while (*q) {
		if (*q == '"' || *q == '\\') add_chr_to_str(s, l, '\\');
		add_chr_to_str(s, l, *q);
		q++;
	}
	add_chr_to_str(s, l, '"');
}

unsigned char *num_rd(struct option *o, unsigned char *c)
{
	unsigned char *tok = get_token(&c);
	unsigned char *end;
	long l;
	if (!tok) return "Missing argument";
	l = strtolx(tok, &end);
	if (*end) {
		mem_free(tok);
		return "Number expected";
	}
	if (l < o->min || l > o->max) {
		mem_free(tok);
		return "Out of range";
	}
	*(int *)o->ptr = l;
	mem_free(tok);
	return NULL;
}

void num_wr(struct option *o, unsigned char **s, int *l)
{
	add_nm(o, s, l);
	add_knum_to_str(s, l, *(int *)o->ptr);
}

int getnum(unsigned char *s, int *n, int r1, int r2)
{
	unsigned char *e;
	long l = strtol(s, (char **)&e, 10);
	if (*e || !*s) return -1;
	if (l < r1 || l >= r2) return -1;
	*n = (int)l;
	return 0;
}

unsigned char *type_rd(struct option *o, unsigned char *c)
{
	unsigned char *err = "Error reading association specification";
	struct assoc new;
	unsigned char *w;
	int n;
	memset(&new, 0, sizeof(struct assoc));
	if (!(new.label = get_token(&c))) goto err;
	if (!(new.ct = get_token(&c))) goto err;
	if (!(new.prog = get_token(&c))) goto err;
	if (!(w = get_token(&c))) goto err;
	if (getnum(w, &n, 0, 32)) goto err_f;
	mem_free(w);
	new.cons = !!(n & 1);
	new.xwin = !!(n & 2);
	new.ask = !!(n & 4);
	if ((n & 8) || (n & 16)) new.block = !!(n & 16);
	else new.block = !new.xwin || new.cons;
	if (!(w = get_token(&c))) goto err;
	if (strlen(w) != 1 || w[0] < '0' || w[0] > '9') goto err_f;
	new.system = w[0] - '0';
	mem_free(w);
	update_assoc(&new);
	err = NULL;
	err:
	if (new.label) mem_free(new.label);
	if (new.ct) mem_free(new.ct);
	if (new.prog) mem_free(new.prog);
	return err;
	err_f:
	mem_free(w);
	goto err;
}

void type_wr(struct option *o, unsigned char **s, int *l)
{
	struct assoc *a;
	foreachback(a, assoc) {
		add_nm(o, s, l);
		add_quoted_to_str(s, l, a->label);
		add_to_str(s, l, " ");
		add_quoted_to_str(s, l, a->ct);
		add_to_str(s, l, " ");
		add_quoted_to_str(s, l, a->prog);
		add_to_str(s, l, " ");
		add_num_to_str(s, l, (!!a->cons) + (!!a->xwin) * 2 + (!!a->ask) * 4 + (!a->block) * 8 + (!!a->block) * 16);
		add_to_str(s, l, " ");
		add_num_to_str(s, l, a->system);
	}
}

unsigned char *ext_rd(struct option *o, unsigned char *c)
{
	unsigned char *err = "Error reading extension specification";
	struct extension new;
	memset(&new, 0, sizeof(struct extension));
	if (!(new.ext = get_token(&c))) goto err;
	if (!(new.ct = get_token(&c))) goto err;
	update_ext(&new);
	err = NULL;
	err:
	if (new.ext) mem_free(new.ext);
	if (new.ct) mem_free(new.ct);
	return err;
}

void ext_wr(struct option *o, unsigned char **s, int *l)
{
	struct extension *a;
	foreachback(a, extensions) {
		add_nm(o, s, l);
		add_quoted_to_str(s, l, a->ext);
		add_to_str(s, l, " ");
		add_quoted_to_str(s, l, a->ct);
	}
}

unsigned char *term_rd(struct option *o, unsigned char *c)
{
	struct term_spec *ts;
	unsigned char *w;
	int i;
	if (!(w = get_token(&c))) goto err;
	if (!(ts = new_term_spec(w))) {
		mem_free(w);
		goto end;
	}
	mem_free(w);
	if (!(w = get_token(&c))) goto err;
	if (strlen(w) != 1 || w[0] < '0' || w[0] > '3') goto err_f;
	ts->mode = w[0] - '0';
	mem_free(w);
	if (!(w = get_token(&c))) goto err;
	if (strlen(w) != 1 || w[0] < '0' || w[0] > '1') goto err_f;
	ts->m11_hack = w[0] - '0';
	mem_free(w);
	if (!(w = get_token(&c))) goto err;
	if (strlen(w) != 1 || w[0] < '0' || w[0] > '7') goto err_f;
	ts->col = (w[0] - '0') & 1;
	ts->restrict_852 = !!((w[0] - '0') & 2);
	ts->block_cursor = !!((w[0] - '0') & 4);
	mem_free(w);
	if (!(w = get_token(&c))) goto err;
	if ((i = get_cp_index(w)) == -1) goto err_f;
	ts->charset = i;
	mem_free(w);
	end:
	return NULL;
	err_f:
	mem_free(w);
	err:
	return "Error reading terminal specification";
}

unsigned char *term2_rd(struct option *o, unsigned char *c)
{
	struct term_spec *ts;
	unsigned char *w;
	int i;
	if (!(w = get_token(&c))) goto err;
	if (!(ts = new_term_spec(w))) {
		mem_free(w);
		goto end;
	}
	mem_free(w);
	if (!(w = get_token(&c))) goto err;
	if (strlen(w) != 1 || w[0] < '0' || w[0] > '3') goto err_f;
	ts->mode = w[0] - '0';
	mem_free(w);
	if (!(w = get_token(&c))) goto err;
	if (strlen(w) != 1 || w[0] < '0' || w[0] > '1') goto err_f;
	ts->m11_hack = w[0] - '0';
	mem_free(w);
	if (!(w = get_token(&c))) goto err;
	if (strlen(w) != 1 || w[0] < '0' || w[0] > '1') goto err_f;
	ts->restrict_852 = w[0] - '0';
	mem_free(w);
	if (!(w = get_token(&c))) goto err;
	if (strlen(w) != 1 || w[0] < '0' || w[0] > '1') goto err_f;
	ts->col = w[0] - '0';
	mem_free(w);
	if (!(w = get_token(&c))) goto err;
	if ((i = get_cp_index(w)) == -1) goto err_f;
	ts->charset = i;
	mem_free(w);
	end:
	return NULL;
	err_f:
	mem_free(w);
	err:
	return "Error reading terminal specification";
}

void term_wr(struct option *o, unsigned char **s, int *l)
{
	struct term_spec *ts;
	foreachback(ts, term_specs) {
		add_nm(o, s, l);
		add_quoted_to_str(s, l, ts->term);
		add_to_str(s, l, " ");
		add_num_to_str(s, l, ts->mode);
		add_to_str(s, l, " ");
		add_num_to_str(s, l, ts->m11_hack);
		add_to_str(s, l, " ");
		add_num_to_str(s, l, !!ts->col + !!ts->restrict_852 * 2 + !!ts->block_cursor * 4);
		add_to_str(s, l, " ");
		add_to_str(s, l, get_cp_mime_name(ts->charset));
	}
}

struct list_head driver_params = { &driver_params, &driver_params };

struct driver_param *get_driver_param(unsigned char *n)
{
	struct driver_param *dp;
	foreach(dp, driver_params) if (!strcasecmp(dp->name, n)) return dp;
	dp = mem_calloc(sizeof(struct driver_param) + strlen(n) + 1);
	dp->codepage = get_cp_index("iso-8859-1");
	strcpy(dp->name, n);
	add_to_list(driver_params, dp);
	return dp;
}

unsigned char *dp_rd(struct option *o, unsigned char *c)
{
	unsigned char *n, *param, *cp;
	struct driver_param *dp;
	if (!(n = get_token(&c))) goto err;
	if (!(param = get_token(&c))) {
		mem_free(n);
		goto err;
	}
	if (!(cp = get_token(&c))) {
		mem_free(n);
		mem_free(param);
		goto err;
	}
	dp=get_driver_param(n);
	dp->codepage=get_cp_index(cp);
	if (dp->param)mem_free(dp->param);
	dp->param=param;
	mem_free(cp);
	mem_free(n);
	return NULL;
	err:
	return "Error reading driver mode specification";
}

void dp_wr(struct option *o, unsigned char **s, int *l)
{
	struct driver_param *dp;
	foreachback(dp, driver_params) {
		if ((!dp->param || !*dp->param) && !dp->codepage) continue;
		add_nm(o, s, l);
		add_quoted_to_str(s, l, dp->name);
		add_to_str(s, l, " ");
		add_quoted_to_str(s, l, (dp->param) ? (dp->param) : (unsigned char*)"");
		add_to_str(s, l, " ");
		add_to_str(s, l, get_cp_mime_name(dp->codepage));
	}
}

unsigned char *gen_cmd(struct option *o, unsigned char ***argv, int *argc)
{
	unsigned char *r;
	if (!*argc) return "Parameter expected";
	(*argv)++; (*argc)--;
	if (!(r = o->rd_cfg(o, *(*argv - 1)))) return NULL;
	(*argv)--; (*argc)++;
	return r;
}

unsigned char *lookup_cmd(struct option *o, unsigned char ***argv, int *argc)
{
	ip__address addr;
	unsigned char *p = (unsigned char *)&addr;
	if (!*argc) return "Parameter expected";
	if (*argc >= 2) return "Too many parameters";
	(*argv)++; (*argc)--;
	if (do_real_lookup(*(*argv - 1), &addr)) {
#ifdef HAVE_HERROR
		herror("error");
#else
		fprintf(stderr, "error: host not found\n");
#endif
		return "";
	}
	printf("%d.%d.%d.%d\n", (int)p[0], (int)p[1], (int)p[2], (int)p[3]);
	fflush(stdout);
	return "";
}

unsigned char *version_cmd(struct option *o, unsigned char ***argv, int *argc)
{
	printf("Links " VERSION_STRING "\n");
	fflush(stdout);
	return "";
}

unsigned char *set_cmd(struct option *o, unsigned char ***argv, int *argc)
{
	*(int *)o->ptr = 1;
	return NULL;
}

unsigned char *setstr_cmd(struct option *o, unsigned char ***argv, int *argc)
{
	if (!*argc) return "Parameter expected";
	strncpy(o->ptr, **argv, o->max);
	((unsigned char *)o->ptr)[o->max - 1] = 0;
	(*argv)++; (*argc)--;
	return NULL;
}

unsigned char *force_html_cmd(struct option *o, unsigned char ***argv, int *argc)
{
	force_html = 1;
	return NULL;
} 

unsigned char *dump_cmd(struct option *o, unsigned char ***argv, int *argc)
{
	if (dmp != o->min && dmp) return "Can't use both -dump and -source";
	dmp = o->min;
	no_connect = 1;
	return NULL;
}

unsigned char *printhelp_cmd(struct option *o, unsigned char ***argv, int *argc)
{
/* Changed and splited - translation is much easier.
 * Print to stdout instead stderr (,,links -help | more''
 * is much better than ,,links -help 2>&1 | more'').
 */
fprintf(stdout, "%s%s%s%s%s%s\n", 

("links [options] URL\n\
Options are:\n\
\n\
 -g\n\
  Run in graphics mode.\n\
\n\
 -driver <driver name>\n\
  Graphics driver to use. Drivers are: x, svgalib, fb, directfb, pmshell, atheos.\n\
  Available drivers depend on your operating system and available libraries.\n\
\n\
 -mode <graphics mode>\n\
  Graphics mode. For SVGALIB it is in format COLUMNSxROWSxCOLORS --\n\
  for example 640x480x256, 800x600x64k, 1024x768x16M32\n\
\n\
 -async-dns <0>/<1>\n\
  Asynchronous DNS resolver on(1)/off(0). \n\
\n\
 -max-connections <max>\n\
  Maximum number of concurrent connections.\n\
  (default: 10)\n\
\n"),
(" -max-connections-to-host <max>\n\
  Maximum number of concurrent connection to a given host.\n\
  (default: 2)\n\
\n\
 -retries <retry>\n\
  Number of retries.\n\
  (default: 3)\n\
\n\
 -receive-timeout <sec>\n\
  Timeout on receive.\n\
  (default: 120)\n\
\n"),
(" -unrestartable-receive-timeout <sec>\n\
  Timeout on non restartable connections.\n\
  (default: 600)\n\
\n\
 -format-cache-size <num>\n\
  Number of formatted document pages cached.\n\
  (default: 5)\n\
\n\
 -memory-cache-size <Kbytes>\n\
  Cache memory in Kilobytes.\n\
  (default: 1024)\n\
\n"),
(" -http-proxy <host:port>\n\
  Host and port number of the HTTP proxy, or blank.\n\
  (default: blank)\n\
\n\
 -ftp-proxy <host:port>\n\
  Host and port number of the FTP proxy, or blank.\n\
  (default: blank)\n\
\n\
 -download-dir <path>\n\
  Default download directory.\n\
  (default: actual dir)\n\
\n"),
(" -assume-codepage <codepage>\n\
  Use the given codepage when the webpage did not specify\n\
  its codepage. (default: ISO 8859-1)\n\
\n\
 -anonymous\n\
  Restrict links so that it can run on an anonymous account.\n\
  No local file browsing. No downloads. Executing of viewers\n\
  is allowed, but user can't add or modify entries in\n\
  association table.\n\
\n\
 -no-connect\n\
  Runs links as a separate instance - instead of connecting to\n\
  existing instance.\n\
\n\
 -version\n\
  Prints the links version number and exit.\n\
\n\
 -help\n\
  Prints this help screen\n\
\n\
\n"),
("Keys:\n\
 	ESC	  display menu\n\
	^C	  quit\n\
	^P        scroll up (text mode)\n\
       	^N	  scroll down\n\
	[, ]	  scroll left, right\n\
	up, down  select link (text mode)\n\
	->	  follow link (text mode), scroll right (graphics mode)\n\
	<-	  go back (text mode), scroll left (graphics mode)\n\
	z	  go back\n\
	g	  go to url\n\
	G	  go to url based on current url\n\
	^G	  go to url based on current link\n\
	/	  search\n\
	?	  search back\n\
	n	  find next\n\
	N	  find previous\n\
	=	  document info\n\
	\\	  document source\n\
	d	  download\n\
	q	  quit or close current window\n\
	Ctrl-INS  copy to clipboard (OS/2 only)\n\
	Shift-DEL cut to clipboard (OS/2 only)\n\
	Shift-INS paste clipboard (OS/2 only)\n\
	Alt-1 .. Alt-9\n\
		  switch virtual screens (svgalib and framebuffer)\n\
"));

	fflush(stdout);
	return "";
}

void end_config()
{
	struct driver_param *dp;
	foreach(dp,driver_params)
		if (dp->param)mem_free(dp->param);
	free_list(driver_params);
	if (links_home) mem_free(links_home);
}

int reuse_window = 0;

int ggr = 0;
unsigned char ggr_drv[MAX_STR_LEN] = "";
unsigned char ggr_mode[MAX_STR_LEN] = "";
unsigned char ggr_display[MAX_STR_LEN] = "";

int anonymous = 0;

unsigned char system_name[MAX_STR_LEN];

unsigned char *links_home = NULL;
int first_use = 0;
int created_home = 0;

int no_connect = 0;
int base_session = 0;
int dmp = 0;
int force_html = 0;

int default_left_margin = HTML_LEFT_MARGIN;

int display_optimize=0;	/*0=CRT, 1=LCD RGB, 2=LCD BGR */
double bfu_aspect=1; /* 0.1 to 10.0, 1.0 default. >1 makes circle wider */
int aspect_on=1;

struct option links_options[] = {
	{1, printhelp_cmd, NULL, NULL, 0, 0, NULL, NULL, "?"},
	{1, printhelp_cmd, NULL, NULL, 0, 0, NULL, NULL, "h"},
	{1, printhelp_cmd, NULL, NULL, 0, 0, NULL, NULL, "help"},
	{1, printhelp_cmd, NULL, NULL, 0, 0, NULL, NULL, "-help"},
	{1, lookup_cmd, NULL, NULL, 0, 0, NULL, NULL, "lookup"},
	{1, version_cmd, NULL, NULL, 0, 0, NULL, NULL, "version"},
	{1, set_cmd, NULL, NULL, 0, 0, &no_connect, NULL, "no-connect"},
	{1, set_cmd, NULL, NULL, 0, 0, &reuse_window, NULL, "reuse-window"},
	{1, set_cmd, NULL, NULL, 0, 0, &anonymous, NULL, "anonymous"},
	{1, set_cmd, NULL, NULL, 0, 0, &ggr, NULL, "g"},
	{1, setstr_cmd, NULL, NULL, 0, MAX_STR_LEN, &ggr_drv, NULL, "driver"},
	{1, setstr_cmd, NULL, NULL, 0, MAX_STR_LEN, &ggr_mode, NULL, "mode"},
	{1, setstr_cmd, NULL, NULL, 0, MAX_STR_LEN, &ggr_display, NULL, "display"},
	{1, gen_cmd, num_rd, NULL, 0, MAXINT, &base_session, NULL, "base-session"},
	{1, force_html_cmd, NULL, NULL, 0, 0, NULL, NULL, "force-html"},
/*
	{1, dump_cmd, NULL, NULL, D_DUMP, 0, NULL, NULL, "dump"},
*/
	{1, dump_cmd, NULL, NULL, D_SOURCE, 0, NULL, NULL, "source"},

        {1, NULL, term_rd, term_wr, 0, 0, NULL, "terminal", NULL},
	{1, NULL, term2_rd, NULL, 0, 0, NULL, "terminal2", NULL},
	{1, NULL, type_rd, type_wr, 0, 0, NULL, "association", NULL},
	{1, NULL, ext_rd, ext_wr, 0, 0, NULL, "extension", NULL},
	{1, NULL, dp_rd, dp_wr, 0, 0, NULL, "video_driver", NULL},
        {0, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL},
};

extern struct history goto_url_history ;

int load_url_history()
{
	unsigned char *history_file ;
	FILE *fp ;
	char	url[MAX_INPUT_URL_LEN];

	if (anonymous) return 0;
	/* Must have been called after init_home */
	if (!links_home)
		return 0;
	history_file = stracpy (links_home) ;
	if (!history_file)
		return 0;
	add_to_strn(&history_file, "links.his");
	if (!(fp = fopen(history_file, "r")))
	{
		mem_free (history_file) ;
		return 0;
	}
	while (fgets (url, MAX_INPUT_URL_LEN, fp))
	{
		url[MAX_INPUT_URL_LEN - 1] = 0;
		if (*url) url[strlen(url)-1] = 0 ;
		add_to_history(&goto_url_history, url) ;
	}
	fclose (fp) ;
	mem_free (history_file) ;
	return 0 ;
}

int save_url_history()
{
	struct history_item* hi ;
	unsigned char *history_file ;
	int  i = 0 ;
	FILE *fp ;
	if (anonymous) return 0;

	/* Must have been called after init_home */
	if (!links_home)
		return 0;
	history_file = stracpy (links_home) ;
	if (!history_file)
		return 0;
	add_to_strn(&history_file, "links.his");
	if (!(fp = fopen(history_file, "w"))) {
		mem_free(history_file);
		return 0;
	}
	foreachback(hi,goto_url_history.items)
	{
		if (i++ > MAX_HISTORY_ITEMS)
			break ;
		else
			fputs (hi->d, fp), fputc ('\n', fp) ;
	}
	fclose (fp) ;
	mem_free (history_file) ;
	return 0 ;
}

