links

lynx-like text mode web browser
git clone anongit@rnpnr.xyz:links.git
Log | Files | Refs | Feed | README | LICENSE

default.c (47033B)


      1 /* default.c
      2  * (c) 2002 Mikulas Patocka, Petr 'Brain' Kulhavy
      3  * This file is a part of the Links program, released under GPL
      4  *
      5  * Does the configuration file.
      6  */
      7 
      8 #include <limits.h>
      9 #include <string.h>
     10 #include <sys/stat.h>
     11 #include <sys/types.h>
     12 #include <unistd.h>
     13 
     14 #include "links.h"
     15 
     16 unsigned char system_name[MAX_STR_LEN];
     17 
     18 static void
     19 get_system_name(void)
     20 {
     21 	{
     22 		struct utsname name;
     23 		int rs;
     24 		memset(&name, 0, sizeof name);
     25 		EINTRLOOP(rs, uname(&name));
     26 		if (!rs) {
     27 			unsigned char *str = NULL;
     28 			size_t l;
     29 			l = add_to_str(&str, 0, cast_uchar name.sysname);
     30 			l = add_chr_to_str(&str, l, ' ');
     31 			l = add_to_str(&str, l, cast_uchar name.release);
     32 			l = add_chr_to_str(&str, l, ' ');
     33 			l = add_to_str(&str, l, cast_uchar name.machine);
     34 			safe_strncpy(system_name, str, MAX_STR_LEN);
     35 			free(str);
     36 			return;
     37 		}
     38 	}
     39 	strcpy(cast_char system_name, SYSTEM_NAME);
     40 }
     41 
     42 struct option {
     43 	int p;
     44 	unsigned char *(*rd_cmd)(struct option *, unsigned char ***, int *);
     45 	unsigned char *(*rd_cfg)(struct option *, unsigned char *);
     46 	size_t (*wr_cfg)(struct option *, unsigned char **, size_t);
     47 	int min, max; /* for double min and max are in 1/100's (e.g. 0.1 is
     48 	                 min==10) */
     49 	void *ptr;
     50 	char *cfg_name;
     51 	char *cmd_name;
     52 };
     53 
     54 static unsigned char *
     55 p_arse_options(int argc, unsigned char *argv[], struct option **opt)
     56 {
     57 	unsigned char *e, *u = NULL;
     58 	int i;
     59 	for (i = 0; i < argc; i++) {
     60 		if (strlen(cast_const_char argv[i]) >= INT_MAX) {
     61 			fprintf(stderr, "Too long parameter\n");
     62 			return NULL;
     63 		}
     64 	}
     65 	while (argc) {
     66 		argv++;
     67 		argc--;
     68 		if (argv[-1][0] == '-') {
     69 			struct option *options;
     70 			struct option **op;
     71 			for (op = opt; (options = *op); op++)
     72 				for (i = 0; options[i].p; i++)
     73 					if (options[i].rd_cmd
     74 					    && options[i].cmd_name
     75 					    && !casestrcmp(
     76 						cast_uchar options[i].cmd_name,
     77 						&argv[-1][1])) {
     78 						if ((e = options[i].rd_cmd(
     79 							 &options[i], &argv,
     80 							 &argc))) {
     81 							if (e[0])
     82 								usage();
     83 							return NULL;
     84 						}
     85 						goto found;
     86 					}
     87 uu:
     88 			usage();
     89 			return NULL;
     90 		} else if (!u)
     91 			u = argv[-1];
     92 		else
     93 			goto uu;
     94 found:;
     95 	}
     96 	if (u)
     97 		return u;
     98 	return cast_uchar "";
     99 }
    100 
    101 static unsigned char *
    102 get_token(unsigned char **line)
    103 {
    104 	unsigned char *s = NULL;
    105 	int l = 0;
    106 	int escape = 0;
    107 	int quote = 0;
    108 
    109 	while (**line == ' ' || **line == 9)
    110 		(*line)++;
    111 	if (**line) {
    112 		for (s = NULL; **line; (*line)++) {
    113 			if (escape)
    114 				escape = 0;
    115 			else if (**line == '\\') {
    116 				escape = 1;
    117 				continue;
    118 			} else if (**line == '"') {
    119 				quote = !quote;
    120 				continue;
    121 			} else if ((**line == ' ' || **line == 9) && !quote)
    122 				break;
    123 			l = add_chr_to_str(&s, l, **line);
    124 		}
    125 	}
    126 	return s;
    127 }
    128 
    129 static void
    130 parse_config_file(unsigned char *name, unsigned char *file, struct option **opt)
    131 {
    132 	struct option *options;
    133 	struct option **op;
    134 	int err = 0;
    135 	int line = 0;
    136 	unsigned char *e;
    137 	int i;
    138 	unsigned char *n, *p;
    139 	unsigned char *tok;
    140 	int nl, pl;
    141 	while (file[0]) {
    142 		line++;
    143 		while (file[0] && (file[0] == ' ' || file[0] == 9))
    144 			file++;
    145 		n = file;
    146 		while (file[0] && file[0] > ' ')
    147 			file++;
    148 		if (file == n) {
    149 			if (file[0])
    150 				file++;
    151 			continue;
    152 		}
    153 		while (file[0] == 9 || file[0] == ' ')
    154 			file++;
    155 		p = file;
    156 		while (file[0] && file[0] != 10 && file[0] != 13)
    157 			file++;
    158 		pl = (int)(file - p);
    159 		if (file[0]) {
    160 			if ((file[1] == 10 || file[1] == 13)
    161 			    && file[0] != file[1])
    162 				file++;
    163 			file++;
    164 		}
    165 		tok = NULL;
    166 		if (n[0] == '#')
    167 			goto f;
    168 		if (!(tok = get_token(&n)))
    169 			goto f;
    170 		nl = (int)strlen(cast_const_char tok);
    171 		for (op = opt; (options = *op); op++)
    172 			for (i = 0; options[i].p; i++)
    173 				if (options[i].cfg_name
    174 				    && (size_t)nl
    175 				           == strlen(cast_const_char options[i]
    176 				                         .cfg_name)
    177 				    && !casecmp(tok,
    178 				                cast_uchar options[i].cfg_name,
    179 				                nl)) {
    180 					unsigned char *o = memacpy(p, pl);
    181 					if (options[i].rd_cfg
    182 					    && (e = options[i].rd_cfg(
    183 						    &options[i], o))) {
    184 						if (e[0]) {
    185 							fprintf(
    186 							    stderr,
    187 							    "Error parsing "
    188 							    "config file %s, "
    189 							    "line %d: %s\n",
    190 							    name, line, e);
    191 							err = 1;
    192 						}
    193 					}
    194 					free(o);
    195 					goto f;
    196 				}
    197 		fprintf(stderr, "Unknown option in config file %s, line %d\n",
    198 		        name, line);
    199 		err = 1;
    200 f:
    201 		free(tok);
    202 	}
    203 	if (err) {
    204 		fprintf(stderr, "\007");
    205 		portable_sleep(1000);
    206 	}
    207 }
    208 
    209 static unsigned char *
    210 create_config_string(struct option *options)
    211 {
    212 	unsigned char *s = NULL;
    213 	size_t l;
    214 	int i;
    215 	l = add_to_str(&s, 0,
    216 	               cast_uchar
    217 	               "# This file is automatically generated by Links "
    218 	               "-- please do not edit.");
    219 	for (i = 0; options[i].p; i++) {
    220 		if (options[i].wr_cfg)
    221 			l = options[i].wr_cfg(&options[i], &s, l);
    222 	}
    223 	l = add_chr_to_str(&s, l, '\n');
    224 	return s;
    225 }
    226 
    227 unsigned char *
    228 read_config_file(unsigned char *name)
    229 {
    230 	int h, r;
    231 	int l = 0;
    232 	unsigned char *cfg_buffer, *s;
    233 	int rs;
    234 	h = c_open(name, O_RDONLY | O_NOCTTY);
    235 	if (h == -1)
    236 		return NULL;
    237 	s = NULL;
    238 	cfg_buffer = xmalloc(page_size);
    239 	while ((r = hard_read(h, cfg_buffer, page_size)) > 0) {
    240 		int i;
    241 		for (i = 0; i < r; i++)
    242 			if (!cfg_buffer[i])
    243 				cfg_buffer[i] = ' ';
    244 		l = add_bytes_to_str(&s, l, cfg_buffer, r);
    245 	}
    246 	free(cfg_buffer);
    247 	if (r == -1) {
    248 		free(s);
    249 		s = NULL;
    250 	}
    251 	EINTRLOOP(rs, close(h));
    252 	return s;
    253 }
    254 
    255 int
    256 write_to_config_file(unsigned char *name, unsigned char *c, int do_sync)
    257 {
    258 	int rr;
    259 	int h, w;
    260 	int count = 0;
    261 	size_t tmp_namel;
    262 	unsigned char *tmp_name;
    263 	int rs, err;
    264 try_new_count:
    265 	tmp_name = NULL;
    266 	tmp_namel = add_to_str(&tmp_name, 0, name);
    267 	for (w = tmp_namel - 1; w >= 0; w--) {
    268 		if (dir_sep(tmp_name[w]))
    269 			break;
    270 		if (tmp_name[w] == '.') {
    271 			if (w <= tmp_namel - 2) {
    272 				tmp_name[w + 2] = 0;
    273 				tmp_namel = w + 2;
    274 			}
    275 			break;
    276 		}
    277 	}
    278 	tmp_namel = add_num_to_str(&tmp_name, tmp_namel, count);
    279 	h = c_open3(tmp_name, O_WRONLY | O_NOCTTY | O_CREAT | O_TRUNC | O_EXCL,
    280 	            0600);
    281 	if (h == -1) {
    282 		err = errno;
    283 		if (err == EEXIST && count < INT_MAX) {
    284 			count++;
    285 			free(tmp_name);
    286 			goto try_new_count;
    287 		}
    288 		goto free_err;
    289 	}
    290 	rr = (int)strlen(cast_const_char c);
    291 	if (hard_write(h, c, rr) != rr) {
    292 		err = errno;
    293 		goto close_unlink_err;
    294 	}
    295 	if (do_sync) {
    296 		EINTRLOOP(rs, fsync(h));
    297 		if (rs) {
    298 			err = errno;
    299 			goto close_unlink_err;
    300 		}
    301 	}
    302 	EINTRLOOP(rs, close(h));
    303 	if (rs) {
    304 		err = errno;
    305 		goto unlink_err;
    306 	}
    307 	EINTRLOOP(rs, rename(cast_const_char tmp_name, cast_const_char name));
    308 	if (rs) {
    309 		err = errno;
    310 		goto unlink_err;
    311 	}
    312 	free(tmp_name);
    313 	if (do_sync) {
    314 		unsigned char *e, *le;
    315 		tmp_name = stracpy(name);
    316 		le = tmp_name;
    317 		for (e = tmp_name; *e; e++)
    318 			if (dir_sep(*e))
    319 				le = e;
    320 		while (le > tmp_name && dir_sep(le[-1]))
    321 			le--;
    322 		if (le == tmp_name && dir_sep(*le))
    323 			le++;
    324 		*le = 0;
    325 
    326 		h = c_open(*tmp_name ? tmp_name : cast_uchar ".",
    327 		           O_RDONLY | O_NOCTTY);
    328 		if (h != -1) {
    329 			EINTRLOOP(rs, fsync(h));
    330 			EINTRLOOP(rs, close(h));
    331 		}
    332 		free(tmp_name);
    333 	}
    334 
    335 	return 0;
    336 
    337 close_unlink_err:
    338 	EINTRLOOP(rs, close(h));
    339 unlink_err:
    340 	EINTRLOOP(rs, unlink(cast_const_char tmp_name));
    341 free_err:
    342 	free(tmp_name);
    343 	return get_error_from_errno(err);
    344 }
    345 
    346 static unsigned char *
    347 get_home(void)
    348 {
    349 	struct stat st;
    350 	int rs;
    351 	unsigned char *home;
    352 	unsigned char *home_links;
    353 	unsigned char *config_dir;
    354 
    355 	if (stat(".", &st))
    356 		die("stat: %s: %s\n", ".", strerror(errno));
    357 
    358 	first_use = 1;
    359 	home = stracpy(cast_uchar getenv("HOME"));
    360 	config_dir = cast_uchar getenv("XDG_CONFIG_HOME");
    361 
    362 	if (!home) {
    363 		int i;
    364 		home = (unsigned char *)argv0;
    365 		for (i = strlen(argv0) - 1; i >= 0; i--)
    366 			if (dir_sep(home[i])) {
    367 				home[i + 1] = 0;
    368 				goto br;
    369 			}
    370 		home[0] = 0;
    371 br:;
    372 	}
    373 	/* clears any '/' from the end of home */
    374 	while (home[0] && home[1]
    375 	       && dir_sep(home[strlen(cast_const_char home) - 1]))
    376 		home[strlen(cast_const_char home) - 1] = 0;
    377 	if (home[0])
    378 		add_to_strn(&home, cast_uchar "/");
    379 	if (config_dir == NULL || config_dir[0] == 0) {
    380 		home_links = stracpy(home);
    381 		add_to_strn(&home_links, cast_uchar ".config");
    382 	} else {
    383 		home_links = stracpy(config_dir);
    384 		while (home_links[0]
    385 		       && dir_sep(
    386 			   home_links[strlen(cast_const_char home_links) - 1]))
    387 			home_links[strlen(cast_const_char home_links) - 1] = 0;
    388 	}
    389 	add_to_strn(&home_links, cast_uchar "/links");
    390 	EINTRLOOP(rs, stat(cast_const_char home_links, &st));
    391 	if (rs) {
    392 		EINTRLOOP(rs, mkdir(cast_const_char home_links, 0700));
    393 		if (!rs)
    394 			goto home_creat;
    395 		if (config_dir)
    396 			goto failed;
    397 		goto first_failed;
    398 	}
    399 	if (S_ISDIR(st.st_mode))
    400 		goto home_ok;
    401 	/* This is a Cygwin hack! Cygwin reports stat for "links" if no
    402 	   "links" exists and only "links.exe" does. So try to create directory
    403 	   anyway. */
    404 	EINTRLOOP(rs, mkdir(cast_const_char home_links, 0700));
    405 	if (!rs)
    406 		goto home_creat;
    407 first_failed:
    408 	free(home_links);
    409 	home_links = stracpy(home);
    410 	add_to_strn(&home_links, cast_uchar "links");
    411 	EINTRLOOP(rs, stat(cast_const_char home_links, &st));
    412 	if (rs) {
    413 		EINTRLOOP(rs, mkdir(cast_const_char home_links, 0700));
    414 		if (!rs)
    415 			goto home_creat;
    416 		goto failed;
    417 	}
    418 	if (S_ISDIR(st.st_mode))
    419 		goto home_ok;
    420 	EINTRLOOP(rs, mkdir(cast_const_char home_links, 0700));
    421 	if (!rs)
    422 		goto home_creat;
    423 failed:
    424 	free(home_links);
    425 	free(home);
    426 	return NULL;
    427 
    428 home_ok:
    429 	first_use = 0;
    430 	if ((st.st_mode & 07777) != 0700) {
    431 home_creat:
    432 		EINTRLOOP(rs, chmod(cast_const_char home_links, 0700));
    433 	}
    434 	add_to_strn(&home_links, cast_uchar "/");
    435 	free(home);
    436 	return home_links;
    437 }
    438 
    439 void
    440 init_home(void)
    441 {
    442 	get_system_name();
    443 	links_home = get_home();
    444 	if (!links_home) {
    445 		fprintf(stderr,
    446 		        "Unable to find or create links config directory. "
    447 		        "Please check, that you have $HOME variable set "
    448 		        "correctly and that you have write permission to your "
    449 		        "home directory.\n\007");
    450 		portable_sleep(3000);
    451 		return;
    452 	}
    453 }
    454 
    455 /* prefix: directory
    456  * name: name of the configuration file (typ. links.cfg)
    457  */
    458 static int
    459 write_config_data(unsigned char *prefix, unsigned char *name, struct option *o,
    460                   struct terminal *term)
    461 {
    462 	int err;
    463 	unsigned char *c, *config_file;
    464 	if (!(c = create_config_string(o)))
    465 		return -1;
    466 	config_file = stracpy(prefix);
    467 	if (!config_file) {
    468 		free(c);
    469 		if (term)
    470 			msg_box(term, NULL, TEXT_(T_CONFIG_ERROR), AL_CENTER,
    471 			        TEXT_(T_UNABLE_TO_WRITE_TO_CONFIG_FILE),
    472 			        cast_uchar ": ",
    473 			        TEXT_(T_HOME_DIRECTORY_INACCESSIBLE),
    474 			        MSG_BOX_END, NULL, 1, TEXT_(T_CANCEL),
    475 			        msg_box_null, B_ENTER | B_ESC);
    476 		return -1;
    477 	}
    478 	add_to_strn(&config_file, name);
    479 	if ((err = write_to_config_file(config_file, c, 1))) {
    480 		if (term)
    481 			msg_box(term, NULL, TEXT_(T_CONFIG_ERROR), AL_CENTER,
    482 			        TEXT_(T_UNABLE_TO_WRITE_TO_CONFIG_FILE),
    483 			        cast_uchar ": ", get_err_msg(err), MSG_BOX_END,
    484 			        NULL, 1, TEXT_(T_CANCEL), msg_box_null,
    485 			        B_ENTER | B_ESC);
    486 		free(c);
    487 		free(config_file);
    488 		return -1;
    489 	}
    490 	free(c);
    491 	free(config_file);
    492 	return 0;
    493 }
    494 
    495 static size_t
    496 add_nm(struct option *o, unsigned char **s, size_t l)
    497 {
    498 	if (l)
    499 		l = add_chr_to_str(s, l, '\n');
    500 	l = add_to_str(s, l, cast_uchar o->cfg_name);
    501 	return add_chr_to_str(s, l, ' ');
    502 }
    503 
    504 static size_t
    505 add_quoted_to_str(unsigned char **s, size_t l, unsigned char *q)
    506 {
    507 	l = add_chr_to_str(s, l, '"');
    508 	while (*q) {
    509 		if (*q == '"' || *q == '\\')
    510 			l = add_chr_to_str(s, l, '\\');
    511 		l = add_chr_to_str(s, l, *q);
    512 		q++;
    513 	}
    514 	return add_chr_to_str(s, l, '"');
    515 }
    516 
    517 static unsigned char *
    518 num_rd(struct option *o, unsigned char *c)
    519 {
    520 	unsigned char *tok = get_token(&c);
    521 	unsigned char *end;
    522 	long l;
    523 	if (!tok)
    524 		return cast_uchar "Missing argument";
    525 	l = strtolx(tok, &end);
    526 	if (*end) {
    527 		free(tok);
    528 		return cast_uchar "Number expected";
    529 	}
    530 	if (l < o->min || l > o->max || l != (long)(int)l) {
    531 		free(tok);
    532 		return cast_uchar "Out of range";
    533 	}
    534 	*(int *)o->ptr = (int)l;
    535 	free(tok);
    536 	return NULL;
    537 }
    538 
    539 static size_t
    540 num_wr(struct option *o, unsigned char **s, size_t l)
    541 {
    542 	l = add_nm(o, s, l);
    543 	return add_knum_to_str(s, l, *(int *)o->ptr);
    544 }
    545 
    546 static unsigned char *
    547 dbl_rd(struct option *o, unsigned char *c)
    548 {
    549 	unsigned char *tok = get_token(&c);
    550 	char *end;
    551 	double d;
    552 
    553 	if (!tok)
    554 		return cast_uchar "Missing argument";
    555 
    556 	if (strlen(cast_const_char tok) >= 1000) {
    557 		free(tok);
    558 		return cast_uchar "Number is too long";
    559 	}
    560 
    561 	d = strtod(cast_const_char tok, &end);
    562 
    563 	if (*end) {
    564 		free(tok);
    565 		return cast_uchar "Number expected";
    566 	}
    567 	if (d < 0 || d > o->max || 100 * d < o->min || 100 * d > o->max) {
    568 		free(tok);
    569 		return cast_uchar "Out of range";
    570 	}
    571 	*(double *)o->ptr = d;
    572 	free(tok);
    573 	return NULL;
    574 }
    575 
    576 static size_t
    577 dbl_wr(struct option *o, unsigned char **s, size_t l)
    578 {
    579 	unsigned char number[80];
    580 	snprintf(cast_char number, sizeof number, "%.6f", *(double *)o->ptr);
    581 
    582 	l = add_nm(o, s, l);
    583 	return add_to_str(s, l, number);
    584 }
    585 
    586 static unsigned char *
    587 str_rd(struct option *o, unsigned char *c)
    588 {
    589 	unsigned char *tok = get_token(&c);
    590 	unsigned char *e = NULL;
    591 	if (!tok)
    592 		tok = stracpy(cast_uchar "");
    593 	if (strlen(cast_const_char tok) + 1 > (size_t)o->max)
    594 		e = cast_uchar "String too long";
    595 	else
    596 		strcpy(cast_char o->ptr, cast_const_char tok);
    597 	free(tok);
    598 	return e;
    599 }
    600 
    601 static size_t
    602 str_wr(struct option *o, unsigned char **s, size_t l)
    603 {
    604 	l = add_nm(o, s, l);
    605 	if (strlen(cast_const_char o->ptr) + 1 > (size_t)o->max) {
    606 		unsigned char *s1 = NULL;
    607 		add_bytes_to_str(&s1, 0, o->ptr, o->max - 1);
    608 		l = add_quoted_to_str(s, l, s1);
    609 		free(s1);
    610 	} else
    611 		l = add_quoted_to_str(s, l, o->ptr);
    612 
    613 	return l;
    614 }
    615 
    616 static unsigned char *
    617 cp_rd(struct option *o, unsigned char *c)
    618 {
    619 	unsigned char *tok = get_token(&c);
    620 	if (!tok)
    621 		return cast_uchar "Missing argument";
    622 	*(int *)o->ptr = 0;
    623 	free(tok);
    624 	return NULL;
    625 }
    626 
    627 static size_t
    628 cp_wr(struct option *o, unsigned char **s, size_t l)
    629 {
    630 	unsigned char *n = get_cp_mime_name(0);
    631 	l = add_nm(o, s, l);
    632 	return add_to_str(s, l, n);
    633 }
    634 
    635 static int
    636 getnum(unsigned char *s, int *n, int r1, int r2)
    637 {
    638 	char *e;
    639 	long l = strtol(cast_const_char s, &e, 10);
    640 	if (*e || !*s)
    641 		return -1;
    642 	if (l < r1 || l >= r2)
    643 		return -1;
    644 	*n = (int)l;
    645 	return 0;
    646 }
    647 
    648 static unsigned char *
    649 type_rd(struct option *o, unsigned char *c)
    650 {
    651 	unsigned char *err =
    652 	    cast_uchar "Error reading association specification";
    653 	struct assoc neww;
    654 	unsigned char *w;
    655 	int n = 0; /* against warning */
    656 	memset(&neww, 0, sizeof(struct assoc));
    657 	if (!(neww.label = get_token(&c)))
    658 		goto err;
    659 	if (!(neww.ct = get_token(&c)))
    660 		goto err;
    661 	if (!(neww.prog = get_token(&c)))
    662 		goto err;
    663 	if (!(w = get_token(&c)))
    664 		goto err;
    665 	if (getnum(w, &n, 0, 128))
    666 		goto err_f;
    667 	free(w);
    668 	neww.cons = !!(n & 1);
    669 	neww.xwin = !!(n & 2);
    670 	neww.ask = !!(n & 4);
    671 	if ((n & 8) || (n & 16))
    672 		neww.block = !!(n & 16);
    673 	else
    674 		neww.block = !neww.xwin || neww.cons;
    675 	neww.accept_http = !!(n & 32);
    676 	neww.accept_ftp = !!(n & 64);
    677 	if (!(w = get_token(&c)))
    678 		goto err;
    679 	if (getnum(w, &neww.system, 0, 256))
    680 		goto err_f;
    681 	free(w);
    682 	update_assoc(&neww);
    683 	err = NULL;
    684 err:
    685 	free(neww.label);
    686 	free(neww.ct);
    687 	free(neww.prog);
    688 	return err;
    689 err_f:
    690 	free(w);
    691 	goto err;
    692 }
    693 
    694 static size_t
    695 type_wr(struct option *o, unsigned char **s, size_t l)
    696 {
    697 	struct list *a = NULL;
    698 	struct list_head *la;
    699 	foreachback (struct list, a, la, assoc.list_entry) {
    700 		struct assoc *as = get_struct(a, struct assoc, head);
    701 		l = add_nm(o, s, l);
    702 		l = add_quoted_to_str(s, l, as->label);
    703 		l = add_chr_to_str(s, l, ' ');
    704 		l = add_quoted_to_str(s, l, as->ct);
    705 		l = add_chr_to_str(s, l, ' ');
    706 		l = add_quoted_to_str(s, l, as->prog);
    707 		l = add_chr_to_str(s, l, ' ');
    708 		l = add_num_to_str(
    709 		    s, l,
    710 		    (!!as->cons) + (!!as->xwin) * 2 + (!!as->ask) * 4
    711 			+ (!as->block) * 8 + (!!as->block) * 16
    712 			+ (!!as->accept_http) * 32 + (!!as->accept_ftp) * 64);
    713 		l = add_chr_to_str(s, l, ' ');
    714 		l = add_num_to_str(s, l, as->system);
    715 	}
    716 	return l;
    717 }
    718 
    719 static unsigned char *
    720 ext_rd(struct option *o, unsigned char *c)
    721 {
    722 	unsigned char *err = cast_uchar "Error reading extension specification";
    723 	struct extension neww;
    724 	memset(&neww, 0, sizeof(struct extension));
    725 	if (!(neww.ext = get_token(&c)))
    726 		goto err;
    727 	if (!(neww.ct = get_token(&c)))
    728 		goto err;
    729 	update_ext(&neww);
    730 	err = NULL;
    731 err:
    732 	free(neww.ext);
    733 	free(neww.ct);
    734 	return err;
    735 }
    736 
    737 static size_t
    738 ext_wr(struct option *o, unsigned char **s, size_t l)
    739 {
    740 	struct list *a = NULL;
    741 	struct list_head *la;
    742 	foreachback (struct list, a, la, extensions.list_entry) {
    743 		struct extension *e = get_struct(a, struct extension, head);
    744 		l = add_nm(o, s, l);
    745 		l = add_quoted_to_str(s, l, e->ext);
    746 		l = add_chr_to_str(s, l, ' ');
    747 		l = add_quoted_to_str(s, l, e->ct);
    748 	}
    749 	return l;
    750 }
    751 
    752 static unsigned char *
    753 term_rd(struct option *o, unsigned char *c)
    754 {
    755 	struct term_spec *ts;
    756 	unsigned char *w;
    757 	if (!(w = get_token(&c)))
    758 		goto err;
    759 	ts = new_term_spec(w);
    760 	free(w);
    761 	if (!(w = get_token(&c)))
    762 		goto err;
    763 	if (strlen(cast_const_char w) != 1 || w[0] < '0' || w[0] > '4')
    764 		goto err_f;
    765 	ts->mode = w[0] - '0';
    766 	free(w);
    767 	if (!(w = get_token(&c)))
    768 		goto err;
    769 	if (strlen(cast_const_char w) != 1 || w[0] < '0' || w[0] > '3')
    770 		goto err_f;
    771 	ts->m11_hack = (w[0] - '0') & 1;
    772 	free(w);
    773 	if (!(w = get_token(&c)))
    774 		goto err;
    775 	if (strlen(cast_const_char w) != 1 || w[0] < '0' || w[0] > '7')
    776 		goto err_f;
    777 	ts->col = (w[0] - '0') & 1;
    778 	ts->restrict_852 = !!((w[0] - '0') & 2);
    779 	ts->block_cursor = !!((w[0] - '0') & 4);
    780 	free(w);
    781 	if (!(w = get_token(&c)))
    782 		goto err;
    783 	ts->character_set = 0;
    784 	free(w);
    785 	ts->left_margin = 0;
    786 	ts->right_margin = 0;
    787 	ts->top_margin = 0;
    788 	ts->bottom_margin = 0;
    789 	return NULL;
    790 err_f:
    791 	free(w);
    792 err:
    793 	return cast_uchar "Error reading terminal specification";
    794 }
    795 
    796 static unsigned char *
    797 term2_rd(struct option *o, unsigned char *c)
    798 {
    799 	struct term_spec *ts;
    800 	unsigned char *w;
    801 	if (!(w = get_token(&c)))
    802 		goto err;
    803 	ts = new_term_spec(w);
    804 	free(w);
    805 	if (!(w = get_token(&c)))
    806 		goto err;
    807 	if (strlen(cast_const_char w) != 1 || w[0] < '0' || w[0] > '3')
    808 		goto err_f;
    809 	ts->mode = w[0] - '0';
    810 	free(w);
    811 	if (!(w = get_token(&c)))
    812 		goto err;
    813 	if (strlen(cast_const_char w) != 1 || w[0] < '0' || w[0] > '1')
    814 		goto err_f;
    815 	ts->m11_hack = w[0] - '0';
    816 	free(w);
    817 	if (!(w = get_token(&c)))
    818 		goto err;
    819 	if (strlen(cast_const_char w) != 1 || w[0] < '0' || w[0] > '1')
    820 		goto err_f;
    821 	ts->restrict_852 = w[0] - '0';
    822 	free(w);
    823 	if (!(w = get_token(&c)))
    824 		goto err;
    825 	if (strlen(cast_const_char w) != 1 || w[0] < '0' || w[0] > '1')
    826 		goto err_f;
    827 	ts->col = w[0] - '0';
    828 	free(w);
    829 	if (!(w = get_token(&c)))
    830 		goto err;
    831 	ts->character_set = 0;
    832 	free(w);
    833 	return NULL;
    834 err_f:
    835 	free(w);
    836 err:
    837 	return cast_uchar "Error reading terminal specification";
    838 }
    839 
    840 static size_t
    841 term_wr(struct option *o, unsigned char **s, size_t l)
    842 {
    843 	struct term_spec *ts = NULL;
    844 	struct list_head *lts;
    845 	foreachback (struct term_spec, ts, lts, term_specs) {
    846 		l = add_nm(o, s, l);
    847 		l = add_quoted_to_str(s, l, ts->term);
    848 		l = add_chr_to_str(s, l, ' ');
    849 		l = add_num_to_str(s, l, ts->mode);
    850 		l = add_chr_to_str(s, l, ' ');
    851 		l = add_num_to_str(s, l, !!ts->m11_hack);
    852 		l = add_chr_to_str(s, l, ' ');
    853 		l = add_num_to_str(s, l,
    854 		                   !!ts->col + !!ts->restrict_852 * 2
    855 		                       + !!ts->block_cursor * 4);
    856 		l = add_chr_to_str(s, l, ' ');
    857 		l = add_to_str(s, l, get_cp_mime_name(ts->character_set));
    858 		if (ts->left_margin || ts->right_margin || ts->top_margin
    859 		    || ts->bottom_margin) {
    860 			l = add_chr_to_str(s, l, ' ');
    861 			l = add_num_to_str(s, l, ts->left_margin);
    862 			l = add_chr_to_str(s, l, ' ');
    863 			l = add_num_to_str(s, l, ts->right_margin);
    864 			l = add_chr_to_str(s, l, ' ');
    865 			l = add_num_to_str(s, l, ts->top_margin);
    866 			l = add_chr_to_str(s, l, ' ');
    867 			l = add_num_to_str(s, l, ts->bottom_margin);
    868 		}
    869 	}
    870 	return l;
    871 }
    872 
    873 static struct list_head driver_params = { &driver_params, &driver_params };
    874 
    875 struct driver_param *
    876 get_driver_param(unsigned char *n)
    877 {
    878 	struct driver_param *dp = NULL;
    879 	size_t sl;
    880 	struct list_head *ldp;
    881 	foreach (struct driver_param, dp, ldp, driver_params)
    882 		if (!casestrcmp(dp->name, n))
    883 			return dp;
    884 	sl = strlen(cast_const_char n);
    885 	if (sl > INT_MAX - sizeof(struct driver_param))
    886 		overalloc();
    887 	dp = mem_calloc(sizeof(struct driver_param) + sl);
    888 	strcpy(cast_char dp->name, cast_const_char n);
    889 	dp->kbd_codepage = -1;
    890 	dp->palette_mode = 0;
    891 	dp->nosave = 1;
    892 	add_to_list(driver_params, dp);
    893 	return dp;
    894 }
    895 
    896 static unsigned char *
    897 dp_rd(struct option *o, unsigned char *c)
    898 {
    899 	int cc;
    900 	unsigned char *n, *param, *cp;
    901 	struct driver_param *dp;
    902 	if (!(n = get_token(&c)))
    903 		goto err;
    904 	dp = get_driver_param(n);
    905 	free(n);
    906 	if (!(param = get_token(&c)))
    907 		goto err;
    908 	free(dp->param);
    909 	dp->param = param;
    910 	if (!(param = get_token(&c)))
    911 		goto err;
    912 	safe_strncpy(dp->shell_term, param, MAX_STR_LEN);
    913 	free(param);
    914 	if (!(cp = get_token(&c)))
    915 		goto err;
    916 	if (!casestrcmp(cp, cast_uchar "default"))
    917 		cc = -1;
    918 	else
    919 		cc = 0;
    920 	free(cp);
    921 	dp->kbd_codepage = cc;
    922 	dp->nosave = 0;
    923 	return NULL;
    924 err:
    925 	return cast_uchar "Error reading driver mode specification";
    926 }
    927 
    928 static size_t
    929 dp_wr(struct option *o, unsigned char **s, size_t l)
    930 {
    931 	struct driver_param *dp = NULL;
    932 	struct list_head *ldp;
    933 	foreachback (struct driver_param, dp, ldp, driver_params) {
    934 		if ((!dp->param || !*dp->param) && !*dp->shell_term
    935 		    && dp->kbd_codepage < 0 && !dp->palette_mode)
    936 			continue;
    937 		if (dp->nosave)
    938 			continue;
    939 		l = add_nm(o, s, l);
    940 		l = add_quoted_to_str(s, l, dp->name);
    941 		l = add_chr_to_str(s, l, ' ');
    942 		l = add_quoted_to_str(
    943 		    s, l, dp->param ? dp->param : (unsigned char *)"");
    944 		l = add_chr_to_str(s, l, ' ');
    945 		l = add_quoted_to_str(s, l, dp->shell_term);
    946 		l = add_chr_to_str(s, l, ' ');
    947 		if (dp->kbd_codepage == -1)
    948 			l = add_to_str(s, l, cast_uchar "default");
    949 		else
    950 			l = add_to_str(s, l,
    951 			               get_cp_mime_name(dp->kbd_codepage));
    952 		l = add_chr_to_str(s, l, ' ');
    953 		l = add_num_to_str(s, l, dp->palette_mode);
    954 		/* pokud se sem neco prida, opravit podminku na zacatku cyklu */
    955 	}
    956 	return l;
    957 }
    958 
    959 static unsigned char *
    960 ip_rd(struct option *o, unsigned char *c)
    961 {
    962 	unsigned char *e;
    963 	e = str_rd(o, c);
    964 	if (e)
    965 		return e;
    966 	if (*(unsigned char *)o->ptr && numeric_ip_address(o->ptr, NULL) == -1)
    967 		return cast_uchar "Invalid IP address";
    968 	return NULL;
    969 }
    970 
    971 static unsigned char *
    972 ipv6_rd(struct option *o, unsigned char *c)
    973 {
    974 	unsigned char *e;
    975 	e = str_rd(o, c);
    976 	if (e)
    977 		return e;
    978 	if (*(unsigned char *)o->ptr
    979 	    && numeric_ipv6_address(o->ptr, NULL, NULL) == -1)
    980 		return cast_uchar "Invalid IPv6 address";
    981 	return NULL;
    982 }
    983 
    984 static unsigned char *
    985 gen_cmd(struct option *o, unsigned char ***argv, int *argc)
    986 {
    987 	unsigned char *e;
    988 	unsigned char *r;
    989 	if (!*argc)
    990 		return cast_uchar "Parameter expected";
    991 	e = NULL;
    992 	add_quoted_to_str(&e, 0, **argv);
    993 	r = o->rd_cfg ? o->rd_cfg(o, e) : 0;
    994 	free(e);
    995 	if (r)
    996 		return r;
    997 	(*argv)++;
    998 	(*argc)--;
    999 	return NULL;
   1000 }
   1001 
   1002 static unsigned char *
   1003 x_proxy_cmd(struct option *o, unsigned char ***argv, int *argc,
   1004             int (*save)(int, unsigned char *, unsigned char *),
   1005             unsigned char *err)
   1006 {
   1007 	unsigned char **pass_argv;
   1008 	unsigned char *result, *ret;
   1009 	if (!*argc)
   1010 		return cast_uchar "Parameter expected";
   1011 	result = xmalloc(MAX_STR_LEN);
   1012 	if (save(0, result, **argv)) {
   1013 		free(result);
   1014 		return err;
   1015 	}
   1016 	pass_argv = &result;
   1017 	ret = gen_cmd(o, &pass_argv, argc);
   1018 	free(result);
   1019 	if (ret)
   1020 		return ret;
   1021 	(*argv)++;
   1022 	return NULL;
   1023 }
   1024 
   1025 static unsigned char *
   1026 proxy_cmd(struct option *o, unsigned char ***argv, int *argc)
   1027 {
   1028 	return x_proxy_cmd(o, argv, argc, save_proxy,
   1029 	                   cast_uchar "Invalid proxy");
   1030 }
   1031 
   1032 static unsigned char *
   1033 noproxy_cmd(struct option *o, unsigned char ***argv, int *argc)
   1034 {
   1035 	return x_proxy_cmd(o, argv, argc, save_noproxy_list,
   1036 	                   cast_uchar "Invalid list of domains");
   1037 }
   1038 
   1039 static unsigned char *
   1040 version_cmd(struct option *o, unsigned char ***argv, int *argc)
   1041 {
   1042 	printf("Links " VERSION "\n");
   1043 	fflush(stdout);
   1044 	exit(RET_OK);
   1045 }
   1046 
   1047 static unsigned char *
   1048 set_cmd(struct option *o, unsigned char ***argv, int *argc)
   1049 {
   1050 	*(int *)o->ptr = 1;
   1051 	return NULL;
   1052 }
   1053 
   1054 static unsigned char *
   1055 setstr_cmd(struct option *o, unsigned char ***argv, int *argc)
   1056 {
   1057 	if (!*argc)
   1058 		return cast_uchar "Parameter expected";
   1059 	safe_strncpy(o->ptr, **argv, o->max);
   1060 	(*argv)++;
   1061 	(*argc)--;
   1062 	return NULL;
   1063 }
   1064 
   1065 static unsigned char *
   1066 dump_cmd(struct option *o, unsigned char ***argv, int *argc)
   1067 {
   1068 	if (dmp != o->min && dmp)
   1069 		return cast_uchar "Can't use both -dump and -source";
   1070 	dmp = o->min;
   1071 	no_connect = 1;
   1072 	return NULL;
   1073 }
   1074 
   1075 static unsigned char *
   1076 printhelp_cmd(struct option *o, unsigned char ***argv, int *argc)
   1077 {
   1078 	usage();
   1079 	/* FIXME: never reached */
   1080 	return NULL;
   1081 }
   1082 
   1083 void
   1084 end_config(void)
   1085 {
   1086 	struct driver_param *dp = NULL;
   1087 	struct list_head *ldp;
   1088 	foreach (struct driver_param, dp, ldp, driver_params) {
   1089 		free(dp->param);
   1090 	}
   1091 	free_list(struct driver_param, driver_params);
   1092 	free(links_home);
   1093 }
   1094 
   1095 int anonymous = 0;
   1096 
   1097 unsigned char default_target[MAX_STR_LEN] = "";
   1098 
   1099 unsigned char *links_home = NULL;
   1100 int first_use = 0;
   1101 
   1102 int disable_libevent = 0;
   1103 int no_connect = 0;
   1104 int base_session = 0;
   1105 int dmp = 0;
   1106 int screen_width = 80;
   1107 int dump_codepage = -1;
   1108 int force_html = 0;
   1109 
   1110 int max_connections = 10;
   1111 int max_connections_to_host = 8;
   1112 int max_tries = 3;
   1113 int receive_timeout = 120;
   1114 int unrestartable_receive_timeout = 600;
   1115 int timeout_multiple_addresses = 3;
   1116 unsigned char bind_ip_address[16] = "";
   1117 unsigned char bind_ipv6_address[INET6_ADDRSTRLEN] = "";
   1118 int download_utime = 0;
   1119 
   1120 int max_format_cache_entries = 5;
   1121 int memory_cache_size = 4194304;
   1122 int image_cache_size = 1048576;
   1123 int font_cache_size = 2097152;
   1124 int aggressive_cache = 1;
   1125 
   1126 struct ipv6_options ipv6_options = { ADDR_PREFERENCE_DEFAULT };
   1127 struct proxies proxies = { "", "", "", "", "", 0 };
   1128 struct ssl_options ssl_options = { SSL_WARN_ON_INVALID_CERTIFICATE, 0, "", "",
   1129 	                           "" };
   1130 struct http_options http_options = {
   1131 	0, 1, 0, 0, 0, {0, "", ""}
   1132 };
   1133 
   1134 unsigned char download_dir[MAX_STR_LEN] = "";
   1135 
   1136 double display_red_gamma = 2.2;   /* Red gamma exponent of the display */
   1137 double display_green_gamma = 2.2; /* Green gamma exponent of the display */
   1138 double display_blue_gamma = 2.2;  /* Blue gamma exponent of the display */
   1139 double user_gamma = 1.0; /* 1.0 for 64 lx. This is the number user directly
   1140                             changes in the menu */
   1141 double bfu_aspect = 1;   /* 0.1 to 10.0, 1.0 default. >1 makes circle wider */
   1142 int dither_letters = 1;
   1143 int dither_images = 1;
   1144 int gamma_bits = 2; /*0 --- 8, 1 --- 16, 2 --- auto */
   1145 int overwrite_instead_of_scroll = 1;
   1146 
   1147 unsigned char bookmarks_file[MAX_STR_LEN] = "";
   1148 
   1149 int save_history = 1;
   1150 
   1151 struct document_setup dds = {
   1152 	0,        /* assumed codepage */
   1153 	0,        /* ignore codepage from server */
   1154 	1,        /* tables */
   1155 	1,        /* frames */
   1156 	0,        /* break_long_lines */
   1157 	0,        /* images */
   1158 	0,        /* image_names */
   1159 	3,        /* margin */
   1160 	0,        /* num_links */
   1161 	0,        /* table_order */
   1162 	0,        /* auto_refresh */
   1163 	20,       /* font_size */
   1164 	1,        /* display images */
   1165 	100,      /* image scale */
   1166 	0,        /* porn enable */
   1167 	0,        /* target in new window */
   1168 	7,        /* t text color */
   1169 	15,       /* t link color */
   1170 	0,        /* t background color */
   1171 	0,        /* t ignore document color */
   1172 	0x000000, /* g text color */
   1173 	0x0000ff, /* g link color */
   1174 	0xc0c0c0, /* g background color */
   1175 	0,        /* g ignore document color */
   1176 };
   1177 
   1178 static struct option links_options[] = {
   1179 	{1,  printhelp_cmd, NULL,     NULL,    0,        0,                NULL,                                NULL,                  "?"                                     },
   1180 	{ 1, printhelp_cmd, NULL,     NULL,    0,        0,                NULL,                                NULL,                  "h"                                     },
   1181 	{ 1, printhelp_cmd, NULL,     NULL,    0,        0,                NULL,                                NULL,                  "help"                                  },
   1182 	{ 1, printhelp_cmd, NULL,     NULL,    0,        0,                NULL,                                NULL,                  "-help"                                 },
   1183 	{ 1, version_cmd,   NULL,     NULL,    0,        0,                NULL,                                NULL,                  "version"                               },
   1184 	{ 1, set_cmd,       NULL,     NULL,    0,        0,                &no_connect,                         NULL,                  "no-connect"                            },
   1185 	{ 1, set_cmd,       NULL,     NULL,    0,        0,                &anonymous,                          NULL,                  "anonymous"                             },
   1186 	{ 1, setstr_cmd,    NULL,     NULL,    0,        MAX_STR_LEN,      default_target,                      NULL,
   1187          "target"																			      },
   1188 	{ 1, gen_cmd,       num_rd,   NULL,    0,        INT_MAX,          &base_session,                       NULL,
   1189          "base-session"																			},
   1190 	{ 1, set_cmd,       NULL,     NULL,    0,        0,                &force_html,                         NULL,                  "force-html"                            },
   1191 	{ 1, dump_cmd,      NULL,     NULL,    D_SOURCE, 0,                NULL,                                NULL,                  "source"                                },
   1192 	{ 1, dump_cmd,      NULL,     NULL,    D_DUMP,   0,                NULL,                                NULL,                  "dump"                                  },
   1193 	{ 1, gen_cmd,       num_rd,   NULL,    10,       512,              &screen_width,                       "dump_width",
   1194          "width"																			       },
   1195 	{ 1, gen_cmd,       cp_rd,    NULL,    1,        0,                &dump_codepage,                      "dump_codepage",
   1196          "codepage"																			    },
   1197 	{ 1, gen_cmd,       str_rd,   str_wr,  0,        MAX_STR_LEN,      download_dir,
   1198          "download_dir",													       "download-dir"                          },
   1199 	{ 1, gen_cmd,       num_rd,   num_wr,  1,        99,               &max_connections,
   1200          "max_connections",													    "max-connections"                       },
   1201 	{ 1, gen_cmd,       num_rd,   num_wr,  1,        99,               &max_connections_to_host,
   1202          "max_connections_to_host",												    "max-connections-to-host"               },
   1203 	{ 1, gen_cmd,       num_rd,   num_wr,  0,        16,               &max_tries,                          "retries",             "retries"                               },
   1204 	{ 1, gen_cmd,       num_rd,   num_wr,  1,        9999,             &receive_timeout,
   1205          "receive_timeout",													    "receive-timeout"                       },
   1206 	{ 1, gen_cmd,       num_rd,   num_wr,  1,        9999,             &unrestartable_receive_timeout,
   1207          "unrestartable_receive_timeout",											      "unrestartable-receive-timeout"         },
   1208 	{ 1, gen_cmd,       num_rd,   num_wr,  1,        999,              &timeout_multiple_addresses,
   1209          "timeout_when_trying_multiple_addresses",	                                                                             "timeout-when-trying-multiple-addresses"},
   1210 	{ 1, gen_cmd,       ip_rd,    str_wr,  0,        16,               bind_ip_address,                     "bind_address",
   1211          "bind-address"																			},
   1212 	{ 1, gen_cmd,       ipv6_rd,  str_wr,  0,        INET6_ADDRSTRLEN, bind_ipv6_address,
   1213          "bind_address_ipv6",													  "bind-address-ipv6"                     },
   1214 	{ 1, set_cmd,       NULL,     NULL,    0,        0,                &disable_libevent,                   NULL,
   1215          "no-libevent"																			 },
   1216 	{ 1, gen_cmd,       num_rd,   num_wr,  0,        1,                &download_utime,                     "download_utime",
   1217          "download-utime"																		      },
   1218 	{ 1, gen_cmd,       num_rd,   num_wr,  0,        999,              &max_format_cache_entries,
   1219          "format_cache_size",													  "format-cache-size"                     },
   1220 	{ 1, gen_cmd,       num_rd,   num_wr,  0,        INT_MAX,          &memory_cache_size,
   1221          "memory_cache_size",													  "memory-cache-size"                     },
   1222 	{ 1, gen_cmd,       num_rd,   num_wr,  0,        INT_MAX,          &image_cache_size,
   1223          "image_cache_size",													   "image-cache-size"                      },
   1224 	{ 1, gen_cmd,       num_rd,   num_wr,  0,        INT_MAX,          &font_cache_size,
   1225          "font_cache_size",													    "font-cache-size"                       },
   1226 	{ 1, gen_cmd,       num_rd,   num_wr,  0,        1,                &aggressive_cache,
   1227          "http_bugs.aggressive_cache",												 "aggressive-cache"                      },
   1228 	{ 1, gen_cmd,       num_rd,   num_wr,  0,        4,                &ipv6_options.addr_preference,
   1229          "ipv6.address_preference",												    "address-preference"                    },
   1230 	{ 1, proxy_cmd,     str_rd,   str_wr,  0,        MAX_STR_LEN,      proxies.http_proxy,
   1231          "http_proxy",														 "http-proxy"                            },
   1232 	{ 1, proxy_cmd,     str_rd,   str_wr,  0,        MAX_STR_LEN,      proxies.https_proxy,
   1233          "https_proxy",														"https-proxy"                           },
   1234 	{ 1, proxy_cmd,     str_rd,   str_wr,  0,        MAX_STR_LEN,      proxies.socks_proxy,
   1235          "socks_proxy",														"socks-proxy"                           },
   1236 	{ 1, gen_cmd,       str_rd,   NULL,    0,        MAX_STR_LEN,      proxies.dns_append,
   1237          "-append_text_to_dns_lookups",	                                                                                        NULL				    }, /* old version incorrectly saved it with '-' */
   1238 	{ 1, noproxy_cmd,   str_rd,   str_wr,  0,        MAX_STR_LEN,      proxies.no_proxy,
   1239          "no_proxy_domains",													   "no-proxy-domains"                      },
   1240 	{ 1, gen_cmd,       str_rd,   str_wr,  0,        MAX_STR_LEN,      proxies.dns_append,
   1241          "append_text_to_dns_lookups",												 "append-text-to-dns-lookups"            },
   1242 	{ 1, gen_cmd,       num_rd,   num_wr,  0,        1,                &proxies.only_proxies,
   1243          "only_proxies",													       "only-proxies"                          },
   1244 	{ 1, gen_cmd,       num_rd,   num_wr,  0,        2,                &ssl_options.certificates,
   1245          "ssl.certificates",													   "ssl.certificates"                      },
   1246 	{ 1, gen_cmd,       num_rd,   num_wr,  0,        1,                &ssl_options.built_in_certificates,
   1247          "ssl.builtin_certificates",												   "ssl.builtin-certificates"              },
   1248 	{ 1, gen_cmd,       str_rd,   str_wr,  0,        MAX_STR_LEN,
   1249          &ssl_options.client_cert_key,									  "ssl.client_cert_key",
   1250          "ssl.client-cert-key"																		 },
   1251 	{ 1, gen_cmd,       str_rd,   str_wr,  0,        MAX_STR_LEN,
   1252          &ssl_options.client_cert_crt,									  "ssl.client_cert_crt",
   1253          "ssl.client-cert-crt"																		 },
   1254 	{ 1, gen_cmd,       str_rd,   NULL,    0,        MAX_STR_LEN,
   1255          &ssl_options.client_cert_password,								     NULL,                  "ssl.client-cert-password"              },
   1256 	{ 1, gen_cmd,       str_rd,   NULL,    0,        MAX_STR_LEN,
   1257          &ssl_options.client_cert_key,									  "client_cert_key",
   1258          "http.client_cert_key"																		}, /* backward compatibility with Debian
   1259   patches */
   1260 	{ 1, gen_cmd,       str_rd,   NULL,    0,        MAX_STR_LEN,
   1261          &ssl_options.client_cert_crt,									  "client_cert_crt",
   1262          "http.client_cert_crt"																		}, /* backward compatibility with Debian
   1263   patches */
   1264 	{ 1, gen_cmd,       num_rd,   num_wr,  0,        1,                &http_options.http10,
   1265          "http_bugs.http10",													   "http-bugs.http10"                      },
   1266 	{ 1, gen_cmd,       num_rd,   num_wr,  0,        1,                &http_options.allow_blacklist,
   1267          "http_bugs.allow_blacklist",												  "http-bugs.allow-blacklist"             },
   1268 	{ 1, gen_cmd,       num_rd,   num_wr,  0,        1,                &http_options.no_accept_charset,
   1269          "http_bugs.no_accept_charset",												"http-bugs.bug-no-accept-charset"       },
   1270 	{ 1, gen_cmd,       num_rd,   num_wr,  0,        1,                &http_options.no_compression,
   1271          "http_bugs.no_compression",												   "http-bugs.no-compression"              },
   1272 	{ 1, gen_cmd,       num_rd,   num_wr,  0,        1,                &http_options.retry_internal_errors,
   1273          "http_bugs.retry_internal_errors",	                                                                                    "http-bugs.retry-internal-errors"       },
   1274 	{ 1, gen_cmd,       num_rd,   num_wr,  0,        1,                &http_options.header.fake_firefox,
   1275          "fake_firefox",													       "http.fake-firefox"                     },
   1276 	{ 1, gen_cmd,       str_rd,   str_wr,  0,        MAX_STR_LEN,
   1277          &http_options.header.fake_useragent,								   "fake_useragent",
   1278          "http.fake-user-agent"																		},
   1279 	{ 1, gen_cmd,       str_rd,   str_wr,  0,        MAX_STR_LEN,
   1280          &http_options.header.extra_header,								     "http.extra_header",
   1281          "http.extra-header"																		   },
   1282 	{ 1, gen_cmd,       str_rd,   str_wr,  0,        MAX_STR_LEN,      bookmarks_file,
   1283          "bookmarks_file",													     "bookmarks-file"                        },
   1284 	{ 1, gen_cmd,       num_rd,   num_wr,  0,        1,                &save_history,                       "save_url_history",
   1285          "save-url-history"																		    },
   1286 	{ 1, gen_cmd,       dbl_rd,   dbl_wr,  1,        10000,            &display_red_gamma,
   1287          "display_red_gamma",													  "display-red-gamma"                     },
   1288 	{ 1, gen_cmd,       dbl_rd,   dbl_wr,  1,        10000,            &display_green_gamma,
   1289          "display_green_gamma",													"display-green-gamma"                   },
   1290 	{ 1, gen_cmd,       dbl_rd,   dbl_wr,  1,        10000,            &display_blue_gamma,
   1291          "display_blue_gamma",													 "display-blue-gamma"                    },
   1292 	{ 1, gen_cmd,       dbl_rd,   dbl_wr,  1,        10000,            &user_gamma,                         "user_gamma",
   1293          "user-gamma"																			  },
   1294 	{ 1, gen_cmd,       dbl_rd,   dbl_wr,  25,       400,              &bfu_aspect,                         "bfu_aspect",
   1295          "bfu-aspect"																			  },
   1296 	{ 1, gen_cmd,       NULL,     NULL,    0,        1,                NULL,                                "aspect_on",           NULL                                    },
   1297 	{ 1, gen_cmd,       num_rd,   num_wr,  0,        1,                &dither_letters,                     "dither_letters",
   1298          "dither-letters"																		      },
   1299 	{ 1, gen_cmd,       num_rd,   num_wr,  0,        1,                &dither_images,                      "dither_images",
   1300          "dither-images"																		       },
   1301 	{ 1, gen_cmd,       num_rd,   num_wr,  0,        2,                &gamma_bits,                         "gamma_correction",
   1302          "gamma-correction"																		    },
   1303 	{ 1, gen_cmd,       num_rd,   num_wr,  0,        1,                &overwrite_instead_of_scroll,
   1304          "overwrite_instead_of_scroll",												"overwrite-instead-of-scroll"           },
   1305 	{ 1, gen_cmd,       cp_rd,    NULL,    0,        0,                &dds.assume_cp,                      "assume_codepage",
   1306          NULL																				  },
   1307 	{ 1, NULL,          term_rd,  term_wr, 0,        0,                NULL,                                "terminal",            NULL                                    },
   1308 	{ 1, NULL,          term2_rd, NULL,    0,        0,                NULL,                                "terminal2",           NULL                                    },
   1309 	{ 1, NULL,          type_rd,  type_wr, 0,        0,                NULL,                                "association",         NULL                                    },
   1310 	{ 1, NULL,          ext_rd,   ext_wr,  0,        0,                NULL,                                "extension",           NULL                                    },
   1311 	{ 1, NULL,          dp_rd,    dp_wr,   0,        0,                NULL,                                "video_driver",        NULL                                    },
   1312 	{ 0, NULL,          NULL,     NULL,    0,        0,                NULL,                                NULL,                  NULL                                    },
   1313 };
   1314 
   1315 static struct option html_options[] = {
   1316 	{1,  gen_cmd, cp_rd,  cp_wr,  0, 0,             &dds.assume_cp,
   1317          "html_assume_codepage",								     "html-assume-codepage"        },
   1318 	{ 1, gen_cmd, num_rd, num_wr, 0, 1,             &dds.hard_assume,
   1319          "html_hard_assume",									 "html-hard-assume"            },
   1320 	{ 1, gen_cmd, num_rd, num_wr, 0, 1,             &dds.tables,                  "html_tables",
   1321          "html-tables"													     },
   1322 	{ 1, gen_cmd, num_rd, num_wr, 0, 1,             &dds.frames,                  "html_frames",
   1323          "html-frames"													     },
   1324 	{ 1, gen_cmd, num_rd, num_wr, 0, 1,             &dds.break_long_lines,
   1325          "html_break_long_lines",								    "html-break-long-lines"       },
   1326 	{ 1, gen_cmd, num_rd, num_wr, 0, 1,             &dds.images,                  "html_images",
   1327          "html-images"													     },
   1328 	{ 1, gen_cmd, num_rd, num_wr, 0, 1,             &dds.image_names,
   1329          "html_image_names",									 "html-image-names"            },
   1330 	{ 1, gen_cmd, num_rd, num_wr, 0, 9,             &dds.margin,                  "html_margin",
   1331          "html-margin"													     },
   1332 	{ 1, gen_cmd, num_rd, num_wr, 0, 1,             &dds.num_links,
   1333          "html_numbered_links",								      "html-numbered-links"         },
   1334 	{ 1, gen_cmd, num_rd, num_wr, 0, 1,             &dds.table_order,
   1335          "html_table_order",									 "html-table-order"            },
   1336 	{ 1, gen_cmd, num_rd, num_wr, 0, 1,             &dds.auto_refresh,
   1337          "html_auto_refresh",									"html-auto-refresh"           },
   1338 	{ 1, gen_cmd, num_rd, num_wr, 1, MAX_FONT_SIZE, &dds.font_size,
   1339          "html_font_size",									   "html-user-font-size"         },
   1340 	{ 1, gen_cmd, num_rd, num_wr, 0, 1,             &dds.display_images,
   1341          "html_display_images",								      "html-display-images"         },
   1342 	{ 1, gen_cmd, num_rd, num_wr, 1, 999,           &dds.image_scale,
   1343          "html_image_scale",									 "html-image-scale"            },
   1344 	{ 1, gen_cmd, num_rd, num_wr, 0, 1,             &dds.porn_enable,
   1345          "html_bare_image_autoscale",								"html-bare-image-autoscale"   },
   1346 	{ 1, gen_cmd, num_rd, num_wr, 0, 1,             &dds.target_in_new_window,
   1347          "html_target_in_new_window",								"html-target-in-new-window"   },
   1348 	{ 1, gen_cmd, num_rd, num_wr, 0, 15,            &dds.t_text_color,
   1349          "html_text_color",									  "html-text-color"             },
   1350 	{ 1, gen_cmd, num_rd, num_wr, 0, 15,            &dds.t_link_color,
   1351          "html_link_color",									  "html-link-color"             },
   1352 	{ 1, gen_cmd, num_rd, num_wr, 0, 7,             &dds.t_background_color,
   1353          "html_background_color",								    "html-background-color"       },
   1354 	{ 1, gen_cmd, num_rd, num_wr, 0, 1,             &dds.t_ignore_document_color,
   1355          "html_ignore_document_color",							       "html-ignore-document-color"  },
   1356 	{ 1, gen_cmd, num_rd, num_wr, 0, 0xffffff,      &dds.g_text_color,
   1357          "html_g_text_color",									"html-g-text-color"           },
   1358 	{ 1, gen_cmd, num_rd, num_wr, 0, 0xffffff,      &dds.g_link_color,
   1359          "html_g_link_color",									"html-g-link-color"           },
   1360 	{ 1, gen_cmd, num_rd, num_wr, 0, 0xffffff,      &dds.g_background_color,
   1361          "html_g_background_color",								  "html-g-background-color"     },
   1362 	{ 1, gen_cmd, num_rd, num_wr, 0, 1,             &dds.g_ignore_document_color,
   1363          "html_g_ignore_document_color",							     "html-g-ignore-document-color"},
   1364 	{ 0, NULL,    NULL,   NULL,   0, 0,             NULL,                         NULL,          NULL                          },
   1365 };
   1366 
   1367 static struct option *all_options[] = {
   1368 	links_options,
   1369 	html_options,
   1370 	NULL,
   1371 };
   1372 
   1373 unsigned char *
   1374 parse_options(int argc, char *argv[])
   1375 {
   1376 	int i;
   1377 	unsigned char **u_argv, *ret;
   1378 	if ((argc * sizeof(unsigned char *)) > INT_MAX)
   1379 		overalloc();
   1380 	u_argv = xmalloc(argc * sizeof(unsigned char *));
   1381 	for (i = 0; i < argc; i++)
   1382 		u_argv[i] = cast_uchar argv[i];
   1383 	ret = p_arse_options(argc, u_argv, all_options);
   1384 	free(u_argv);
   1385 	return ret;
   1386 }
   1387 
   1388 static void
   1389 load_config_file(unsigned char *prefix, unsigned char *name)
   1390 {
   1391 	unsigned char *c, *config_file;
   1392 	config_file = stracpy(prefix);
   1393 	if (!config_file)
   1394 		return;
   1395 	add_to_strn(&config_file, name);
   1396 	if ((c = read_config_file(config_file)))
   1397 		goto ok;
   1398 	free(config_file);
   1399 	config_file = stracpy(prefix);
   1400 	if (!config_file)
   1401 		return;
   1402 	add_to_strn(&config_file, cast_uchar ".");
   1403 	add_to_strn(&config_file, name);
   1404 	if ((c = read_config_file(config_file)))
   1405 		goto ok;
   1406 	free(config_file);
   1407 	return;
   1408 ok:
   1409 	parse_config_file(config_file, c, all_options);
   1410 	free(c);
   1411 	free(config_file);
   1412 }
   1413 
   1414 void
   1415 load_config(void)
   1416 {
   1417 #ifdef SHARED_CONFIG_DIR
   1418 	load_config_file(cast_uchar SHARED_CONFIG_DIR, cast_uchar "links.cfg");
   1419 #endif
   1420 	load_config_file(links_home, cast_uchar "links.cfg");
   1421 	load_config_file(links_home, cast_uchar "html.cfg");
   1422 	load_config_file(links_home, cast_uchar "user.cfg");
   1423 }
   1424 
   1425 void
   1426 write_config(struct terminal *term)
   1427 {
   1428 	write_config_data(links_home, cast_uchar "links.cfg", links_options,
   1429 	                  term);
   1430 }
   1431 
   1432 void
   1433 write_html_config(struct terminal *term)
   1434 {
   1435 	write_config_data(links_home, cast_uchar "html.cfg", html_options,
   1436 	                  term);
   1437 }
   1438 
   1439 void
   1440 load_url_history(void)
   1441 {
   1442 	unsigned char *history_file, *hs;
   1443 	unsigned char *hsp;
   1444 
   1445 	if (anonymous)
   1446 		return;
   1447 	/* Must have been called after init_home */
   1448 	if (!links_home)
   1449 		return;
   1450 	history_file = stracpy(links_home);
   1451 	add_to_strn(&history_file, cast_uchar "links.his");
   1452 	hs = read_config_file(history_file);
   1453 	free(history_file);
   1454 	if (!hs)
   1455 		return;
   1456 	for (hsp = hs; *hsp;) {
   1457 		unsigned char *hsl, *hsc;
   1458 		for (hsl = hsp; *hsl && *hsl != 10 && *hsl != 13; hsl++)
   1459 			;
   1460 		hsc = memacpy(hsp, hsl - hsp);
   1461 		add_to_history(NULL, &goto_url_history, hsc);
   1462 		free(hsc);
   1463 		hsp = hsl;
   1464 		while (*hsp == 10 || *hsp == 13)
   1465 			hsp++;
   1466 	}
   1467 	free(hs);
   1468 }
   1469 
   1470 void
   1471 save_url_history(void)
   1472 {
   1473 	struct history_item *hi = NULL;
   1474 	struct list_head *lhi;
   1475 	unsigned char *history_file;
   1476 	unsigned char *hs = NULL;
   1477 	size_t hsl = 0;
   1478 	if (anonymous || !save_history || proxies.only_proxies)
   1479 		return;
   1480 
   1481 	/* Must have been called after init_home */
   1482 	if (!links_home)
   1483 		return;
   1484 	history_file = stracpy(links_home);
   1485 	add_to_strn(&history_file, cast_uchar "links.his");
   1486 	foreachback (struct history_item, hi, lhi, goto_url_history.items) {
   1487 		if (!*hi->str || hi->str[0] == ' '
   1488 		    || strchr(cast_const_char hi->str, 10)
   1489 		    || strchr(cast_const_char hi->str, 13))
   1490 			continue;
   1491 		if (!url_not_saveable(hi->str)) {
   1492 			hsl = add_to_str(&hs, hsl, hi->str);
   1493 			hsl = add_chr_to_str(&hs, hsl, '\n');
   1494 		}
   1495 	}
   1496 	write_to_config_file(history_file, hs, 0);
   1497 	free(history_file);
   1498 	free(hs);
   1499 	return;
   1500 }