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