Edited settings_init() to use read_file_by_line()
[uzbl-mobile] / uzbl.c
1 /* -*- c-basic-offset: 4; -*- */
2 // Original code taken from the example webkit-gtk+ application. see notice below.
3 // Modified code is licensed under the GPL 3.  See LICENSE file.
4
5
6 /*
7  * Copyright (C) 2006, 2007 Apple Inc.
8  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32
33 #define LENGTH(x) (sizeof x / sizeof x[0])
34 #define MAX_BINDINGS 256
35
36 #include <gtk/gtk.h>
37 #include <gdk/gdkx.h>
38 #include <gdk/gdkkeysyms.h>
39 #include <sys/socket.h>
40 #include <sys/stat.h>
41 #include <sys/types.h>
42 #include <sys/un.h>
43 #include <sys/utsname.h>
44 #include <webkit/webkit.h>
45 #include <stdio.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include <stdlib.h>
49 #include <errno.h>
50 #include <string.h>
51 #include <fcntl.h>
52 #include <sys/socket.h>
53 #include <sys/un.h>
54 #include <libsoup/soup.h>
55 #include <signal.h>
56 #include "uzbl.h"
57
58
59 static Uzbl uzbl;
60
61 /* define names and pointers to all config specific variables */
62 typedef const struct {
63     void **ptr;
64     int type;
65     void (*func)(void);
66 } uzbl_cmdprop;
67
68 enum {TYPE_INT, TYPE_STRING};
69
70 const struct {
71     char *name;
72     uzbl_cmdprop cp;
73 } var_name_to_ptr[] = {
74 /*    variable name           pointer to variable in code                         variable type      callback function  */
75 /*  ------------------------------------------------------------------------------------------------------------------- */
76     { "uri",                {.ptr = (void *)&uzbl.state.uri,                   .type = TYPE_STRING, .func = cmd_load_uri}},
77     { "status_message",     {.ptr = (void *)&uzbl.gui.sbar.msg,                .type = TYPE_STRING, .func = update_title}},
78     { "show_status",        {.ptr = (void *)&uzbl.behave.show_status,          .type = TYPE_INT,    .func = cmd_set_status}},
79     { "status_top",         {.ptr = (void *)&uzbl.behave.status_top,           .type = TYPE_INT,    .func = move_statusbar}},
80     { "status_format",      {.ptr = (void *)&uzbl.behave.status_format,        .type = TYPE_STRING, .func = update_title}},
81     { "status_background",  {.ptr = (void *)&uzbl.behave.status_background,    .type = TYPE_STRING, .func = update_title}},
82     { "title_format_long",  {.ptr = (void *)&uzbl.behave.title_format_long,    .type = TYPE_STRING, .func = update_title}},
83     { "title_format_short", {.ptr = (void *)&uzbl.behave.title_format_short,   .type = TYPE_STRING, .func = update_title}},
84     { "insert_mode",        {.ptr = (void *)&uzbl.behave.insert_mode,          .type = TYPE_INT,    .func = NULL}},
85     { "always_insert_mode", {.ptr = (void *)&uzbl.behave.always_insert_mode,   .type = TYPE_INT,    .func = cmd_always_insert_mode}},
86     { "reset_command_mode", {.ptr = (void *)&uzbl.behave.reset_command_mode,   .type = TYPE_INT,    .func = NULL}},
87     { "modkey"     ,        {.ptr = (void *)&uzbl.behave.modkey,               .type = TYPE_STRING, .func = cmd_modkey}},
88     { "load_finish_handler",{.ptr = (void *)&uzbl.behave.load_finish_handler,  .type = TYPE_STRING, .func = NULL}},
89     { "load_start_handler", {.ptr = (void *)&uzbl.behave.load_start_handler,   .type = TYPE_STRING, .func = NULL}},
90     { "load_commit_handler",{.ptr = (void *)&uzbl.behave.load_commit_handler,  .type = TYPE_STRING, .func = NULL}},
91     { "history_handler",    {.ptr = (void *)&uzbl.behave.history_handler,      .type = TYPE_STRING, .func = NULL}},
92     { "download_handler",   {.ptr = (void *)&uzbl.behave.download_handler,     .type = TYPE_STRING, .func = NULL}},
93     { "cookie_handler",     {.ptr = (void *)&uzbl.behave.cookie_handler,       .type = TYPE_STRING, .func = NULL}},
94     { "fifo_dir",           {.ptr = (void *)&uzbl.behave.fifo_dir,             .type = TYPE_STRING, .func = cmd_fifo_dir}},
95     { "socket_dir",         {.ptr = (void *)&uzbl.behave.socket_dir,           .type = TYPE_STRING, .func = cmd_socket_dir}},
96     { "http_debug",         {.ptr = (void *)&uzbl.behave.http_debug,           .type = TYPE_INT,    .func = cmd_http_debug}},
97     { "default_font_size",  {.ptr = (void *)&uzbl.behave.default_font_size,    .type = TYPE_INT,    .func = cmd_default_font_size}},
98     { "minimum_font_size",  {.ptr = (void *)&uzbl.behave.minimum_font_size,    .type = TYPE_INT,    .func = cmd_minimum_font_size}},
99     { "shell_cmd",          {.ptr = (void *)&uzbl.behave.shell_cmd,            .type = TYPE_STRING, .func = NULL}},
100     { "proxy_url",          {.ptr = (void *)&uzbl.net.proxy_url,               .type = TYPE_STRING, .func = set_proxy_url}},
101     { "max_conns",          {.ptr = (void *)&uzbl.net.max_conns,               .type = TYPE_INT,    .func = cmd_max_conns}},
102     { "max_conns_host",     {.ptr = (void *)&uzbl.net.max_conns_host,          .type = TYPE_INT,    .func = cmd_max_conns_host}},
103     { "useragent",          {.ptr = (void *)&uzbl.net.useragent,               .type = TYPE_STRING, .func = cmd_useragent}},
104     { NULL,                 {.ptr = NULL,                                      .type = TYPE_INT,    .func = NULL}}
105 }, *n2v_p = var_name_to_ptr;
106
107 const struct {
108     char *key;
109     guint mask;
110 } modkeys[] = {
111     { "SHIFT",   GDK_SHIFT_MASK   }, // shift
112     { "LOCK",    GDK_LOCK_MASK    }, // capslock or shiftlock, depending on xserver's modmappings
113     { "CONTROL", GDK_CONTROL_MASK }, // control
114     { "MOD1",    GDK_MOD1_MASK    }, // 4th mod - normally alt but depends on modmappings
115     { "MOD2",    GDK_MOD2_MASK    }, // 5th mod
116     { "MOD3",    GDK_MOD3_MASK    }, // 6th mod
117     { "MOD4",    GDK_MOD4_MASK    }, // 7th mod
118     { "MOD5",    GDK_MOD5_MASK    }, // 8th mod
119     { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
120     { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
121     { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
122     { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
123     { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
124     { "SUPER",   GDK_SUPER_MASK   }, // super (since 2.10)
125     { "HYPER",   GDK_HYPER_MASK   }, // hyper (since 2.10)
126     { "META",    GDK_META_MASK    }, // meta (since 2.10)
127     { NULL,      0                }
128 };
129
130
131 /* construct a hash from the var_name_to_ptr array for quick access */
132 static void
133 make_var_to_name_hash() {
134     uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
135     while(n2v_p->name) {
136         g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
137         n2v_p++;
138     }
139 }
140
141 /* commandline arguments (set initial values for the state variables) */
142 static GOptionEntry entries[] =
143 {
144     { "uri",     'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,           "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
145     { "verbose", 'v', 0, G_OPTION_ARG_NONE,   &uzbl.state.verbose,       "Whether to print all messages or just errors.", NULL },
146     { "name",    'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name, "Name of the current instance (defaults to Xorg window id)", "NAME" },
147     { "config",  'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,   "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
148     { NULL,      0, 0, 0, NULL, NULL, NULL }
149 };
150
151 typedef void (*Command)(WebKitWebView*, GArray *argv);
152
153 /* --- UTILITY FUNCTIONS --- */
154
155 char *
156 itos(int val) {
157     char tmp[20];
158
159     snprintf(tmp, sizeof(tmp), "%i", val);
160     return g_strdup(tmp);
161 }
162
163 static gchar*
164 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
165
166 static char *
167 str_replace (const char* search, const char* replace, const char* string) {
168     gchar **buf;
169     char *ret;
170
171     buf = g_strsplit (string, search, -1);
172     ret = g_strjoinv (replace, buf);
173     g_strfreev(buf);
174
175     return ret;
176 }
177
178 static GArray*
179 read_file_by_line (gchar *path) {
180     GIOChannel *chan = NULL;
181     gchar *readbuf = NULL;
182     gsize len;
183     GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
184     int i = 0;
185     
186     chan = g_io_channel_new_file(path, "r", NULL);
187     
188     if (chan) {
189         while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
190             const gchar* val = g_strdup (readbuf);
191             g_array_append_val (lines, val);
192             g_free (readbuf);
193             i ++;
194         }
195         
196         g_io_channel_unref (chan);
197     } else {
198         fprintf(stderr, "File '%s' not be read.\n", path);
199     }
200     
201     return lines;
202 }
203
204 static
205 gchar* parseenv (const char* string) {
206     extern char** environ;
207     gchar* newstring = g_strdup (string);
208     int i = 0;
209
210     while (environ[i] != NULL) {
211         gchar** env = g_strsplit (environ[i], "=", 0);
212         gchar* envname = malloc (strlen (env[0]) + 1);
213
214         strcat (envname, "$");
215         strcat (envname, env[0]);
216
217         newstring = str_replace(envname, env[1], newstring);
218
219         g_free (envname);
220         //g_strfreev (env); - This still breaks uzbl, but shouldn't. The mystery thickens...
221         i ++;
222     }
223
224     return newstring;
225 }
226
227 static sigfunc*
228 setup_signal(int signr, sigfunc *shandler) {
229     struct sigaction nh, oh;
230
231     nh.sa_handler = shandler;
232     sigemptyset(&nh.sa_mask);
233     nh.sa_flags = 0;
234
235     if(sigaction(signr, &nh, &oh) < 0)
236         return SIG_ERR;
237
238     return NULL;
239 }
240
241 static void
242 clean_up(void) {
243     if (uzbl.behave.fifo_dir)
244         unlink (uzbl.comm.fifo_path);
245     if (uzbl.behave.socket_dir)
246         unlink (uzbl.comm.socket_path);
247
248     g_free(uzbl.state.executable_path);
249     g_string_free(uzbl.state.keycmd, TRUE);
250     g_hash_table_destroy(uzbl.bindings);
251     g_hash_table_destroy(uzbl.behave.commands);
252 }
253
254
255 /* --- SIGNAL HANDLER --- */
256
257 static void
258 catch_sigterm(int s) {
259     (void) s;
260     clean_up();
261 }
262
263 static void
264 catch_sigint(int s) {
265     (void) s;
266     clean_up();
267     exit(EXIT_SUCCESS);
268 }
269
270 /* --- CALLBACKS --- */
271
272 static gboolean
273 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
274     (void) web_view;
275     (void) frame;
276     (void) navigation_action;
277     (void) policy_decision;
278     (void) user_data;
279     const gchar* uri = webkit_network_request_get_uri (request);
280     if (uzbl.state.verbose)
281         printf("New window requested -> %s \n", uri);
282     new_window_load_uri(uri);
283     return (FALSE);
284 }
285
286 WebKitWebView*
287 create_web_view_cb (WebKitWebView  *web_view, WebKitWebFrame *frame, gpointer user_data) {
288     (void) web_view;
289     (void) frame;
290     (void) user_data;
291     if (uzbl.state.selected_url != NULL) {
292         if (uzbl.state.verbose)
293             printf("\nNew web view -> %s\n",uzbl.state.selected_url);
294         new_window_load_uri(uzbl.state.selected_url);
295     } else {
296         if (uzbl.state.verbose)
297             printf("New web view -> %s\n","Nothing to open, exiting");
298     }
299     return (NULL);
300 }
301
302 static gboolean
303 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
304     (void) web_view;
305     (void) user_data;
306     if (uzbl.behave.download_handler) {
307         const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
308         if (uzbl.state.verbose)
309             printf("Download -> %s\n",uri);
310         /* if urls not escaped, we may have to escape and quote uri before this call */
311         run_handler(uzbl.behave.download_handler, uri);
312     }
313     return (FALSE);
314 }
315
316 /* scroll a bar in a given direction */
317 static void
318 scroll (GtkAdjustment* bar, GArray *argv) {
319     gdouble amount;
320     gchar *end;
321
322     amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
323     if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
324     gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
325 }
326
327 static void scroll_begin(WebKitWebView* page, GArray *argv) {
328     (void) page; (void) argv;
329     gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
330 }
331
332 static void scroll_end(WebKitWebView* page, GArray *argv) {
333     (void) page; (void) argv;
334     gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
335                               gtk_adjustment_get_page_size(uzbl.gui.bar_v));
336 }
337
338 static void scroll_vert(WebKitWebView* page, GArray *argv) {
339     (void) page;
340     scroll(uzbl.gui.bar_v, argv);
341 }
342
343 static void scroll_horz(WebKitWebView* page, GArray *argv) {
344     (void) page;
345     scroll(uzbl.gui.bar_h, argv);
346 }
347
348 static void
349 cmd_set_status() {
350     if (!uzbl.behave.show_status) {
351         gtk_widget_hide(uzbl.gui.mainbar);
352     } else {
353         gtk_widget_show(uzbl.gui.mainbar);
354     }
355     update_title();
356 }
357
358 static void
359 toggle_status_cb (WebKitWebView* page, GArray *argv) {
360     (void)page;
361     (void)argv;
362
363     if (uzbl.behave.show_status) {
364         gtk_widget_hide(uzbl.gui.mainbar);
365     } else {
366         gtk_widget_show(uzbl.gui.mainbar);
367     }
368     uzbl.behave.show_status = !uzbl.behave.show_status;
369     update_title();
370 }
371
372 static void
373 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
374     (void) page;
375     (void) title;
376     (void) data;
377     //Set selected_url state variable
378     g_free(uzbl.state.selected_url);
379     uzbl.state.selected_url = NULL;
380     if (link) {
381         uzbl.state.selected_url = g_strdup(link);
382     }
383     update_title();
384 }
385
386 static void
387 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
388     (void) web_view;
389     (void) web_frame;
390     (void) data;
391     if (uzbl.gui.main_title)
392         g_free (uzbl.gui.main_title);
393     uzbl.gui.main_title = g_strdup (title);
394     update_title();
395 }
396
397 static void
398 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
399     (void) page;
400     (void) data;
401     uzbl.gui.sbar.load_progress = progress;
402     update_title();
403 }
404
405 static void
406 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
407     (void) page;
408     (void) frame;
409     (void) data;
410     if (uzbl.behave.load_finish_handler)
411         run_handler(uzbl.behave.load_finish_handler, "");
412 }
413
414 static void
415 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
416     (void) page;
417     (void) frame;
418     (void) data;
419     if (uzbl.behave.load_start_handler)
420         run_handler(uzbl.behave.load_start_handler, "");
421 }
422
423 static void
424 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
425     (void) page;
426     (void) data;
427     free (uzbl.state.uri);
428     GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
429     uzbl.state.uri = g_string_free (newuri, FALSE);
430     if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
431         uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
432         update_title();
433     }
434     g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
435     if (uzbl.behave.load_commit_handler)
436         run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
437 }
438
439 static void
440 destroy_cb (GtkWidget* widget, gpointer data) {
441     (void) widget;
442     (void) data;
443     gtk_main_quit ();
444 }
445
446 static void
447 log_history_cb () {
448    if (uzbl.behave.history_handler) {
449        time_t rawtime;
450        struct tm * timeinfo;
451        char date [80];
452        time ( &rawtime );
453        timeinfo = localtime ( &rawtime );
454        strftime (date, 80, " \"%Y-%m-%d %H:%M:%S\"", timeinfo);
455        run_handler(uzbl.behave.history_handler, date);
456    }
457 }
458
459
460 /* VIEW funcs (little webkit wrappers) */
461 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv){(void)argv; webkit_web_view_##name(page);}
462 VIEWFUNC(reload)
463 VIEWFUNC(reload_bypass_cache)
464 VIEWFUNC(stop_loading)
465 VIEWFUNC(zoom_in)
466 VIEWFUNC(zoom_out)
467 VIEWFUNC(go_back)
468 VIEWFUNC(go_forward)
469 #undef VIEWFUNC
470
471 /* -- command to callback/function map for things we cannot attach to any signals */
472 // TODO: reload
473 static struct {char *name; Command command[2];} cmdlist[] =
474 {   /* key                   function      no_split      */
475     { "back",               {view_go_back, 0}              },
476     { "forward",            {view_go_forward, 0}           },
477     { "scroll_vert",        {scroll_vert, 0}               },
478     { "scroll_horz",        {scroll_horz, 0}               },
479     { "scroll_begin",       {scroll_begin, 0}              },
480     { "scroll_end",         {scroll_end, 0}                },
481     { "reload",             {view_reload, 0},              },
482     { "reload_ign_cache",   {view_reload_bypass_cache, 0}  },
483     { "stop",               {view_stop_loading, 0},        },
484     { "zoom_in",            {view_zoom_in, 0},             }, //Can crash (when max zoom reached?).
485     { "zoom_out",           {view_zoom_out, 0},            },
486     { "uri",                {load_uri, NOSPLIT}            },
487     { "js",                 {run_js, NOSPLIT}              },
488     { "script",             {run_external_js, 0}           },
489     { "toggle_status",      {toggle_status_cb, 0}          },
490     { "spawn",              {spawn, 0}                     },
491     { "sh",                 {spawn_sh, 0}                  },
492     { "exit",               {close_uzbl, 0}                },
493     { "search",             {search_forward_text, NOSPLIT} },
494     { "search_reverse",     {search_reverse_text, NOSPLIT} },
495     { "toggle_insert_mode", {toggle_insert_mode, 0}        },
496     { "runcmd",             {runcmd, NOSPLIT}              }
497 };
498
499 static void
500 commands_hash(void)
501 {
502     unsigned int i;
503     uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
504
505     for (i = 0; i < LENGTH(cmdlist); i++)
506         g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
507 }
508
509 /* -- CORE FUNCTIONS -- */
510
511 void
512 free_action(gpointer act) {
513     Action *action = (Action*)act;
514     g_free(action->name);
515     if (action->param)
516         g_free(action->param);
517     g_free(action);
518 }
519
520 Action*
521 new_action(const gchar *name, const gchar *param) {
522     Action *action = g_new(Action, 1);
523
524     action->name = g_strdup(name);
525     if (param)
526         action->param = g_strdup(param);
527     else
528         action->param = NULL;
529
530     return action;
531 }
532
533 static bool
534 file_exists (const char * filename) {
535     return (access(filename, F_OK) == 0);
536 }
537
538 static void
539 toggle_insert_mode(WebKitWebView *page, GArray *argv) {
540     (void)page;
541     (void)argv;
542
543     if (argv_idx(argv, 0)) {
544         if (strcmp (argv_idx(argv, 0), "0") == 0) {
545             uzbl.behave.insert_mode = FALSE;
546         } else {
547             uzbl.behave.insert_mode = TRUE;
548         }
549     } else {
550         uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
551     }
552
553     update_title();
554 }
555
556 static void
557 load_uri (WebKitWebView *web_view, GArray *argv) {
558     if (argv_idx(argv, 0)) {
559         GString* newuri = g_string_new (argv_idx(argv, 0));
560         if (g_strrstr (argv_idx(argv, 0), "://") == NULL)
561             g_string_prepend (newuri, "http://");
562         /* if we do handle cookies, ask our handler for them */
563         webkit_web_view_load_uri (web_view, newuri->str);
564         g_string_free (newuri, TRUE);
565     }
566 }
567
568 static void
569 run_js (WebKitWebView * web_view, GArray *argv) {
570     if (argv_idx(argv, 0))
571         webkit_web_view_execute_script (web_view, argv_idx(argv, 0));
572 }
573
574 static void
575 run_external_js (WebKitWebView * web_view, GArray *argv) {
576     if (argv_idx(argv, 0)) {
577         GArray* lines = read_file_by_line (argv_idx (argv, 0));
578         gchar*  js = NULL;
579         int i = 0;
580         gchar* line;
581
582         while ((line = g_array_index(lines, gchar*, i))) {
583             if (js == NULL) {
584                 js = g_strdup (line);
585             } else {
586                 gchar* newjs = g_strconcat (js, line, NULL);
587                 js = newjs;
588             }
589             i ++;
590         }
591         
592         if (uzbl.state.verbose)
593             printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
594
595         if (argv_idx (argv, 1)) {
596             gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
597             g_free (js);
598             js = newjs;
599         }
600         webkit_web_view_execute_script (web_view, js);
601         g_free (js);
602         g_array_free (lines, TRUE);
603     }
604 }
605
606 static void
607 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
608     if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0'))
609         uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
610
611     if (uzbl.state.searchtx != NULL) {
612         if (uzbl.state.verbose)
613             printf ("Searching: %s\n", uzbl.state.searchtx);
614
615         if (g_strcmp0 (uzbl.state.searchtx, uzbl.state.searchold) != 0) {
616             webkit_web_view_unmark_text_matches (page);
617             webkit_web_view_mark_text_matches (page, uzbl.state.searchtx, FALSE, 0);
618
619             if (uzbl.state.searchold != NULL)
620                 g_free (uzbl.state.searchold);
621
622             uzbl.state.searchold = g_strdup (uzbl.state.searchtx);
623         }
624
625         webkit_web_view_set_highlight_text_matches (page, TRUE);
626         webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
627         g_free(uzbl.state.searchtx);
628         uzbl.state.searchtx = NULL;
629     }
630 }
631
632 static void
633 search_forward_text (WebKitWebView *page, GArray *argv) {
634     search_text(page, argv, TRUE);
635 }
636
637 static void
638 search_reverse_text (WebKitWebView *page, GArray *argv) {
639     search_text(page, argv, FALSE);
640 }
641
642 static void
643 new_window_load_uri (const gchar * uri) {
644     GString* to_execute = g_string_new ("");
645     g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
646     int i;
647     for (i = 0; entries[i].long_name != NULL; i++) {
648         if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
649             gchar** str = (gchar**)entries[i].arg_data;
650             if (*str!=NULL) {
651                 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
652             }
653         }
654     }
655     if (uzbl.state.verbose)
656         printf("\n%s\n", to_execute->str);
657     g_spawn_command_line_async (to_execute->str, NULL);
658     g_string_free (to_execute, TRUE);
659 }
660
661 static void
662 close_uzbl (WebKitWebView *page, GArray *argv) {
663     (void)page;
664     (void)argv;
665     gtk_main_quit ();
666 }
667
668 /* --Statusbar functions-- */
669 static char*
670 build_progressbar_ascii(int percent) {
671    int width=10;
672    int i;
673    double l;
674    GString *bar = g_string_new("");
675
676    l = (double)percent*((double)width/100.);
677    l = (int)(l+.5)>=(int)l ? l+.5 : l;
678
679    for(i=0; i<(int)l; i++)
680        g_string_append(bar, "=");
681
682    for(; i<width; i++)
683        g_string_append(bar, "·");
684
685    return g_string_free(bar, FALSE);
686 }
687
688 static void
689 setup_scanner() {
690      const GScannerConfig scan_config = {
691              (
692               "\t\r\n"
693              )            /* cset_skip_characters */,
694              (
695               G_CSET_a_2_z
696               "_#"
697               G_CSET_A_2_Z
698              )            /* cset_identifier_first */,
699              (
700               G_CSET_a_2_z
701               "_0123456789"
702               G_CSET_A_2_Z
703               G_CSET_LATINS
704               G_CSET_LATINC
705              )            /* cset_identifier_nth */,
706              ( "" )    /* cpair_comment_single */,
707
708              TRUE         /* case_sensitive */,
709
710              FALSE        /* skip_comment_multi */,
711              FALSE        /* skip_comment_single */,
712              FALSE        /* scan_comment_multi */,
713              TRUE         /* scan_identifier */,
714              TRUE         /* scan_identifier_1char */,
715              FALSE        /* scan_identifier_NULL */,
716              TRUE         /* scan_symbols */,
717              FALSE        /* scan_binary */,
718              FALSE        /* scan_octal */,
719              FALSE        /* scan_float */,
720              FALSE        /* scan_hex */,
721              FALSE        /* scan_hex_dollar */,
722              FALSE        /* scan_string_sq */,
723              FALSE        /* scan_string_dq */,
724              TRUE         /* numbers_2_int */,
725              FALSE        /* int_2_float */,
726              FALSE        /* identifier_2_string */,
727              FALSE        /* char_2_token */,
728              FALSE        /* symbol_2_token */,
729              TRUE         /* scope_0_fallback */,
730              FALSE,
731              TRUE
732      };
733
734      uzbl.scan = g_scanner_new(&scan_config);
735      while(symp->symbol_name) {
736          g_scanner_scope_add_symbol(uzbl.scan, 0,
737                          symp->symbol_name,
738                          GINT_TO_POINTER(symp->symbol_token));
739          symp++;
740      }
741 }
742
743 static gchar *
744 expand_template(const char *template) {
745      if(!template) return NULL;
746
747      GTokenType token = G_TOKEN_NONE;
748      GString *ret = g_string_new("");
749      char *buf=NULL;
750      int sym;
751
752      g_scanner_input_text(uzbl.scan, template, strlen(template));
753      while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
754          token = g_scanner_get_next_token(uzbl.scan);
755
756          if(token == G_TOKEN_SYMBOL) {
757              sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
758              switch(sym) {
759                  case SYM_URI:
760                      buf = uzbl.state.uri?
761                          g_markup_printf_escaped("%s", uzbl.state.uri) :
762                          g_strdup("");
763                      g_string_append(ret, buf);
764                      free(buf);
765                      break;
766                  case SYM_LOADPRGS:
767                      buf = itos(uzbl.gui.sbar.load_progress);
768                      g_string_append(ret, buf);
769                      free(buf);
770                      break;
771                  case SYM_LOADPRGSBAR:
772                      buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
773                      g_string_append(ret, buf);
774                      g_free(buf);
775                      break;
776                  case SYM_TITLE:
777                      buf = uzbl.gui.main_title?
778                          g_markup_printf_escaped("%s", uzbl.gui.main_title) :
779                          g_strdup("");
780                      g_string_append(ret, buf);
781                      free(buf);
782                      break;
783                  case SYM_SELECTED_URI:
784                      buf = uzbl.state.selected_url?
785                          g_markup_printf_escaped("%s", uzbl.state.selected_url) :
786                          g_strdup("");
787                      g_string_append(ret, buf);
788                      free(buf);
789                     break;
790                  case SYM_NAME:
791                      buf = itos(uzbl.xwin);
792                      g_string_append(ret,
793                          uzbl.state.instance_name?uzbl.state.instance_name:buf);
794                      free(buf);
795                      break;
796                  case SYM_KEYCMD:
797                      buf = uzbl.state.keycmd->str?
798                          g_markup_printf_escaped("%s", uzbl.state.keycmd->str) :
799                          g_strdup("");
800                      g_string_append(ret, buf);
801                      free(buf);
802                      break;
803                  case SYM_MODE:
804                      g_string_append(ret,
805                          uzbl.behave.insert_mode?"[I]":"[C]");
806                      break;
807                  case SYM_MSG:
808                      g_string_append(ret,
809                          uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
810                      break;
811                      /* useragent syms */
812                  case SYM_WK_MAJ:
813                      buf = itos(WEBKIT_MAJOR_VERSION);
814                      g_string_append(ret, buf);
815                      free(buf);
816                      break;
817                  case SYM_WK_MIN:
818                      buf = itos(WEBKIT_MINOR_VERSION);
819                      g_string_append(ret, buf);
820                      free(buf);
821                      break;
822                  case SYM_WK_MIC:
823                      buf = itos(WEBKIT_MICRO_VERSION);
824                      g_string_append(ret, buf);
825                      free(buf);
826                      break;
827                  case SYM_SYSNAME:
828                      g_string_append(ret, uzbl.state.unameinfo.sysname);
829                      break;
830                  case SYM_NODENAME:
831                      g_string_append(ret, uzbl.state.unameinfo.nodename);
832                      break;
833                  case SYM_KERNREL:
834                      g_string_append(ret, uzbl.state.unameinfo.release);
835                      break;
836                  case SYM_KERNVER:
837                      g_string_append(ret, uzbl.state.unameinfo.version);
838                      break;
839                  case SYM_ARCHSYS:
840                      g_string_append(ret, uzbl.state.unameinfo.machine);
841                      break;
842                  case SYM_ARCHUZBL:
843                      g_string_append(ret, ARCH);
844                      break;
845 #ifdef _GNU_SOURCE
846                  case SYM_DOMAINNAME:
847                      g_string_append(ret, uzbl.state.unameinfo.domainname);
848                      break;
849 #endif
850                  case SYM_COMMIT:
851                      g_string_append(ret, COMMIT);
852                      break;
853                  default:
854                      break;
855              }
856          }
857          else if(token == G_TOKEN_INT) {
858              buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
859              g_string_append(ret, buf);
860              free(buf);
861          }
862          else if(token == G_TOKEN_IDENTIFIER) {
863              g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
864          }
865          else if(token == G_TOKEN_CHAR) {
866              g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
867          }
868      }
869
870      return g_string_free(ret, FALSE);
871 }
872 /* --End Statusbar functions-- */
873
874 static void
875 sharg_append(GArray *a, const gchar *str) {
876     const gchar *s = (str ? str : "");
877     g_array_append_val(a, s);
878 }
879
880 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
881 static gboolean
882 run_command (const gchar *command, const guint npre, const gchar **args,
883              const gboolean sync, char **stdout) {
884    //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
885     GError *err = NULL;
886     
887     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
888     gchar *pid = itos(getpid());
889     gchar *xwin = itos(uzbl.xwin);
890     guint i;
891     sharg_append(a, command);
892     for (i = 0; i < npre; i++) /* add n args before the default vars */
893         sharg_append(a, args[i]);
894     sharg_append(a, uzbl.state.config_file);
895     sharg_append(a, pid);
896     sharg_append(a, xwin);
897     sharg_append(a, uzbl.comm.fifo_path);
898     sharg_append(a, uzbl.comm.socket_path);
899     sharg_append(a, uzbl.state.uri);
900     sharg_append(a, uzbl.gui.main_title);
901
902     for (i = npre; i < g_strv_length((gchar**)args); i++)
903         sharg_append(a, args[i]);
904     gboolean result;
905     if (sync) result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
906                                     NULL, NULL, stdout, NULL, NULL, &err);
907     else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
908                                 NULL, NULL, NULL, &err);
909
910     if (uzbl.state.verbose) {
911         GString *s = g_string_new("spawned:");
912         for (i = 0; i < (a->len); i++) {
913             gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
914             g_string_append_printf(s, " %s", qarg);
915             g_free (qarg);
916         }
917         g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
918         printf("%s\n", s->str);
919         g_string_free(s, TRUE);
920     }
921     if (err) {
922         g_printerr("error on run_command: %s\n", err->message);
923         g_error_free (err);
924     }
925     g_free (pid);
926     g_free (xwin);
927     g_array_free (a, TRUE);
928     return result;
929 }
930
931 static gchar**
932 split_quoted(const gchar* src, const gboolean unquote) {
933     /* split on unquoted space, return array of strings;
934        remove a layer of quotes and backslashes if unquote */
935     if (!src) return NULL;
936     
937     gboolean dq = FALSE;
938     gboolean sq = FALSE;
939     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
940     GString *s = g_string_new ("");
941     const gchar *p;
942     gchar **ret;
943     gchar *dup;
944     for (p = src; *p != '\0'; p++) {
945         if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
946         else if (*p == '\\') { g_string_append_c(s, *p++);
947                                g_string_append_c(s, *p); }
948         else if ((*p == '"') && unquote && !sq) dq = !dq;
949         else if (*p == '"' && !sq) { g_string_append_c(s, *p);
950                                      dq = !dq; }
951         else if ((*p == '\'') && unquote && !dq) sq = !sq;
952         else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
953                                       sq = ! sq; }
954         else if ((*p == ' ') && !dq && !sq) {
955             dup = g_strdup(s->str);
956             g_array_append_val(a, dup);
957             g_string_truncate(s, 0);
958         } else g_string_append_c(s, *p);
959     }
960     dup = g_strdup(s->str);
961     g_array_append_val(a, dup);
962     ret = (gchar**)a->data;
963     g_array_free (a, FALSE);
964     g_string_free (s, TRUE);
965     return ret;
966 }
967
968 static void
969 spawn(WebKitWebView *web_view, GArray *argv) {
970     (void)web_view;
971     //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
972     if (argv_idx(argv, 0))
973         run_command(argv_idx(argv, 0), 0, (const gchar **) argv->data + sizeof(gchar*), FALSE, NULL);
974 }
975
976 static void
977 spawn_sh(WebKitWebView *web_view, GArray *argv) {
978     (void)web_view;
979     if (!uzbl.behave.shell_cmd) {
980         g_printerr ("spawn_sh: shell_cmd is not set!\n");
981         return;
982     }
983     
984     guint i;
985     gchar *spacer = g_strdup("");
986     g_array_insert_val(argv, 1, spacer);
987     gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
988
989     for (i = 1; i < g_strv_length(cmd); i++)
990         g_array_prepend_val(argv, cmd[i]);
991
992     if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
993     g_free (spacer);
994     g_strfreev (cmd);
995 }
996
997 static void
998 parse_command(const char *cmd, const char *param) {
999     Command *c;
1000
1001     if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1002
1003             guint i;
1004             gchar **par = split_quoted(param, TRUE);
1005             GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1006
1007             if (c[1]) { /* don't split */
1008                 sharg_append(a, param);
1009             } else {
1010                 for (i = 0; i < g_strv_length(par); i++)
1011                     sharg_append(a, par[i]);
1012             }
1013             c[0](uzbl.gui.web_view, a);
1014             g_strfreev (par);
1015             g_array_free (a, TRUE);
1016
1017     } else
1018         g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1019 }
1020
1021 /* command parser */
1022 static void
1023 setup_regex() {
1024     uzbl.comm.get_regex  = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
1025             G_REGEX_OPTIMIZE, 0, NULL);
1026     uzbl.comm.set_regex  = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
1027             G_REGEX_OPTIMIZE, 0, NULL);
1028     uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
1029             G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
1030     uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
1031             G_REGEX_OPTIMIZE, 0, NULL);
1032     uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
1033             G_REGEX_OPTIMIZE, 0, NULL);
1034 }
1035
1036 static gboolean
1037 get_var_value(gchar *name) {
1038     uzbl_cmdprop *c;
1039
1040     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1041         if(c->type == TYPE_STRING)
1042             printf("VAR: %s VALUE: %s\n", name, (char *)*c->ptr);
1043         else if(c->type == TYPE_INT)
1044             printf("VAR: %s VALUE: %d\n", name, (int)*c->ptr);
1045     }
1046     return TRUE;
1047 }
1048
1049 static void
1050 set_proxy_url() {
1051     SoupURI *suri;
1052
1053     if(*uzbl.net.proxy_url == ' '
1054        || uzbl.net.proxy_url == NULL) {
1055         soup_session_remove_feature_by_type(uzbl.net.soup_session,
1056                 (GType) SOUP_SESSION_PROXY_URI);
1057     }
1058     else {
1059         suri = soup_uri_new(uzbl.net.proxy_url);
1060         g_object_set(G_OBJECT(uzbl.net.soup_session),
1061                 SOUP_SESSION_PROXY_URI,
1062                 suri, NULL);
1063         soup_uri_free(suri);
1064     }
1065     return;
1066 }
1067
1068 static void
1069 cmd_load_uri() {
1070     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1071     g_array_append_val (a, uzbl.state.uri);
1072     load_uri(uzbl.gui.web_view, a);
1073     g_array_free (a, TRUE);
1074 }
1075
1076 static void 
1077 cmd_always_insert_mode() {
1078     uzbl.behave.insert_mode =
1079         uzbl.behave.always_insert_mode ?  TRUE : FALSE;
1080     update_title();
1081 }
1082
1083 static void
1084 cmd_max_conns() {
1085     g_object_set(G_OBJECT(uzbl.net.soup_session),
1086             SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1087 }
1088
1089 static void
1090 cmd_max_conns_host() {
1091     g_object_set(G_OBJECT(uzbl.net.soup_session),
1092             SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1093 }
1094
1095 static void
1096 cmd_http_debug() {
1097     soup_session_remove_feature
1098         (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1099     /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1100     /*g_free(uzbl.net.soup_logger);*/
1101
1102     uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1103     soup_session_add_feature(uzbl.net.soup_session,
1104             SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1105 }
1106
1107 static void
1108 cmd_default_font_size() {
1109     WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1110     g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.default_font_size, NULL);
1111 }
1112
1113 static void
1114 cmd_minimum_font_size() {
1115     WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1116     g_object_set (G_OBJECT(ws), "minimum-font-size", uzbl.behave.minimum_font_size, NULL);
1117 }
1118
1119 static void
1120 cmd_fifo_dir() {
1121     char *buf;
1122
1123     buf = init_fifo(uzbl.behave.fifo_dir);
1124     if(uzbl.behave.fifo_dir) 
1125         free(uzbl.behave.fifo_dir);
1126
1127     uzbl.behave.fifo_dir = buf?buf:g_strdup("");
1128 }
1129
1130 static void
1131 cmd_socket_dir() {
1132     char *buf;
1133
1134     buf = init_socket(uzbl.behave.fifo_dir);
1135     if(uzbl.behave.socket_dir) 
1136         free(uzbl.behave.socket_dir);
1137
1138     uzbl.behave.socket_dir = buf?buf:g_strdup("");
1139 }
1140
1141 static void
1142 cmd_modkey() {
1143     int i;
1144     char *buf;
1145
1146     buf = g_utf8_strup(uzbl.behave.modkey, -1);
1147     uzbl.behave.modmask = 0;
1148     
1149     if(uzbl.behave.modkey) 
1150         free(uzbl.behave.modkey);
1151
1152     for (i = 0; modkeys[i].key != NULL; i++) {
1153         if (g_strrstr(buf, modkeys[i].key))
1154             uzbl.behave.modmask |= modkeys[i].mask;
1155     }
1156 }
1157
1158 static void
1159 cmd_useragent() {
1160     char *buf;
1161
1162     buf = set_useragent(uzbl.net.useragent);
1163     if(uzbl.net.useragent) 
1164         free(uzbl.net.useragent);
1165             
1166     uzbl.net.useragent = buf?buf:g_strdup("");
1167 }
1168
1169 static void
1170 move_statusbar() {
1171     gtk_widget_ref(uzbl.gui.scrolled_win);
1172     gtk_widget_ref(uzbl.gui.mainbar);
1173     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1174     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1175
1176     if(uzbl.behave.status_top) {
1177         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1178         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1179     }
1180     else {
1181         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1182         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1183     }
1184     gtk_widget_unref(uzbl.gui.scrolled_win);
1185     gtk_widget_unref(uzbl.gui.mainbar);
1186     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1187     return;
1188 }
1189
1190 static gboolean
1191 set_var_value(gchar *name, gchar *val) {
1192     void *p = NULL;
1193     uzbl_cmdprop *c = NULL;
1194     char *endp = NULL;
1195     char *buf=NULL;
1196
1197     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1198         /* check for the variable type */
1199         if (c->type == TYPE_STRING) {
1200             free(*c->ptr);
1201             *c->ptr = g_strdup(val);
1202         } else if(c->type == TYPE_INT) {
1203             int *ip = c->ptr;
1204             *ip = (int)strtoul(val, &endp, 10);
1205         }
1206
1207         /* invoke a command specific function */
1208         if(c->func) c->func();
1209
1210         /* this will be removed as soon as we have converted to
1211         * the callback interface
1212         */
1213         p = *c->ptr;
1214     }
1215     return TRUE;
1216 }
1217
1218 static void
1219 runcmd(WebKitWebView* page, GArray *argv) {
1220     (void) page;
1221     parse_cmd_line(argv_idx(argv, 0));
1222 }
1223
1224 static void
1225 parse_cmd_line(const char *ctl_line) {
1226     gchar **tokens;
1227
1228     /* SET command */
1229     if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1230         tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1231         if(tokens[0][0] == 0) {
1232             gchar* value = parseenv (tokens[2]);
1233             set_var_value(tokens[1], value);
1234             g_strfreev(tokens);
1235             g_free(value);
1236         }
1237         else
1238             printf("Error in command: %s\n", tokens[0]);
1239     }
1240     /* GET command */
1241     else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1242         tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1243         if(tokens[0][0] == 0) {
1244             get_var_value(tokens[1]);
1245             g_strfreev(tokens);
1246         }
1247         else
1248             printf("Error in command: %s\n", tokens[0]);
1249     }
1250     /* BIND command */
1251     else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1252         tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1253         if(tokens[0][0] == 0) {
1254             gchar* value = parseenv (tokens[2]);
1255             add_binding(tokens[1], value);
1256             g_strfreev(tokens);
1257             g_free(value);
1258         }
1259         else
1260             printf("Error in command: %s\n", tokens[0]);
1261     }
1262     /* ACT command */
1263     else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1264         tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1265         if(tokens[0][0] == 0) {
1266             parse_command(tokens[1], tokens[2]);
1267             g_strfreev(tokens);
1268         }
1269         else
1270             printf("Error in command: %s\n", tokens[0]);
1271     }
1272     /* KEYCMD command */
1273     else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1274         tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1275         if(tokens[0][0] == 0) {
1276             /* should incremental commands want each individual "keystroke"
1277                sent in a loop or the whole string in one go like now? */
1278             g_string_assign(uzbl.state.keycmd, tokens[1]);
1279             run_keycmd(FALSE);
1280             if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
1281                 run_keycmd(TRUE);
1282             update_title();
1283             g_strfreev(tokens);
1284         }
1285     }
1286     /* Comments */
1287     else if(   (ctl_line[0] == '#')
1288             || (ctl_line[0] == ' ')
1289             || (ctl_line[0] == '\n'))
1290         ; /* ignore these lines */
1291     else
1292         printf("Command not understood (%s)\n", ctl_line);
1293
1294     return;
1295 }
1296
1297 static gchar*
1298 build_stream_name(int type, const gchar* dir) {
1299     char *xwin_str;
1300     State *s = &uzbl.state;
1301     gchar *str;
1302
1303     xwin_str = itos((int)uzbl.xwin);
1304     if (type == FIFO) {
1305         str = g_strdup_printf
1306             ("%s/uzbl_fifo_%s", dir,
1307              s->instance_name ? s->instance_name : xwin_str);
1308     } else if (type == SOCKET) {
1309         str = g_strdup_printf
1310             ("%s/uzbl_socket_%s", dir,
1311              s->instance_name ? s->instance_name : xwin_str );
1312     }
1313     g_free(xwin_str);
1314     return str;
1315 }
1316
1317 static gboolean
1318 control_fifo(GIOChannel *gio, GIOCondition condition) {
1319     if (uzbl.state.verbose)
1320         printf("triggered\n");
1321     gchar *ctl_line;
1322     GIOStatus ret;
1323     GError *err = NULL;
1324
1325     if (condition & G_IO_HUP)
1326         g_error ("Fifo: Read end of pipe died!\n");
1327
1328     if(!gio)
1329        g_error ("Fifo: GIOChannel broke\n");
1330
1331     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1332     if (ret == G_IO_STATUS_ERROR) {
1333         g_error ("Fifo: Error reading: %s\n", err->message);
1334         g_error_free (err);
1335     }
1336
1337     parse_cmd_line(ctl_line);
1338     g_free(ctl_line);
1339
1340     return TRUE;
1341 }
1342
1343 static gchar*
1344 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1345     if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1346         if (unlink(uzbl.comm.fifo_path) == -1)
1347             g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1348         g_free(uzbl.comm.fifo_path);
1349         uzbl.comm.fifo_path = NULL;
1350     }
1351
1352     if (*dir == ' ') { /* space unsets the variable */
1353         return NULL;
1354     }
1355
1356     GIOChannel *chan = NULL;
1357     GError *error = NULL;
1358     gchar *path = build_stream_name(FIFO, dir);
1359
1360     if (!file_exists(path)) {
1361         if (mkfifo (path, 0666) == 0) {
1362             // we don't really need to write to the file, but if we open the file as 'r' we will block here, waiting for a writer to open the file.
1363             chan = g_io_channel_new_file(path, "r+", &error);
1364             if (chan) {
1365                 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1366                     if (uzbl.state.verbose)
1367                         printf ("init_fifo: created successfully as %s\n", path);
1368                     uzbl.comm.fifo_path = path;
1369                     return dir;
1370                 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1371             } else g_warning ("init_fifo: can't open: %s\n", error->message);
1372         } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1373     } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1374
1375     /* if we got this far, there was an error; cleanup */
1376     if (error) g_error_free (error);
1377     g_free(path);
1378     return NULL;
1379 }
1380
1381 static gboolean
1382 control_stdin(GIOChannel *gio, GIOCondition condition) {
1383     gchar *ctl_line = NULL;
1384     GIOStatus ret;
1385
1386     if (condition & G_IO_HUP) {
1387         ret = g_io_channel_shutdown (gio, FALSE, NULL);
1388         return FALSE;
1389     }
1390
1391     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1392     if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1393         return FALSE;
1394
1395     parse_cmd_line(ctl_line);
1396     g_free(ctl_line);
1397
1398     return TRUE;
1399 }
1400
1401 static void
1402 create_stdin () {
1403     GIOChannel *chan = NULL;
1404     GError *error = NULL;
1405
1406     chan = g_io_channel_unix_new(fileno(stdin));
1407     if (chan) {
1408         if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1409             g_error ("Stdin: could not add watch\n");
1410         } else {
1411             if (uzbl.state.verbose)
1412                 printf ("Stdin: watch added successfully\n");
1413         }
1414     } else {
1415         g_error ("Stdin: Error while opening: %s\n", error->message);
1416     }
1417     if (error) g_error_free (error);
1418 }
1419
1420 static gboolean
1421 control_socket(GIOChannel *chan) {
1422     struct sockaddr_un remote;
1423     char buffer[512], *ctl_line;
1424     char temp[128];
1425     int sock, clientsock, n, done;
1426     unsigned int t;
1427
1428     sock = g_io_channel_unix_get_fd(chan);
1429
1430     memset (buffer, 0, sizeof (buffer));
1431
1432     t          = sizeof (remote);
1433     clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1434
1435     done = 0;
1436     do {
1437         memset (temp, 0, sizeof (temp));
1438         n = recv (clientsock, temp, 128, 0);
1439         if (n == 0) {
1440             buffer[strlen (buffer)] = '\0';
1441             done = 1;
1442         }
1443         if (!done)
1444             strcat (buffer, temp);
1445     } while (!done);
1446
1447     if (strcmp (buffer, "\n") < 0) {
1448         buffer[strlen (buffer) - 1] = '\0';
1449     } else {
1450         buffer[strlen (buffer)] = '\0';
1451     }
1452     close (clientsock);
1453     ctl_line = g_strdup(buffer);
1454     parse_cmd_line (ctl_line);
1455
1456 /*
1457    TODO: we should be able to do it with this.  but glib errors out with "Invalid argument"
1458     GError *error = NULL;
1459     gsize len;
1460     GIOStatus ret;
1461     ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1462     if (ret == G_IO_STATUS_ERROR)
1463         g_error ("Error reading: %s\n", error->message);
1464
1465     printf("Got line %s (%u bytes) \n",ctl_line, len);
1466     if(ctl_line) {
1467        parse_line(ctl_line);
1468 */
1469
1470     g_free(ctl_line);
1471     return TRUE;
1472 }
1473
1474 static gchar*
1475 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1476     if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1477         if (unlink(uzbl.comm.socket_path) == -1)
1478             g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1479         g_free(uzbl.comm.socket_path);
1480         uzbl.comm.socket_path = NULL;
1481     }
1482
1483     if (*dir == ' ') {
1484         g_free(dir);
1485         return NULL;
1486     }
1487
1488     GIOChannel *chan = NULL;
1489     int sock, len;
1490     struct sockaddr_un local;
1491     gchar *path = build_stream_name(SOCKET, dir);
1492
1493     sock = socket (AF_UNIX, SOCK_STREAM, 0);
1494
1495     local.sun_family = AF_UNIX;
1496     strcpy (local.sun_path, path);
1497     unlink (local.sun_path);
1498
1499     len = strlen (local.sun_path) + sizeof (local.sun_family);
1500     if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1501         if (uzbl.state.verbose)
1502             printf ("init_socket: opened in %s\n", path);
1503         listen (sock, 5);
1504
1505         if( (chan = g_io_channel_unix_new(sock)) ) {
1506             g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1507             uzbl.comm.socket_path = path;
1508             return dir;
1509         }
1510     } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1511
1512     /* if we got this far, there was an error; cleanup */
1513     g_free(path);
1514     g_free(dir);
1515     return NULL;
1516 }
1517
1518 /*
1519  NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1520  it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1521 */
1522 // this function may be called very early when the templates are not set (yet), hence the checks
1523 static void
1524 update_title (void) {
1525     Behaviour *b = &uzbl.behave;
1526     gchar *parsed;
1527
1528     if (b->show_status) {
1529         if (b->title_format_short) {
1530             parsed = expand_template(b->title_format_short);
1531             gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1532             g_free(parsed);
1533         }
1534         if (b->status_format) {
1535             parsed = expand_template(b->status_format);
1536             gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1537             g_free(parsed);
1538         }
1539         if (b->status_background) {
1540             GdkColor color;
1541             gdk_color_parse (b->status_background, &color);
1542             //labels and hboxes do not draw their own background.  applying this on the window is ok as we the statusbar is the only affected widget.  (if not, we could also use GtkEventBox)
1543             gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1544         }
1545     } else {
1546         if (b->title_format_long) {
1547             parsed = expand_template(b->title_format_long);
1548             gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1549             g_free(parsed);
1550         }
1551     }
1552 }
1553
1554 static gboolean
1555 key_press_cb (GtkWidget* window, GdkEventKey* event)
1556 {
1557     //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1558
1559     (void) window;
1560
1561     if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1562         || event->keyval == GDK_Up || event->keyval == GDK_Down || event->keyval == GDK_Left || event->keyval == GDK_Right || event->keyval == GDK_Shift_L || event->keyval == GDK_Shift_R)
1563         return FALSE;
1564
1565     /* turn off insert mode (if always_insert_mode is not used) */
1566     if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1567         uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1568         update_title();
1569         return TRUE;
1570     }
1571
1572     if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1573         return FALSE;
1574
1575     if (event->keyval == GDK_Escape) {
1576         g_string_truncate(uzbl.state.keycmd, 0);
1577         update_title();
1578         return TRUE;
1579     }
1580
1581     //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1582     if (event->keyval == GDK_Insert) {
1583         gchar * str;
1584         if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1585             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1586         } else {
1587             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1588         }
1589         if (str) {
1590             g_string_append (uzbl.state.keycmd, str);
1591             update_title ();
1592             free (str);
1593         }
1594         return TRUE;
1595     }
1596
1597     if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1598         g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1599         update_title();
1600     }
1601
1602     gboolean key_ret = FALSE;
1603     if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1604         key_ret = TRUE;
1605     if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1606
1607     run_keycmd(key_ret);
1608     update_title();
1609     if (key_ret) return (!uzbl.behave.insert_mode);
1610     return TRUE;
1611 }
1612
1613 static void
1614 run_keycmd(const gboolean key_ret) {
1615     /* run the keycmd immediately if it isn't incremental and doesn't take args */
1616     Action *action;
1617     if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1618         g_string_truncate(uzbl.state.keycmd, 0);
1619         parse_command(action->name, action->param);
1620         return;
1621     }
1622
1623     /* try if it's an incremental keycmd or one that takes args, and run it */
1624     GString* short_keys = g_string_new ("");
1625     GString* short_keys_inc = g_string_new ("");
1626     unsigned int i;
1627     for (i=0; i<(uzbl.state.keycmd->len); i++) {
1628         g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1629         g_string_assign(short_keys_inc, short_keys->str);
1630         g_string_append_c(short_keys, '_');
1631         g_string_append_c(short_keys_inc, '*');
1632
1633         gboolean exec_now = FALSE;
1634         if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1635             if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1636         } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1637             if (key_ret) { /* just quit the incremental command on return */
1638                 g_string_truncate(uzbl.state.keycmd, 0);
1639                 break;
1640             } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1641         }
1642
1643         if (exec_now) {
1644             GString* parampart = g_string_new (uzbl.state.keycmd->str);
1645             GString* actionname = g_string_new ("");
1646             GString* actionparam = g_string_new ("");
1647             g_string_erase (parampart, 0, i+1);
1648             if (action->name)
1649                 g_string_printf (actionname, action->name, parampart->str);
1650             if (action->param)
1651                 g_string_printf (actionparam, action->param, parampart->str);
1652             parse_command(actionname->str, actionparam->str);
1653             g_string_free (actionname, TRUE);
1654             g_string_free (actionparam, TRUE);
1655             g_string_free (parampart, TRUE);
1656             if (key_ret)
1657                 g_string_truncate(uzbl.state.keycmd, 0);
1658             break;
1659         }
1660
1661         g_string_truncate(short_keys, short_keys->len - 1);
1662     }
1663     g_string_free (short_keys, TRUE);
1664     g_string_free (short_keys_inc, TRUE);
1665 }
1666
1667 static GtkWidget*
1668 create_browser () {
1669     GUI *g = &uzbl.gui;
1670
1671     GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1672     //main_window_ref = g_object_ref(scrolled_window);
1673     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_NEVER); //todo: some sort of display of position/total length. like what emacs does
1674
1675     g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1676     gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1677
1678     g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1679     g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1680     g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1681     g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1682     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1683     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1684     g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1685     g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1686     g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1687     g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1688
1689     return scrolled_window;
1690 }
1691
1692 static GtkWidget*
1693 create_mainbar () {
1694     GUI *g = &uzbl.gui;
1695
1696     g->mainbar = gtk_hbox_new (FALSE, 0);
1697
1698     /* keep a reference to the bar so we can re-pack it at runtime*/
1699     //sbar_ref = g_object_ref(g->mainbar);
1700
1701     g->mainbar_label = gtk_label_new ("");
1702     gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1703     gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1704     gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1705     gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1706     gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1707     return g->mainbar;
1708 }
1709
1710 static
1711 GtkWidget* create_window () {
1712     GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1713     gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1714     gtk_widget_set_name (window, "Uzbl browser");
1715     g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1716     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
1717
1718     return window;
1719 }
1720
1721 static void
1722 run_handler (const gchar *act, const gchar *args) {
1723     char **parts = g_strsplit(act, " ", 2);
1724     if (!parts) return;
1725     else if ((g_strcmp0(parts[0], "spawn") == 0)
1726              || (g_strcmp0(parts[0], "sh") == 0)) {
1727         guint i;
1728         GString *a = g_string_new ("");
1729         char **spawnparts;
1730         spawnparts = split_quoted(parts[1], FALSE);
1731         g_string_append_printf(a, "%s", spawnparts[0]);
1732         if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1733         for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1734             g_string_append_printf(a, " %s", spawnparts[i]);
1735         parse_command(parts[0], a->str);
1736         g_string_free (a, TRUE);
1737         g_strfreev (spawnparts);
1738     } else
1739         parse_command(parts[0], parts[1]);
1740     g_strfreev (parts);
1741 }
1742
1743 static void
1744 add_binding (const gchar *key, const gchar *act) {
1745     char **parts = g_strsplit(act, " ", 2);
1746     Action *action;
1747
1748     if (!parts)
1749         return;
1750
1751     //Debug:
1752     if (uzbl.state.verbose)
1753         printf ("Binding %-10s : %s\n", key, act);
1754     action = new_action(parts[0], parts[1]);
1755
1756     if(g_hash_table_lookup(uzbl.bindings, key))
1757         g_hash_table_remove(uzbl.bindings, key);
1758     g_hash_table_insert(uzbl.bindings, g_strdup(key), action);
1759
1760     g_strfreev(parts);
1761 }
1762
1763 static gchar*
1764 get_xdg_var (XDG_Var xdg) {
1765     const gchar* actual_value = getenv (xdg.environmental);
1766     const gchar* home         = getenv ("HOME");
1767
1768     gchar* return_value = str_replace ("~", home, actual_value);
1769
1770     if (! actual_value || strcmp (actual_value, "") == 0) {
1771         if (xdg.default_value) {
1772             return_value = str_replace ("~", home, xdg.default_value);
1773         } else {
1774             return_value = NULL;
1775         }
1776     }
1777     return return_value;
1778 }
1779
1780 static gchar*
1781 find_xdg_file (int xdg_type, char* filename) {
1782     /* xdg_type = 0 => config
1783        xdg_type = 1 => data
1784        xdg_type = 2 => cache*/
1785
1786     gchar* xdgv = get_xdg_var (XDG[xdg_type]);
1787     gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
1788     g_free (xdgv);
1789
1790     gchar* temporary_string;
1791     char*  saveptr;
1792     char*  buf;
1793
1794     if (! file_exists (temporary_file) && xdg_type != 2) {
1795         buf = get_xdg_var (XDG[3 + xdg_type]);
1796         temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1797         free(buf);
1798
1799         while (temporary_string && ! file_exists (temporary_file)) {
1800             strcpy (temporary_file, temporary_string);
1801             strcat (temporary_file, filename);
1802             temporary_string = (char * ) strtok_r (NULL, ":", &saveptr);
1803         }
1804     }
1805     
1806     g_free (temporary_string);
1807
1808     if (file_exists (temporary_file)) {
1809         return temporary_file;
1810     } else {
1811         return NULL;
1812     }
1813 }
1814
1815 static void
1816 settings_init () {
1817     State *s = &uzbl.state;
1818     Network *n = &uzbl.net;
1819     uzbl.behave.reset_command_mode = 1;
1820
1821     if (!s->config_file) {
1822         s->config_file = find_xdg_file (0, "/uzbl/config");
1823     }
1824
1825     if (s->config_file) {
1826         GArray* lines = read_file_by_line (s->config_file);
1827         int i = 0;
1828         gchar* line;
1829
1830         while ((line = g_array_index(lines, gchar*, i))) {
1831             parse_cmd_line (line);
1832             i ++;
1833         }
1834         g_array_free (lines, TRUE);
1835     } else {
1836         if (uzbl.state.verbose)
1837             printf ("No configuration file loaded.\n");
1838     }
1839     if (!uzbl.behave.status_format)
1840         set_var_value("status_format", STATUS_DEFAULT);
1841     if (!uzbl.behave.title_format_long)
1842         set_var_value("title_format_long", TITLE_LONG_DEFAULT);
1843     if (!uzbl.behave.title_format_short)
1844         set_var_value("title_format_short", TITLE_SHORT_DEFAULT);
1845
1846
1847     g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1848 }
1849
1850 static gchar*
1851 set_useragent(gchar *val) {
1852     if (*val == ' ') {
1853         g_free(val);
1854         return NULL;
1855     }
1856     gchar *ua = expand_template(val);
1857     if (ua)
1858         g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1859     return ua;
1860 }
1861
1862 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1863     (void) session;
1864     (void) user_data;
1865     if (!uzbl.behave.cookie_handler) return;
1866
1867     gchar * stdout = NULL;
1868     soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1869     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1870     gchar *action = g_strdup ("GET");
1871     SoupURI * soup_uri = soup_message_get_uri(msg);
1872     sharg_append(a, action);
1873     sharg_append(a, soup_uri->host);
1874     sharg_append(a, soup_uri->path);
1875     run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, TRUE, &stdout); /* TODO: use handler */
1876     //run_handler(uzbl.behave.cookie_handler); /* TODO: global stdout pointer, spawn_sync */
1877     if(stdout) {
1878         soup_message_headers_replace (msg->request_headers, "Cookie", stdout);
1879     }
1880     g_free (action);
1881     g_array_free(a, TRUE);
1882 }
1883
1884 static void
1885 save_cookies (SoupMessage *msg, gpointer user_data){
1886     (void) user_data;
1887     GSList *ck;
1888     char *cookie;
1889     for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1890         cookie = soup_cookie_to_set_cookie_header(ck->data);
1891         GArray *a = g_array_new(TRUE, FALSE, sizeof(gchar*));
1892         SoupURI * soup_uri = soup_message_get_uri(msg);
1893         gchar *action = strdup("PUT");
1894         sharg_append(a, action);
1895         sharg_append(a, soup_uri->host);
1896         sharg_append(a, soup_uri->path);
1897         sharg_append(a, cookie);
1898         run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, FALSE, NULL);
1899         g_free (cookie);
1900         g_free (action);
1901         g_array_free(a, TRUE);
1902     }
1903     g_slist_free(ck);
1904 }
1905
1906 int
1907 main (int argc, char* argv[]) {
1908     gtk_init (&argc, &argv);
1909     if (!g_thread_supported ())
1910         g_thread_init (NULL);
1911     uzbl.state.executable_path = g_strdup(argv[0]);
1912     uzbl.state.selected_url = NULL;
1913     uzbl.state.searchtx = NULL;
1914
1915     GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1916     g_option_context_add_main_entries (context, entries, NULL);
1917     g_option_context_add_group (context, gtk_get_option_group (TRUE));
1918     g_option_context_parse (context, &argc, &argv, NULL);
1919     /* initialize hash table */
1920     uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1921
1922     uzbl.net.soup_session = webkit_get_default_session();
1923     uzbl.state.keycmd = g_string_new("");
1924
1925     if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1926         fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1927     if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1928         fprintf(stderr, "uzbl: error hooking SIGINT\n");
1929
1930     if(uname(&uzbl.state.unameinfo) == -1)
1931         g_printerr("Can't retrieve unameinfo.  Your useragent might appear wrong.\n");
1932
1933     setup_regex();
1934     setup_scanner();
1935     commands_hash ();
1936     make_var_to_name_hash();
1937
1938     uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1939
1940     uzbl.gui.scrolled_win = create_browser();
1941     create_mainbar();
1942
1943     /* initial packing */
1944     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1945     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1946
1947     uzbl.gui.main_window = create_window ();
1948     gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
1949
1950     //load_uri (uzbl.gui.web_view, uzbl.state.uri); //TODO: is this needed?
1951
1952     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1953     gtk_widget_show_all (uzbl.gui.main_window);
1954     uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1955
1956     if (uzbl.state.verbose) {
1957         printf("Uzbl start location: %s\n", argv[0]);
1958         printf("window_id %i\n",(int) uzbl.xwin);
1959         printf("pid %i\n", getpid ());
1960         printf("name: %s\n", uzbl.state.instance_name);
1961     }
1962
1963     uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1964     uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1965     uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
1966     uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
1967     gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
1968
1969     settings_init ();
1970
1971     if (!uzbl.behave.show_status)
1972         gtk_widget_hide(uzbl.gui.mainbar);
1973     else
1974         update_title();
1975
1976     create_stdin();
1977
1978     //if(uzbl.state.uri)
1979     //    load_uri (uzbl.gui.web_view, uzbl.state.uri);
1980
1981     gtk_main ();
1982     clean_up();
1983
1984     return EXIT_SUCCESS;
1985 }
1986
1987 /* vi: set et ts=4: */