about summary refs log blame commit diff stats
path: root/settings.c
blob: ee88167bb40c8268d1cd6de087a5972acdcc2563 (plain) (tree)




















                                                                           
                    













                                                                        
                                         
















                                                                        
                                    














                                                                      
                                            

                                                                      
                                       
                                                                          
                                           














                                              
                                       
                                         
                                     
                                     


                                         
                                        

                                                 









                                                       
                                                  
                                                      
                                                 
                                                






                                                         
                                                  




                                                              
                                                         
                                                        
                                                  


                                                      

                                                             

                                                       
                                            












                                                                              

                                                                              







































                                                                          





                                      

































































                                            





                                       





                                           





                                





                                     




                                                                                                      
                                                                              
                                                                                                  





                                                                                                        
                                                                                                               
                                                                               








                                                                                                       
                                                                                                                    





                                                                                                          
                                                                                                  



















                                                                                                     

                                                                                             
                                                                                               

                                                                                                



                                                                                                                                    
                                                                                                   













                                                                                            
                                                                                      










































































                                                                           
                                                      













                                                              
                                                 













                                                              
                                                 

























                                              



































                                               










































                                                                      
                                                                             


















                                                                    
                                                                         












































































































































                                                                              
                                                                  
                                                                  



                                                                  

                                                                  
                                                                  
                                                                  

                                                                  



                                                                  


                                                                  
                                                                  

                                                                  
                                                                  
                                                                  
                                                                  





                                                                  

                                                                  








                                                                  
                                                                  


                                                                  





                                                                  










                                                                  

                                                                  

                                                                  

                                                                  








                                                                  

                                                                  
                                                                  
                                                                  
















                                                                  

                                                          



                                                                  


















































































































































































                                                                          











                                               
                           





















                                                                 












                                                                        




















                                           












































                                                             










                                           















































                                                                                

                                 
                                      








                                            
                                         
                                    
                                     






                           





































                                                










                                           
                                                                 




















































































































                                                                              
                                                         



































































































































































































































                                                                                         
/*
 * Copyright (c) 2010, 2011 Marco Peereboom <marco@peereboom.us>
 * Copyright (c) 2011 Stevan Andjelkovic <stevan@student.chalmers.se>
 * Copyright (c) 2010, 2011 Edd Barrett <vext01@gmail.com>
 * Copyright (c) 2011 Todd T. Fries <todd@fries.net>
 * Copyright (c) 2011 Raphael Graf <r@undefined.ch>
 * Copyright (c) 2011 Michal Mazurek <akfaew@jasminek.net>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <xxxterm.h>

/* globals */
SoupURI			*proxy_uri = NULL;
PangoFontDescription	*cmd_font;
PangoFontDescription	*oops_font;
PangoFontDescription	*statusbar_font;
PangoFontDescription	*tabbar_font;

/* settings that require restart */
int		tabless = 0;	/* allow only 1 tab */
int		enable_socket = 0;
int		single_instance = 0; /* only allow one xxxterm to run */
int		fancy_bar = 1;	/* fancy toolbar */
int		browser_mode = XT_BM_NORMAL;
int		gui_mode = XT_GM_CLASSIC;
int		enable_localstorage = 1;
char		*statusbar_elems = NULL;

/* runtime settings */
int		show_tabs = 1;	/* show tabs on notebook */
int		tab_style = XT_TABS_NORMAL; /* tab bar style */
int		show_url = 1;	/* show url toolbar on notebook */
int		show_statusbar = 0; /* vimperator style status bar */
int		ctrl_click_focus = 0; /* ctrl click gets focus */
int		cookies_enabled = 1; /* enable cookies */
int		read_only_cookies = 0; /* enable to not write cookies */
int		enable_scripts = 1;
int		enable_plugins = 1;
gfloat		default_zoom_level = 1.0;
char		default_script[PATH_MAX];
int		window_height = 768;
int		window_width = 1024;
int		window_maximize = 0;
int		icon_size = 2; /* 1 = smallest, 2+ = bigger */
int		refresh_interval = 10; /* download refresh interval */
int		enable_plugin_whitelist = 0;
int		enable_cookie_whitelist = 0;
int		enable_js_whitelist = 0;
int		session_timeout = 3600; /* cookie session timeout */
int		cookie_policy = SOUP_COOKIE_JAR_ACCEPT_ALWAYS;
char		*ssl_ca_file = NULL;
char		*resource_dir = NULL;
gboolean	ssl_strict_certs = FALSE;
int		append_next = 1; /* append tab after current tab */
char		*home = NULL;
char		*search_string = NULL;
char		*http_proxy = NULL;
char		download_dir[PATH_MAX];
int		download_mode = XT_DM_START;
char		runtime_settings[PATH_MAX]; /* override of settings */
int		allow_volatile_cookies = 0;
int		color_visited_uris = 1;
int		save_global_history = 0; /* save global history to disk */
struct user_agent	*user_agent = NULL;
int		save_rejected_cookies = 0;
int		session_autosave = 0;
int		guess_search = 0;
int		dns_prefetch = FALSE;
gint		max_connections = 25;
gint		max_host_connections = 5;
gint		enable_spell_checking = 0;
char		*spell_check_languages = NULL;
int		xterm_workaround = 0;
char		*url_regex = NULL;
int		history_autosave = 0;
char		search_file[PATH_MAX];
char		command_file[PATH_MAX];
char		*encoding = NULL;
int		autofocus_onload = 0;
int		js_autorun_enabled = 1;
int		edit_mode = XT_EM_HYBRID;
int		userstyle_global = 0;
int		auto_load_images = 1;
int		enable_autoscroll = 0;
int		enable_favicon_entry = 1;
int		enable_favicon_tabs = 0;
char		*external_editor = NULL;
int		referer_mode = XT_REFERER_ALWAYS;
char		*referer_custom = NULL;

char		*cmd_font_name = NULL;
char		*oops_font_name = NULL;
char		*statusbar_font_name = NULL;
char		*tabbar_font_name = NULL;

char		*get_download_dir(struct settings *);
char		*get_default_script(struct settings *);
char		*get_runtime_dir(struct settings *);
char		*get_tab_style(struct settings *);
char		*get_edit_mode(struct settings *);
char		*get_download_mode(struct settings *);
char		*get_work_dir(struct settings *);
char		*get_referer(struct settings *);

int		add_cookie_wl(struct settings *, char *);
int		add_js_wl(struct settings *, char *);
int		add_pl_wl(struct settings *, char *);
int		add_mime_type(struct settings *, char *);
int		add_alias(struct settings *, char *);
int		add_kb(struct settings *, char *);
int		add_ua(struct settings *, char *);

int		set_download_dir(struct settings *, char *);
int		set_default_script(struct settings *, char *);
int		set_runtime_dir(struct settings *, char *);
int		set_tab_style(struct settings *, char *);
int		set_edit_mode(struct settings *, char *);
int		set_work_dir(struct settings *, char *);
int		set_auto_load_images(char *value);
int		set_enable_autoscroll(char *value);
int		set_enable_favicon_entry(char *value);
int		set_enable_favicon_tabs(char *value);
int		set_download_mode(struct settings *, char *);
int		set_download_mode_rt(char *);
int		set_referer(struct settings *, char *);
int		set_referer_rt(char *);
int		set_external_editor(char *);

void		walk_mime_type(struct settings *, void (*)(struct settings *,
		    char *, void *), void *);
void		walk_alias(struct settings *, void (*)(struct settings *,
		    char *, void *), void *);
void		walk_cookie_wl(struct settings *, void (*)(struct settings *,
		    char *, void *), void *);
void		walk_js_wl(struct settings *, void (*)(struct settings *,
		    char *, void *), void *);
void		walk_pl_wl(struct settings *, void (*)(struct settings *,
		    char *, void *), void *);
void		walk_kb(struct settings *, void (*)(struct settings *, char *,
		    void *), void *);
void		walk_ua(struct settings *, void (*)(struct settings *, char *,
		    void *), void *);

int
set_http_proxy(char *proxy)
{
	SoupURI			*uri;

	if (proxy == NULL)
		return (1);

	/* see if we need to clear it instead */
	if (strlen(proxy) == 0) {
		setup_proxy(NULL);
		return (0);
	}

	uri = soup_uri_new(proxy);
	if (uri == NULL || !SOUP_URI_VALID_FOR_HTTP(uri))
		return (1);

	setup_proxy(proxy);

	soup_uri_free(uri);

	return (0);
}

struct special {
	int		(*set)(struct settings *, char *);
	char		*(*get)(struct settings *);
	void		(*walk)(struct settings *,
			    void (*cb)(struct settings *, char *, void *),
			    void *);
};

struct special		s_browser_mode = {
	set_browser_mode,
	get_browser_mode,
	NULL
};

struct special		s_gui_mode = {
	set_gui_mode,
	get_gui_mode,
	NULL
};

struct special		s_cookie = {
	set_cookie_policy,
	get_cookie_policy,
	NULL
};

struct special		s_alias = {
	add_alias,
	NULL,
	walk_alias
};

struct special		s_mime = {
	add_mime_type,
	NULL,
	walk_mime_type
};

struct special		s_js = {
	add_js_wl,
	NULL,
	walk_js_wl
};

struct special		s_pl = {
	add_pl_wl,
	NULL,
	walk_pl_wl
};

struct special		s_kb = {
	add_kb,
	NULL,
	walk_kb
};

struct special		s_cookie_wl = {
	add_cookie_wl,
	NULL,
	walk_cookie_wl
};

struct special		s_default_script = {
	set_default_script,
	get_default_script,
	NULL
};

struct special		s_download_dir = {
	set_download_dir,
	get_download_dir,
	NULL
};

struct special		s_work_dir = {
	set_work_dir,
	get_work_dir,
	NULL
};

struct special		s_tab_style = {
	set_tab_style,
	get_tab_style,
	NULL
};

struct special		s_edit_mode = {
	set_edit_mode,
	get_edit_mode,
	NULL
};

struct special		s_download_mode = {
	set_download_mode,
	get_download_mode,
	NULL
};

struct special		s_ua = {
	add_ua,
	NULL,
	walk_ua
};

struct special		s_referer = {
	set_referer,
	get_referer,
	NULL
};

struct settings		rs[] = {
	{ "allow_volatile_cookies",	XT_S_INT, 0,		&allow_volatile_cookies, NULL, NULL },
	{ "append_next",		XT_S_INT, 0,		&append_next, NULL, NULL },
	{ "autofocus_onload",		XT_S_INT, 0,		&autofocus_onload, NULL, NULL },
	{ "browser_mode",		XT_S_INT, 0, NULL, NULL,&s_browser_mode },
	{ "gui_mode",			XT_S_INT, 0, NULL, NULL,&s_gui_mode },
	{ "color_visited_uris",		XT_S_INT, 0,		&color_visited_uris, NULL, NULL },
	{ "cookie_policy",		XT_S_INT, 0, NULL, NULL,&s_cookie },
	{ "cookies_enabled",		XT_S_INT, 0,		&cookies_enabled, NULL, NULL },
	{ "ctrl_click_focus",		XT_S_INT, 0,		&ctrl_click_focus, NULL, NULL },
	{ "default_zoom_level",		XT_S_FLOAT, 0,		NULL, NULL, NULL, &default_zoom_level },
	{ "default_script",		XT_S_STR, 0, NULL, NULL,&s_default_script },
	{ "download_dir",		XT_S_STR, 0, NULL, NULL,&s_download_dir },
	{ "download_mode",		XT_S_STR, 0, NULL, NULL,&s_download_mode, NULL, set_download_mode_rt },
	{ "edit_mode",			XT_S_STR, 0, NULL, NULL,&s_edit_mode },
	{ "enable_cookie_whitelist",	XT_S_INT, 0,		&enable_cookie_whitelist, NULL, NULL },
	{ "enable_js_whitelist",	XT_S_INT, 0,		&enable_js_whitelist, NULL, NULL },
	{ "enable_plugin_whitelist",	XT_S_INT, 0,		&enable_plugin_whitelist, NULL, NULL },
	{ "enable_localstorage",	XT_S_INT, 0,		&enable_localstorage, NULL, NULL },
	{ "enable_plugins",		XT_S_INT, 0,		&enable_plugins, NULL, NULL },
	{ "enable_scripts",		XT_S_INT, 0,		&enable_scripts, NULL, NULL },
	{ "enable_socket",		XT_S_INT, XT_SF_RESTART,&enable_socket, NULL, NULL },
	{ "enable_spell_checking",	XT_S_INT, 0,		&enable_spell_checking, NULL, NULL },
	{ "encoding",			XT_S_STR, 0, NULL,	&encoding, NULL },
	{ "external_editor",		XT_S_STR,0, NULL,	&external_editor, NULL, NULL, set_external_editor },
	{ "fancy_bar",			XT_S_INT, XT_SF_RESTART,&fancy_bar, NULL, NULL },
	{ "guess_search",		XT_S_INT, 0,		&guess_search, NULL, NULL },
	{ "history_autosave",		XT_S_INT, 0,		&history_autosave, NULL, NULL },
	{ "home",			XT_S_STR, 0, NULL,	&home, NULL },
	{ "http_proxy",			XT_S_STR, 0, NULL,	&http_proxy, NULL, NULL, set_http_proxy },
	{ "icon_size",			XT_S_INT, 0,		&icon_size, NULL, NULL },
	{ "js_autorun_enabled",		XT_S_INT, 0,		&js_autorun_enabled, NULL, NULL },
	{ "max_connections",		XT_S_INT, XT_SF_RESTART,&max_connections, NULL, NULL },
	{ "max_host_connections",	XT_S_INT, XT_SF_RESTART,&max_host_connections, NULL, NULL },
	{ "read_only_cookies",		XT_S_INT, 0,		&read_only_cookies, NULL, NULL },
	{ "refresh_interval",		XT_S_INT, 0,		&refresh_interval, NULL, NULL },
	{ "resource_dir",		XT_S_STR, 0, NULL,	&resource_dir, NULL },
	{ "search_string",		XT_S_STR, 0, NULL,	&search_string, NULL },
	{ "save_global_history",	XT_S_INT, XT_SF_RESTART,&save_global_history, NULL, NULL },
	{ "save_rejected_cookies",	XT_S_INT, XT_SF_RESTART,&save_rejected_cookies, NULL, NULL },
	{ "session_timeout",		XT_S_INT, 0,		&session_timeout, NULL, NULL },
	{ "session_autosave",		XT_S_INT, 0,		&session_autosave, NULL, NULL },
	{ "single_instance",		XT_S_INT, XT_SF_RESTART,&single_instance, NULL, NULL },
	{ "show_tabs",			XT_S_INT, 0,		&show_tabs, NULL, NULL },
	{ "show_url",			XT_S_INT, 0,		&show_url, NULL, NULL },
	{ "show_statusbar",		XT_S_INT, 0,		&show_statusbar, NULL, NULL },
	{ "spell_check_languages",	XT_S_STR, 0, NULL, &spell_check_languages, NULL },
	{ "ssl_ca_file",		XT_S_STR, 0, NULL,	&ssl_ca_file, NULL },
	{ "ssl_strict_certs",		XT_S_INT, 0,		&ssl_strict_certs, NULL, NULL },
	{ "statusbar_elems",		XT_S_STR, 0, NULL,	&statusbar_elems, NULL },
	{ "tab_style",			XT_S_STR, 0, NULL, NULL,&s_tab_style },
	{ "url_regex",			XT_S_STR, 0, NULL,	&url_regex, NULL },
	{ "window_height",		XT_S_INT, 0,		&window_height, NULL, NULL },
	{ "window_width",		XT_S_INT, 0,		&window_width, NULL, NULL },
	{ "window_maximize",		XT_S_INT, 0,		&window_maximize, NULL, NULL },
	{ "work_dir",			XT_S_STR, 0, NULL, NULL,&s_work_dir },
	{ "xterm_workaround",		XT_S_INT, 0,		&xterm_workaround, NULL, NULL },
	{ "auto_load_images",		XT_S_INT, 0,		&auto_load_images, NULL, NULL, NULL, set_auto_load_images },
	{ "enable_autoscroll",		XT_S_INT, 0,		&enable_autoscroll, NULL, NULL, NULL, set_enable_autoscroll },
	{ "enable_favicon_entry",	XT_S_INT, 0,		&enable_favicon_entry, NULL, NULL, NULL, set_enable_favicon_entry },
	{ "enable_favicon_tabs",	XT_S_INT, 0,		&enable_favicon_tabs, NULL, NULL, NULL, set_enable_favicon_tabs },
	{ "referer",			XT_S_STR, 0, NULL, NULL,&s_referer, NULL, set_referer_rt },

	/* font settings */
	{ "cmd_font",			XT_S_STR, 0, NULL, &cmd_font_name, NULL },
	{ "oops_font",			XT_S_STR, 0, NULL, &oops_font_name, NULL },
	{ "statusbar_font",		XT_S_STR, 0, NULL, &statusbar_font_name, NULL },
	{ "tabbar_font",		XT_S_STR, 0, NULL, &tabbar_font_name, NULL },

	/* runtime settings */
	{ "alias",			XT_S_STR, XT_SF_RUNTIME, NULL, NULL, &s_alias },
	{ "cookie_wl",			XT_S_STR, XT_SF_RUNTIME, NULL, NULL, &s_cookie_wl },
	{ "js_wl",			XT_S_STR, XT_SF_RUNTIME, NULL, NULL, &s_js },
	{ "keybinding",			XT_S_STR, XT_SF_RUNTIME, NULL, NULL, &s_kb },
	{ "mime_type",			XT_S_STR, XT_SF_RUNTIME, NULL, NULL, &s_mime },
	{ "pl_wl",			XT_S_STR, XT_SF_RUNTIME, NULL, NULL, &s_pl },
	{ "user_agent",			XT_S_STR, XT_SF_RUNTIME, NULL,	NULL, &s_ua },
};

size_t
get_settings_size(void)
{
	return (LENGTH(rs));
}

char *
get_setting_name(int i)
{
	if (i > LENGTH(rs))
		return (NULL);
	return (rs[i].name);
}

char *
get_as_string(struct settings *s)
{
	char			*r = NULL;

	if (s == NULL)
		return (NULL);

	if (s->s) {
		if (s->s->get)
			r = s->s->get(s);
		else
			warnx("get_as_string skip %s\n", s->name);
	} else if (s->type == XT_S_INT)
		r = g_strdup_printf("%d", *s->ival);
	else if (s->type == XT_S_STR)
		r = g_strdup(*s->sval);
	else if (s->type == XT_S_FLOAT)
		r = g_strdup_printf("%f", *s->fval);
	else
		r = g_strdup_printf("INVALID TYPE");

	return (r);
}

void
settings_walk(void (*cb)(struct settings *, char *, void *), void *cb_args)
{
	int			i;
	char			*s;

	for (i = 0; i < LENGTH(rs); i++) {
		if (rs[i].s && rs[i].s->walk)
			rs[i].s->walk(&rs[i], cb, cb_args);
		else {
			s = get_as_string(&rs[i]);
			cb(&rs[i], s, cb_args);
			g_free(s);
		}
	}
}

int
set_browser_mode(struct settings *s, char *val)
{
	if (!strcmp(val, "whitelist")) {
		browser_mode = XT_BM_WHITELIST;
		allow_volatile_cookies	= 0;
		cookie_policy = SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY;
		cookies_enabled = 1;
		enable_cookie_whitelist = 1;
		enable_plugin_whitelist = 1;
		enable_plugins = 0;
		read_only_cookies = 0;
		save_rejected_cookies = 0;
		session_timeout = 3600;
		enable_scripts = 0;
		enable_js_whitelist = 1;
		enable_localstorage = 0;
		referer_mode = XT_REFERER_SAME_DOMAIN;
	} else if (!strcmp(val, "normal")) {
		browser_mode = XT_BM_NORMAL;
		allow_volatile_cookies = 0;
		cookie_policy = SOUP_COOKIE_JAR_ACCEPT_ALWAYS;
		cookies_enabled = 1;
		enable_cookie_whitelist = 0;
		enable_plugin_whitelist = 0;
		enable_plugins = 1;
		read_only_cookies = 0;
		save_rejected_cookies = 0;
		session_timeout = 3600;
		enable_scripts = 1;
		enable_js_whitelist = 0;
		enable_localstorage = 1;
		referer_mode = XT_REFERER_ALWAYS;
	} else if (!strcmp(val, "kiosk")) {
		browser_mode = XT_BM_KIOSK;
		allow_volatile_cookies = 0;
		cookie_policy = SOUP_COOKIE_JAR_ACCEPT_ALWAYS;
		cookies_enabled = 1;
		enable_cookie_whitelist = 0;
		enable_plugin_whitelist = 0;
		enable_plugins = 1;
		read_only_cookies = 0;
		save_rejected_cookies = 0;
		session_timeout = 3600;
		enable_scripts = 1;
		enable_js_whitelist = 0;
		enable_localstorage = 1;
		referer_mode = XT_REFERER_ALWAYS;
		show_tabs = 0;
		tabless = 1;
	} else
		return (1);

	return (0);
}

char *
get_browser_mode(struct settings *s)
{
	char			*r = NULL;

	if (browser_mode == XT_BM_WHITELIST)
		r = g_strdup("whitelist");
	else if (browser_mode == XT_BM_NORMAL)
		r = g_strdup("normal");
	else if (browser_mode == XT_BM_KIOSK)
		r = g_strdup("kiosk");
	else
		return (NULL);

	return (r);
}

int
set_gui_mode(struct settings *s, char *val)
{
	if (!strcmp(val, "classic")) {
		fancy_bar = 1;
		show_tabs = 1;
		tab_style = XT_TABS_NORMAL;
		show_url = 1;
		show_statusbar = 0;
	} else if (!strcmp(val, "minimal")) {
		fancy_bar = 0;
		show_tabs = 1;
		tab_style = XT_TABS_COMPACT;
		show_url = 0;
		show_statusbar = 1;
	} else
		return (1);

	return (0);
}

char *
get_gui_mode(struct settings *s)
{
	char			*r = NULL;

	if (gui_mode == XT_GM_CLASSIC)
		r = g_strdup("classic");
	else if (browser_mode == XT_GM_MINIMAL)
		r = g_strdup("minimal");
	else
		return (NULL);

	return (r);
}

int
set_cookie_policy(struct settings *s, char *val)
{
	if (!strcmp(val, "no3rdparty"))
		cookie_policy = SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY;
	else if (!strcmp(val, "accept"))
		cookie_policy = SOUP_COOKIE_JAR_ACCEPT_ALWAYS;
	else if (!strcmp(val, "reject"))
		cookie_policy = SOUP_COOKIE_JAR_ACCEPT_NEVER;
	else
		return (1);

	return (0);
}

char *
get_cookie_policy(struct settings *s)
{
	char			*r = NULL;

	if (cookie_policy == SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY)
		r = g_strdup("no3rdparty");
	else if (cookie_policy == SOUP_COOKIE_JAR_ACCEPT_ALWAYS)
		r = g_strdup("accept");
	else if (cookie_policy == SOUP_COOKIE_JAR_ACCEPT_NEVER)
		r = g_strdup("reject");
	else
		return (NULL);

	return (r);
}

char *
get_default_script(struct settings *s)
{
	if (default_script[0] == '\0')
		return (0);
	return (g_strdup(default_script));
}

int
set_default_script(struct settings *s, char *val)
{
	if (val[0] == '~')
		snprintf(default_script, sizeof default_script, "%s" PS "%s",
		    pwd->pw_dir, &val[1]);
	else
		strlcpy(default_script, val, sizeof default_script);

	return (0);
}

char *
get_download_dir(struct settings *s)
{
	if (download_dir[0] == '\0')
		return (0);
	return (g_strdup(download_dir));
}

int
set_download_dir(struct settings *s, char *val)
{
	if (val[0] == '~')
		snprintf(download_dir, sizeof download_dir, "%s" PS "%s",
		    pwd->pw_dir, &val[1]);
	else
		strlcpy(download_dir, val, sizeof download_dir);

	return (0);
}

int
add_alias(struct settings *s, char *line)
{
	char			*l, *alias;
	struct alias		*a = NULL;

	if (s == NULL || line == NULL) {
		show_oops(NULL, "add_alias invalid parameters");
		return (1);
	}

	l = line;
	a = g_malloc(sizeof(*a));

	if ((alias = strsep(&l, " \t,")) == NULL || l == NULL) {
		show_oops(NULL, "add_alias: incomplete alias definition");
		goto bad;
	}
	if (strlen(alias) == 0 || strlen(l) == 0) {
		show_oops(NULL, "add_alias: invalid alias definition");
		goto bad;
	}

	a->a_name = g_strdup(alias);
	a->a_uri = g_strdup(l);

	DNPRINTF(XT_D_CONFIG, "add_alias: %s for %s\n", a->a_name, a->a_uri);

	TAILQ_INSERT_TAIL(&aliases, a, entry);

	return (0);
bad:
	if (a)
		g_free(a);
	return (1);
}

void
walk_alias(struct settings *s,
    void (*cb)(struct settings *, char *, void *), void *cb_args)
{
	struct alias		*a;
	char			*str;

	if (s == NULL || cb == NULL) {
		show_oops(NULL, "walk_alias invalid parameters");
		return;
	}

	TAILQ_FOREACH(a, &aliases, entry) {
		str = g_strdup_printf("%s --> %s", a->a_name, a->a_uri);
		cb(s, str, cb_args);
		g_free(str);
	}
}

int
add_mime_type(struct settings *s, char *line)
{
	char			*mime_type;
	char			*l;
	struct mime_type	*m = NULL;
	int			downloadfirst = 0;

	/* XXX this could be smarter */

	if (line == NULL || strlen(line) == 0) {
		show_oops(NULL, "add_mime_type invalid parameters");
		return (1);
	}

	l = line;
	if (*l == '@') {
		downloadfirst = 1;
		l++;
	}
	m = g_malloc(sizeof(*m));

	if ((mime_type = strsep(&l, " \t,")) == NULL || l == NULL) {
		show_oops(NULL, "add_mime_type: invalid mime_type");
		goto bad;
	}
	if (mime_type[strlen(mime_type) - 1] == '*') {
		mime_type[strlen(mime_type) - 1] = '\0';
		m->mt_default = 1;
	} else
		m->mt_default = 0;

	if (strlen(mime_type) == 0 || strlen(l) == 0) {
		show_oops(NULL, "add_mime_type: invalid mime_type");
		goto bad;
	}

	m->mt_type = g_strdup(mime_type);
	m->mt_action = g_strdup(l);
	m->mt_download = downloadfirst;

	DNPRINTF(XT_D_CONFIG, "add_mime_type: type %s action %s default %d\n",
	    m->mt_type, m->mt_action, m->mt_default);

	TAILQ_INSERT_TAIL(&mtl, m, entry);

	return (0);
bad:
	if (m)
		g_free(m);
	return (1);
}

void
walk_mime_type(struct settings *s,
    void (*cb)(struct settings *, char *, void *), void *cb_args)
{
	struct mime_type	*m;
	char			*str;

	if (s == NULL || cb == NULL) {
		show_oops(NULL, "walk_mime_type invalid parameters");
		return;
	}

	TAILQ_FOREACH(m, &mtl, entry) {
		str = g_strdup_printf("%s%s --> %s",
		    m->mt_type,
		    m->mt_default ? "*" : "",
		    m->mt_action);
		cb(s, str, cb_args);
		g_free(str);
	}
}

/* inherent to GTK not all keys will be caught at all times */
/* XXX sort key bindings */
struct key_binding	keys[] = {
	{ "command_mode",	0,	1,	GDK_Escape	},
	{ "insert_mode",	0,	0,	GDK_i		},
	{ "cookiejar",		MOD1,	1,	GDK_j		},
	{ "downloadmgr",	MOD1,	1,	GDK_d		},
	{ "history",		MOD1,	1,	GDK_h		},
	{ "print",		CTRL,	1,	GDK_p		},
	{ "search",		0,	0,	GDK_slash	},
	{ "searchb",		0,	0,	GDK_question	},
	{ "statustoggle",	CTRL,	1,	GDK_n		},
	{ "command",		0,	0,	GDK_colon	},
	{ "qa",			CTRL,	1,	GDK_q		},
	{ "restart",		MOD1,	1,	GDK_q		},
	{ "js toggle",		CTRL,	1,	GDK_j		},
	{ "plugin toggle",	MOD1,	1,	GDK_p		},
	{ "cookie toggle",	MOD1,	1,	GDK_c		},
	{ "togglesrc",		CTRL,	1,	GDK_s		},
	{ "yankuri",		0,	0,	GDK_y		},
	{ "pasteuricur",	0,	0,	GDK_p		},
	{ "pasteurinew",	0,	0,	GDK_P		},
	{ "toplevel toggle",	0,	1,	GDK_F4		},
	{ "help",		0,	1,	GDK_F1		},
	{ "run_script",		MOD1,	1,	GDK_r		},
	{ "proxy toggle",	0,	1,	GDK_F2		},
	{ "editelement",	CTRL,	1,	GDK_i		},
	{ "passthrough",	CTRL,	1,	GDK_z		},

	/* search */
	{ "searchnext",		0,	0,	GDK_n		},
	{ "searchprevious",	0,	0,	GDK_N		},

	/* focus */
	{ "focusaddress",	0,	1,	GDK_F6		},
	{ "focussearch",	0,	1,	GDK_F7		},

	/* hinting */
	{ "hinting",		0,	0,	GDK_f		},
	{ "hinting",		0,	0,	GDK_period	},
	{ "hinting_newtab",	SHFT,	0,	GDK_F		},
	{ "hinting_newtab",	0,	0,	GDK_comma	},

	/* custom stylesheet */
	{ "userstyle",		0,	0,	GDK_s		},
	{ "userstyle_global",	SHFT,	0,	GDK_S		},

	/* navigation */
	{ "goback",		0,	0,	GDK_BackSpace	},
	{ "goback",		MOD1,	1,	GDK_Left	},
	{ "goforward",		SHFT,	1,	GDK_BackSpace	},
	{ "goforward",		MOD1,	1,	GDK_Right	},
	{ "reload",		0,	1,	GDK_F5		},
	{ "reload",		CTRL,	1,	GDK_r		},
	{ "reload",		CTRL,	1,	GDK_l		},
	{ "favorites",		MOD1,	1,	GDK_f		},

	/* vertical movement */
	{ "scrolldown",		0,	0,	GDK_j		},
	{ "scrolldown",		0,	0,	GDK_Down	},
	{ "scrollup",		0,	0,	GDK_Up		},
	{ "scrollup",		0,	0,	GDK_k		},
	{ "scrollbottom",	0,	0,	GDK_G		},
	{ "scrollbottom",	0,	0,	GDK_End		},
	{ "scrolltop",		0,	0,	GDK_Home	},
	{ "scrollpagedown",	0,	0,	GDK_space	},
	{ "scrollpagedown",	CTRL,	1,	GDK_f		},
	{ "scrollhalfdown",	CTRL,	1,	GDK_d		},
	{ "scrollpagedown",	0,	0,	GDK_Page_Down	},
	{ "scrollpageup",	0,	0,	GDK_Page_Up	},
	{ "scrollpageup",	CTRL,	1,	GDK_b		},
	{ "scrollhalfup",	CTRL,	1,	GDK_u		},
	/* horizontal movement */
	{ "scrollright",	0,	0,	GDK_l		},
	{ "scrollright",	0,	0,	GDK_Right	},
	{ "scrollleft",		0,	0,	GDK_Left	},
	{ "scrollleft",		0,	0,	GDK_h		},
	{ "scrollfarright",	0,	0,	GDK_dollar	},
	{ "scrollfarleft",	0,	0,	GDK_0		},

	/* tabs */
	{ "tabnew",		CTRL,	1,	GDK_t		},
	{ "999tabnew",		CTRL,	1,	GDK_T		},
	{ "tabclose",		CTRL,	1,	GDK_w		},
	{ "tabundoclose",	0,	0,	GDK_U		},
	{ "tabnext 1",		CTRL,	1,	GDK_1		},
	{ "tabnext 2",		CTRL,	1,	GDK_2		},
	{ "tabnext 3",		CTRL,	1,	GDK_3		},
	{ "tabnext 4",		CTRL,	1,	GDK_4		},
	{ "tabnext 5",		CTRL,	1,	GDK_5		},
	{ "tabnext 6",		CTRL,	1,	GDK_6		},
	{ "tabnext 7",		CTRL,	1,	GDK_7		},
	{ "tabnext 8",		CTRL,	1,	GDK_8		},
	{ "tabnext 9",		CTRL,	1,	GDK_9		},
	{ "tabfirst",		CTRL,	1,	GDK_less	},
	{ "tablast",		CTRL,	1,	GDK_greater	},
	{ "tabprevious",	CTRL,	1,	GDK_Left	},
	{ "tabnext",		CTRL,	1,	GDK_Right	},
	{ "focusout",		CTRL,	1,	GDK_minus	},
	{ "focusin",		CTRL,	1,	GDK_plus	},
	{ "focusin",		CTRL,	1,	GDK_equal	},
	{ "focusreset",		CTRL,	1,	GDK_0		},

	/* command aliases (handy when -S flag is used) */
	{ "promptopen",		0,	1,	GDK_F9		},
	{ "promptopencurrent",	0,	1,	GDK_F10		},
	{ "prompttabnew",	0,	1,	GDK_F11		},
	{ "prompttabnewcurrent",0,	1,	GDK_F12		},
};

void
walk_kb(struct settings *s,
    void (*cb)(struct settings *, char *, void *), void *cb_args)
{
	struct key_binding	*k;
	char			str[1024];

	if (s == NULL || cb == NULL) {
		show_oops(NULL, "walk_kb invalid parameters");
		return;
	}

	TAILQ_FOREACH(k, &kbl, entry) {
		if (k->cmd == NULL)
			continue;
		str[0] = '\0';

		/* sanity */
		if (gdk_keyval_name(k->key) == NULL)
			continue;

		strlcat(str, k->cmd, sizeof str);
		strlcat(str, ",", sizeof str);

		if (k->mask & GDK_SHIFT_MASK)
			strlcat(str, "S-", sizeof str);
		if (k->mask & GDK_CONTROL_MASK)
			strlcat(str, "C-", sizeof str);
		if (k->mask & GDK_MOD1_MASK)
			strlcat(str, "M1-", sizeof str);
		if (k->mask & GDK_MOD2_MASK)
			strlcat(str, "M2-", sizeof str);
		if (k->mask & GDK_MOD3_MASK)
			strlcat(str, "M3-", sizeof str);
		if (k->mask & GDK_MOD4_MASK)
			strlcat(str, "M4-", sizeof str);
		if (k->mask & GDK_MOD5_MASK)
			strlcat(str, "M5-", sizeof str);

		strlcat(str, gdk_keyval_name(k->key), sizeof str);
		cb(s, str, cb_args);
	}
}

void
init_keybindings(void)
{
	int			i;
	struct key_binding	*k;

	for (i = 0; i < LENGTH(keys); i++) {
		k = g_malloc0(sizeof *k);
		k->cmd = keys[i].cmd;
		k->mask = keys[i].mask;
		k->use_in_entry = keys[i].use_in_entry;
		k->key = keys[i].key;
		TAILQ_INSERT_HEAD(&kbl, k, entry);

		DNPRINTF(XT_D_KEYBINDING, "init_keybindings: added: %s\n",
		    k->cmd ? k->cmd : "unnamed key");
	}
}

void
keybinding_clearall(void)
{
	struct key_binding	*k, *next;

	for (k = TAILQ_FIRST(&kbl); k; k = next) {
		next = TAILQ_NEXT(k, entry);
		if (k->cmd == NULL)
			continue;

		DNPRINTF(XT_D_KEYBINDING, "keybinding_clearall: %s\n",
		    k->cmd ? k->cmd : "unnamed key");
		TAILQ_REMOVE(&kbl, k, entry);
		g_free(k);
	}
}

int
keybinding_add(char *cmd, char *key, int use_in_entry)
{
	struct key_binding	*k;
	guint			keyval, mask = 0;
	int			i;

	DNPRINTF(XT_D_KEYBINDING, "keybinding_add: %s %s\n", cmd, key);

	/* Keys which are to be used in entry have been prefixed with an
	 * exclamation mark. */
	if (use_in_entry)
		key++;

	/* find modifier keys */
	if (strstr(key, "S-"))
		mask |= GDK_SHIFT_MASK;
	if (strstr(key, "C-"))
		mask |= GDK_CONTROL_MASK;
	if (strstr(key, "M1-"))
		mask |= GDK_MOD1_MASK;
	if (strstr(key, "M2-"))
		mask |= GDK_MOD2_MASK;
	if (strstr(key, "M3-"))
		mask |= GDK_MOD3_MASK;
	if (strstr(key, "M4-"))
		mask |= GDK_MOD4_MASK;
	if (strstr(key, "M5-"))
		mask |= GDK_MOD5_MASK;

	/* find keyname */
	for (i = strlen(key) - 1; i > 0; i--)
		if (key[i] == '-')
			key = &key[i + 1];

	/* validate keyname */
	keyval = gdk_keyval_from_name(key);
	if (keyval == GDK_VoidSymbol) {
		warnx("invalid keybinding name %s", key);
		return (1);
	}
	/* must run this test too, gtk+ doesn't handle 10 for example */
	if (gdk_keyval_name(keyval) == NULL) {
		warnx("invalid keybinding name %s", key);
		return (1);
	}

	/* Remove eventual dupes. */
	TAILQ_FOREACH(k, &kbl, entry)
		if (k->key == keyval && k->mask == mask) {
			TAILQ_REMOVE(&kbl, k, entry);
			g_free(k);
			break;
		}

	/* add keyname */
	k = g_malloc0(sizeof *k);
	k->cmd = g_strdup(cmd);
	k->mask = mask;
	k->use_in_entry = use_in_entry;
	k->key = keyval;

	DNPRINTF(XT_D_KEYBINDING, "keybinding_add: %s 0x%x %d 0x%x\n",
	    k->cmd,
	    k->mask,
	    k->use_in_entry,
	    k->key);
	DNPRINTF(XT_D_KEYBINDING, "keybinding_add: adding: %s %s\n",
	    k->cmd, gdk_keyval_name(keyval));

	TAILQ_INSERT_HEAD(&kbl, k, entry);

	return (0);
}

int
add_kb(struct settings *s, char *entry)
{
	char			*kb, *key;

	DNPRINTF(XT_D_KEYBINDING, "add_kb: %s\n", entry);

	/* clearall is special */
	if (!strcmp(entry, "clearall")) {
		keybinding_clearall();
		return (0);
	}

	kb = strstr(entry, ",");
	if (kb == NULL)
		return (1);
	*kb = '\0';
	key = kb + 1;

	return (keybinding_add(entry, key, key[0] == '!'));
}

int
add_ua(struct settings *s, char *value)
{
	struct user_agent *ua;

	ua = g_malloc0(sizeof *ua);
	ua->value = g_strdup(value);

	TAILQ_INSERT_HEAD(&ua_list, ua, entry);

	/* use the last added user agent */
	user_agent = TAILQ_FIRST(&ua_list);
	user_agent_count++;

	return (0);
}


void
walk_ua(struct settings *s,
    void (*cb)(struct settings *, char *, void *), void *cb_args)
{
	struct user_agent		*ua;

	if (s == NULL || cb == NULL) {
		show_oops(NULL, "walk_ua invalid parameters");
		return;
	}

	TAILQ_FOREACH(ua, &ua_list, entry) {
		cb(s, ua->value, cb_args);
	}
}

int
set_auto_load_images(char *value)
{
	struct tab *t;

	auto_load_images = atoi(value);
	TAILQ_FOREACH(t, &tabs, entry) {
		g_object_set(G_OBJECT(t->settings),
		    "auto-load-images", auto_load_images, (char *)NULL);
		webkit_web_view_set_settings(t->wv, t->settings);
	}
	return (0);
}

int
set_enable_autoscroll(char *value)
{
	enable_autoscroll = atoi(value);
	return (0);
}

int
set_enable_favicon_entry(char *value)
{
	enable_favicon_entry = atoi(value);
	return (0);
}

int
set_enable_favicon_tabs(char *value)
{
	enable_favicon_tabs = atoi(value);
	return (0);
}

char *
get_referer(struct settings *s)
{
	if (referer_mode == XT_REFERER_ALWAYS)
		return (g_strdup("always"));
	if (referer_mode == XT_REFERER_NEVER)
		return (g_strdup("never"));
	if (referer_mode == XT_REFERER_SAME_DOMAIN)
		return (g_strdup("same-domain"));
	if (referer_mode == XT_REFERER_CUSTOM)
		return (g_strdup(referer_custom));
	return (NULL);
}

int
set_referer(struct settings *s, char *value)
{
	if (referer_custom)
		g_free(referer_custom);

	if (!strcmp(value, "always"))
		referer_mode = XT_REFERER_ALWAYS;
	else if (!strcmp(value, "never"))
		referer_mode = XT_REFERER_NEVER;
	else if (!strcmp(value, "same-domain"))
		referer_mode = XT_REFERER_SAME_DOMAIN;
	else if (!valid_url_type(value)) {
		referer_mode = XT_REFERER_CUSTOM;
		referer_custom = g_strdup(value);
	} else {
		/* we've already free'd the custom referer */
		if (referer_mode == XT_REFERER_CUSTOM)
			referer_mode = XT_REFERER_NEVER;
		return (1);
	}

	return (0);
}

int
set_referer_rt(char *value)
{
	return set_referer(NULL, value);
}

int
set_external_editor(char *editor)
{
	if (external_editor)
		g_free(external_editor);

	external_editor = g_strdup(editor);

	return (0);
}

void
setup_proxy(char *uri)
{
	if (proxy_uri) {
		g_object_set(session, "proxy_uri", NULL, (char *)NULL);
		soup_uri_free(proxy_uri);
		proxy_uri = NULL;
	}
	if (http_proxy) {
		if (http_proxy != uri) {
			g_free(http_proxy);
			http_proxy = NULL;
		}
	}

	if (uri) {
		http_proxy = g_strdup(uri);
		DNPRINTF(XT_D_CONFIG, "setup_proxy: %s\n", uri);
		proxy_uri = soup_uri_new(http_proxy);
		if (!(proxy_uri == NULL || !SOUP_URI_VALID_FOR_HTTP(proxy_uri)))
			g_object_set(session, "proxy-uri", proxy_uri,
			    (char *)NULL);
	}
}

char *
get_tab_style(struct settings *s)
{
	if (tab_style == XT_TABS_NORMAL)
		return (g_strdup("normal"));
	else
		return (g_strdup("compact"));
}

int
set_tab_style(struct settings *s, char *val)
{
	if (!strcmp(val, "normal"))
		tab_style = XT_TABS_NORMAL;
	else if (!strcmp(val, "compact"))
		tab_style = XT_TABS_COMPACT;
	else
		return (1);

	return (0);
}

char *
get_edit_mode(struct settings *s)
{
	if (edit_mode == XT_EM_HYBRID)
		return (g_strdup("hybrid"));
	else
		return (g_strdup("vi"));
}

int
set_edit_mode(struct settings *s, char *val)
{
	if (!strcmp(val, "hybrid"))
		edit_mode = XT_EM_HYBRID;
	else if (!strcmp(val, "vi"))
		edit_mode = XT_EM_VI;
	else
		return (1);

	return (0);
}

char *
get_download_mode(struct settings *s)
{
	switch (download_mode) {
	case XT_DM_START:
		return (g_strdup("start"));
		break;
	case XT_DM_ASK:
		return (g_strdup("ask"));
		break;
	case XT_DM_ADD:
		return (g_strdup("add"));
		break;
	}
	return (g_strdup("unknown"));
}

int
set_download_mode(struct settings *s, char *val)
{
	if (!strcmp(val, "start"))
		download_mode = XT_DM_START;
	else if (!strcmp(val, "ask"))
		download_mode = XT_DM_ASK;
	else if (!strcmp(val, "add"))
		download_mode = XT_DM_ADD;
	else
		return (1);

	return (0);
}

int
set_download_mode_rt(char *val)
{
	return set_download_mode(NULL, val);
}

char *
get_work_dir(struct settings *s)
{
	if (work_dir[0] == '\0')
		return (0);
	return (g_strdup(work_dir));
}

int
set_work_dir(struct settings *s, char *val)
{
	if (val[0] == '~')
		snprintf(work_dir, sizeof work_dir, "%s" PS "%s",
		    pwd->pw_dir, &val[1]);
	else
		strlcpy(work_dir, val, sizeof work_dir);

	return (0);
}

void
walk_cookie_wl(struct settings *s,
    void (*cb)(struct settings *, char *, void *), void *cb_args)
{
	struct domain		*d;

	if (s == NULL || cb == NULL) {
		show_oops(NULL, "walk_cookie_wl invalid parameters");
		return;
	}

	RB_FOREACH_REVERSE(d, domain_list, &c_wl)
		cb(s, d->d, cb_args);
}

void
walk_js_wl(struct settings *s,
    void (*cb)(struct settings *, char *, void *), void *cb_args)
{
	struct domain		*d;

	if (s == NULL || cb == NULL) {
		show_oops(NULL, "walk_js_wl invalid parameters");
		return;
	}

	RB_FOREACH_REVERSE(d, domain_list, &js_wl)
		cb(s, d->d, cb_args);
}

void
walk_pl_wl(struct settings *s,
    void (*cb)(struct settings *, char *, void *), void *cb_args)
{
	struct domain		*d;

	if (s == NULL || cb == NULL) {
		show_oops(NULL, "walk_pl_wl invalid parameters");
		return;
	}

	RB_FOREACH_REVERSE(d, domain_list, &pl_wl)
		cb(s, d->d, cb_args);
}

int
settings_add(char *var, char *val)
{
	int i, rv, *p;
	gfloat *f;
	char **s;

	/* get settings */
	for (i = 0, rv = 0; i < LENGTH(rs); i++) {
		if (strcmp(var, rs[i].name))
			continue;

		if (rs[i].s) {
			if (rs[i].s->set(&rs[i], val))
				errx(1, "invalid value for %s: %s", var, val);
			rv = 1;
			break;
		} else
			switch (rs[i].type) {
			case XT_S_INT:
				p = rs[i].ival;
				*p = atoi(val);
				rv = 1;
				break;
			case XT_S_STR:
				s = rs[i].sval;
				if (s == NULL)
					errx(1, "invalid sval for %s",
					    rs[i].name);
				if (*s)
					g_free(*s);
				*s = g_strdup(val);
				rv = 1;
				break;
			case XT_S_FLOAT:
				f = rs[i].fval;
				*f = atof(val);
				rv = 1;
				break;
			case XT_S_INVALID:
			default:
				errx(1, "invalid type for %s", var);
			}
		break;
	}
	return (rv);
}

#define WS	"\n= \t"
void
config_parse(char *filename, int runtime)
{
	FILE			*config, *f;
	char			*line, *cp, *var, *val;
	size_t			len, lineno = 0;
	int			handled;
	char			file[PATH_MAX];
	struct stat		sb;

	DNPRINTF(XT_D_CONFIG, "config_parse: filename %s\n", filename);

	if (filename == NULL)
		return;

	if (runtime && runtime_settings[0] != '\0') {
		snprintf(file, sizeof file, "%s" PS "%s",
		    work_dir, runtime_settings);
		if (stat(file, &sb)) {
			warnx("runtime file doesn't exist, creating it");
			if ((f = fopen(file, "w")) == NULL)
				err(1, "runtime");
			fprintf(f, "# AUTO GENERATED, DO NOT EDIT\n");
			fclose(f);
		}
	} else
		strlcpy(file, filename, sizeof file);

	if ((config = fopen(file, "r")) == NULL) {
		warn("config_parse: cannot open %s", filename);
		return;
	}

	for (;;) {
		if ((line = fparseln(config, &len, &lineno, NULL, 0)) == NULL)
			if (feof(config) || ferror(config))
				break;

		cp = line;
		cp += (long)strspn(cp, WS);
		if (cp[0] == '\0') {
			/* empty line */
			free(line);
			continue;
		}

		if ((var = strsep(&cp, WS)) == NULL || cp == NULL)
			startpage_add("invalid configuration file entry: %s",
			    line);
		else {
			cp += (long)strspn(cp, WS);

			if ((val = strsep(&cp, "\0")) == NULL)
				break;

			DNPRINTF(XT_D_CONFIG, "config_parse: %s=%s\n",
			    var, val);
			handled = settings_add(var, val);

			if (handled == 0)
				startpage_add("invalid configuration file entry"
				    ": %s=%s", var, val);
		}

		free(line);
	}

	fclose(config);
}

struct settings_args {
	char		**body;
	int		i;
};

void
print_setting(struct settings *s, char *val, void *cb_args)
{
	char			*tmp, *color;
	struct settings_args	*sa = cb_args;

	if (sa == NULL)
		return;

	if (s->flags & XT_SF_RUNTIME)
		color = "#22cc22";
	else
		color = "#cccccc";

	tmp = *sa->body;
	*sa->body = g_strdup_printf(
	    "%s\n<tr>"
	    "<td style='background-color: %s; width: 10%%;word-break:break-all'>%s</td>"
	    "<td style='background-color: %s; width: 20%%;word-break:break-all'>%s</td>",
	    *sa->body,
	    color,
	    s->name,
	    color,
	    val
	    );
	g_free(tmp);
	sa->i++;
}

int
set_show(struct tab *t, struct karg *args)
{
	char			*body, *page, *tmp;
	int			i = 1;
	struct settings_args	sa;

	bzero(&sa, sizeof sa);
	sa.body = &body;

	/* body */
	body = g_strdup_printf("<div align='center'><table><tr>"
	    "<th align='left'>Setting</th>"
	    "<th align='left'>Value</th></tr>\n");

	settings_walk(print_setting, &sa);
	i = sa.i;

	/* small message if there are none */
	if (i == 1) {
		tmp = body;
		body = g_strdup_printf("%s\n<tr><td style='text-align:center'"
		    "colspan='2'>No settings</td></tr>\n", body);
		g_free(tmp);
	}

	tmp = body;
	body = g_strdup_printf("%s</table></div>", body);
	g_free(tmp);

	page = get_html_page("Settings", body, "", 0);

	g_free(body);

	load_webkit_string(t, page, XT_URI_ABOUT_SET);

	g_free(page);

	return (XT_CB_PASSTHROUGH);
}

int
set(struct tab *t, struct karg *args)
{
	char			*p, *val;
	int			i;

	if (args == NULL || args->s == NULL)
		return (set_show(t, args));

	/* strip spaces */
	p = g_strstrip(args->s);

	if (strlen(p) == 0)
		return (set_show(t, args));

	/* we got some sort of string */
	val = g_strrstr(p, "=");
	if (val) {
		*val++ = '\0';
		val = g_strchomp(val);
		p = g_strchomp(p);

		for (i = 0; i < get_settings_size(); i++) {
			if (strcmp(rs[i].name, p))
				continue;

			if (rs[i].activate) {
				if (rs[i].activate(val))
					show_oops(t, "%s invalid value %s",
					    p, val);
				else
					show_oops(t, ":set %s = %s", p, val);
				goto done;
			} else {
				show_oops(t, "not a runtime option: %s", p);
				goto done;
			}
		}
		show_oops(t, "unknown option: %s", p);
	} else {
		p = g_strchomp(p);

		for (i = 0; i < get_settings_size(); i++) {
			if (strcmp(rs[i].name, p))
				continue;

			/* XXX this could use some cleanup */
			switch (rs[i].type) {
			case XT_S_INT:
				if (rs[i].ival)
					show_oops(t, "%s = %d",
					    rs[i].name, *rs[i].ival);
				else if (rs[i].s && rs[i].s->get)
					show_oops(t, "%s = %s",
					    rs[i].name,
					    rs[i].s->get(&rs[i]));
				else if (rs[i].s && rs[i].s->get == NULL)
					show_oops(t, "%s = ...", rs[i].name);
				else
					show_oops(t, "%s = ", rs[i].name);
				break;
			case XT_S_FLOAT:
				if (rs[i].fval)
					show_oops(t, "%s = %f",
					    rs[i].name, *rs[i].fval);
				else if (rs[i].s && rs[i].s->get)
					show_oops(t, "%s = %s",
					    rs[i].name,
					    rs[i].s->get(&rs[i]));
				else if (rs[i].s && rs[i].s->get == NULL)
					show_oops(t, "%s = ...", rs[i].name);
				else
					show_oops(t, "%s = ", rs[i].name);
				break;
			case XT_S_STR:
				if (rs[i].sval && *rs[i].sval)
					show_oops(t, "%s = %s",
					    rs[i].name, *rs[i].sval);
				else if (rs[i].s && rs[i].s->get)
					show_oops(t, "%s = %s",
					    rs[i].name,
					    rs[i].s->get(&rs[i]));
				else if (rs[i].s && rs[i].s->get == NULL)
					show_oops(t, "%s = ...", rs[i].name);
				else
					show_oops(t, "%s = ", rs[i].name);
				break;
			default:
				show_oops(t, "unknown type for %s", rs[i].name);
				goto done;
			}

			goto done;
		}
		show_oops(t, "unknown option: %s", p);
	}
done:
	return (XT_CB_PASSTHROUGH);
}