Handle sync_* in run_handler
[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, cmd_cookie_handler)},
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_cookie_handler() {
1181     gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1182     if ((g_strcmp0(split[0], "sh") == 0) ||
1183         (g_strcmp0(split[0], "spawn") == 0)) {
1184         g_free (uzbl.behave.cookie_handler);
1185         uzbl.behave.cookie_handler =
1186             g_strdup_printf("sync_%s %s", split[0], split[1]);
1187     }
1188     g_strfreev (split);
1189 }
1190
1191 static void
1192 cmd_fifo_dir() {
1193     uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1194 }
1195
1196 static void
1197 cmd_socket_dir() {
1198     uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1199 }
1200
1201 static void
1202 cmd_modkey() {
1203     int i;
1204     char *buf;
1205
1206     buf = g_utf8_strup(uzbl.behave.modkey, -1);
1207     uzbl.behave.modmask = 0;
1208
1209     if(uzbl.behave.modkey) 
1210         g_free(uzbl.behave.modkey);
1211     uzbl.behave.modkey = buf;
1212
1213     for (i = 0; modkeys[i].key != NULL; i++) {
1214         if (g_strrstr(buf, modkeys[i].key))
1215             uzbl.behave.modmask |= modkeys[i].mask;
1216     }
1217 }
1218
1219 static void
1220 cmd_useragent() {
1221     if (*uzbl.net.useragent == ' ') {
1222         g_free (uzbl.net.useragent);
1223         uzbl.net.useragent = NULL;
1224     } else {
1225         gchar *ua = expand_template(uzbl.net.useragent);
1226         if (ua)
1227             g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1228         g_free(uzbl.net.useragent);
1229         uzbl.net.useragent = ua;
1230     }
1231 }
1232
1233 static void
1234 move_statusbar() {
1235     gtk_widget_ref(uzbl.gui.scrolled_win);
1236     gtk_widget_ref(uzbl.gui.mainbar);
1237     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1238     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1239
1240     if(uzbl.behave.status_top) {
1241         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1242         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1243     }
1244     else {
1245         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1246         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1247     }
1248     gtk_widget_unref(uzbl.gui.scrolled_win);
1249     gtk_widget_unref(uzbl.gui.mainbar);
1250     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1251     return;
1252 }
1253
1254 static gboolean
1255 set_var_value(gchar *name, gchar *val) {
1256     uzbl_cmdprop *c = NULL;
1257     char *endp = NULL;
1258
1259     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1260         /* check for the variable type */
1261         if (c->type == TYPE_STR) {
1262             g_free(*c->ptr);
1263             *c->ptr = g_strdup(val);
1264         } else if(c->type == TYPE_INT) {
1265             int *ip = GPOINTER_TO_INT(c->ptr);
1266             *ip = (int)strtoul(val, &endp, 10);
1267         }
1268
1269         /* invoke a command specific function */
1270         if(c->func) c->func();
1271     }
1272     return TRUE;
1273 }
1274
1275 static void
1276 runcmd(WebKitWebView* page, GArray *argv) {
1277     (void) page;
1278     parse_cmd_line(argv_idx(argv, 0));
1279 }
1280
1281 static void
1282 parse_cmd_line(const char *ctl_line) {
1283     gchar **tokens;
1284
1285     /* SET command */
1286     if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1287         tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1288         if(tokens[0][0] == 0) {
1289             gchar* value = parseenv(g_strdup(tokens[2]));
1290             set_var_value(tokens[1], value);
1291             g_strfreev(tokens);
1292             g_free(value);
1293         }
1294         else
1295             printf("Error in command: %s\n", tokens[0]);
1296     }
1297     /* GET command */
1298     else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1299         tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1300         if(tokens[0][0] == 0) {
1301             get_var_value(tokens[1]);
1302             g_strfreev(tokens);
1303         }
1304         else
1305             printf("Error in command: %s\n", tokens[0]);
1306     }
1307     /* BIND command */
1308     else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1309         tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1310         if(tokens[0][0] == 0) {
1311             gchar* value = parseenv(g_strdup(tokens[2]));
1312             add_binding(tokens[1], value);
1313             g_strfreev(tokens);
1314             g_free(value);
1315         }
1316         else
1317             printf("Error in command: %s\n", tokens[0]);
1318     }
1319     /* ACT command */
1320     else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1321         tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1322         if(tokens[0][0] == 0) {
1323             parse_command(tokens[1], tokens[2]);
1324             g_strfreev(tokens);
1325         }
1326         else
1327             printf("Error in command: %s\n", tokens[0]);
1328     }
1329     /* KEYCMD command */
1330     else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1331         tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1332         if(tokens[0][0] == 0) {
1333             /* should incremental commands want each individual "keystroke"
1334                sent in a loop or the whole string in one go like now? */
1335             g_string_assign(uzbl.state.keycmd, tokens[1]);
1336             run_keycmd(FALSE);
1337             if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
1338                 run_keycmd(TRUE);
1339             update_title();
1340             g_strfreev(tokens);
1341         }
1342     }
1343     /* Comments */
1344     else if(   (ctl_line[0] == '#')
1345             || (ctl_line[0] == ' ')
1346             || (ctl_line[0] == '\n'))
1347         ; /* ignore these lines */
1348     else
1349         printf("Command not understood (%s)\n", ctl_line);
1350
1351     return;
1352 }
1353
1354 static gchar*
1355 build_stream_name(int type, const gchar* dir) {
1356     char *xwin_str;
1357     State *s = &uzbl.state;
1358     gchar *str;
1359
1360     xwin_str = itos((int)uzbl.xwin);
1361     if (type == FIFO) {
1362         str = g_strdup_printf
1363             ("%s/uzbl_fifo_%s", dir,
1364              s->instance_name ? s->instance_name : xwin_str);
1365     } else if (type == SOCKET) {
1366         str = g_strdup_printf
1367             ("%s/uzbl_socket_%s", dir,
1368              s->instance_name ? s->instance_name : xwin_str );
1369     }
1370     g_free(xwin_str);
1371     return str;
1372 }
1373
1374 static gboolean
1375 control_fifo(GIOChannel *gio, GIOCondition condition) {
1376     if (uzbl.state.verbose)
1377         printf("triggered\n");
1378     gchar *ctl_line;
1379     GIOStatus ret;
1380     GError *err = NULL;
1381
1382     if (condition & G_IO_HUP)
1383         g_error ("Fifo: Read end of pipe died!\n");
1384
1385     if(!gio)
1386        g_error ("Fifo: GIOChannel broke\n");
1387
1388     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1389     if (ret == G_IO_STATUS_ERROR) {
1390         g_error ("Fifo: Error reading: %s\n", err->message);
1391         g_error_free (err);
1392     }
1393
1394     parse_cmd_line(ctl_line);
1395     g_free(ctl_line);
1396
1397     return TRUE;
1398 }
1399
1400 static gchar*
1401 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1402     if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1403         if (unlink(uzbl.comm.fifo_path) == -1)
1404             g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1405         g_free(uzbl.comm.fifo_path);
1406         uzbl.comm.fifo_path = NULL;
1407     }
1408
1409     if (*dir == ' ') { /* space unsets the variable */
1410         g_free (dir);
1411         return NULL;
1412     }
1413
1414     GIOChannel *chan = NULL;
1415     GError *error = NULL;
1416     gchar *path = build_stream_name(FIFO, dir);
1417
1418     if (!file_exists(path)) {
1419         if (mkfifo (path, 0666) == 0) {
1420             // 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.
1421             chan = g_io_channel_new_file(path, "r+", &error);
1422             if (chan) {
1423                 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1424                     if (uzbl.state.verbose)
1425                         printf ("init_fifo: created successfully as %s\n", path);
1426                     uzbl.comm.fifo_path = path;
1427                     return dir;
1428                 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1429             } else g_warning ("init_fifo: can't open: %s\n", error->message);
1430         } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1431     } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1432
1433     /* if we got this far, there was an error; cleanup */
1434     if (error) g_error_free (error);
1435     g_free(dir);
1436     g_free(path);
1437     return NULL;
1438 }
1439
1440 static gboolean
1441 control_stdin(GIOChannel *gio, GIOCondition condition) {
1442     (void) condition;
1443     gchar *ctl_line = NULL;
1444     GIOStatus ret;
1445
1446     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1447     if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1448         return FALSE;
1449
1450     parse_cmd_line(ctl_line);
1451     g_free(ctl_line);
1452
1453     return TRUE;
1454 }
1455
1456 static void
1457 create_stdin () {
1458     GIOChannel *chan = NULL;
1459     GError *error = NULL;
1460
1461     chan = g_io_channel_unix_new(fileno(stdin));
1462     if (chan) {
1463         if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1464             g_error ("Stdin: could not add watch\n");
1465         } else {
1466             if (uzbl.state.verbose)
1467                 printf ("Stdin: watch added successfully\n");
1468         }
1469     } else {
1470         g_error ("Stdin: Error while opening: %s\n", error->message);
1471     }
1472     if (error) g_error_free (error);
1473 }
1474
1475 static gboolean
1476 control_socket(GIOChannel *chan) {
1477     struct sockaddr_un remote;
1478     char buffer[512], *ctl_line;
1479     char temp[128];
1480     int sock, clientsock, n, done;
1481     unsigned int t;
1482
1483     sock = g_io_channel_unix_get_fd(chan);
1484
1485     memset (buffer, 0, sizeof (buffer));
1486
1487     t          = sizeof (remote);
1488     clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1489
1490     done = 0;
1491     do {
1492         memset (temp, 0, sizeof (temp));
1493         n = recv (clientsock, temp, 128, 0);
1494         if (n == 0) {
1495             buffer[strlen (buffer)] = '\0';
1496             done = 1;
1497         }
1498         if (!done)
1499             strcat (buffer, temp);
1500     } while (!done);
1501
1502     if (strcmp (buffer, "\n") < 0) {
1503         buffer[strlen (buffer) - 1] = '\0';
1504     } else {
1505         buffer[strlen (buffer)] = '\0';
1506     }
1507     close (clientsock);
1508     ctl_line = g_strdup(buffer);
1509     parse_cmd_line (ctl_line);
1510
1511 /*
1512    TODO: we should be able to do it with this.  but glib errors out with "Invalid argument"
1513     GError *error = NULL;
1514     gsize len;
1515     GIOStatus ret;
1516     ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1517     if (ret == G_IO_STATUS_ERROR)
1518         g_error ("Error reading: %s\n", error->message);
1519
1520     printf("Got line %s (%u bytes) \n",ctl_line, len);
1521     if(ctl_line) {
1522        parse_line(ctl_line);
1523 */
1524
1525     g_free(ctl_line);
1526     return TRUE;
1527 }
1528
1529 static gchar*
1530 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1531     if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1532         if (unlink(uzbl.comm.socket_path) == -1)
1533             g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1534         g_free(uzbl.comm.socket_path);
1535         uzbl.comm.socket_path = NULL;
1536     }
1537
1538     if (*dir == ' ') {
1539         g_free(dir);
1540         return NULL;
1541     }
1542
1543     GIOChannel *chan = NULL;
1544     int sock, len;
1545     struct sockaddr_un local;
1546     gchar *path = build_stream_name(SOCKET, dir);
1547
1548     sock = socket (AF_UNIX, SOCK_STREAM, 0);
1549
1550     local.sun_family = AF_UNIX;
1551     strcpy (local.sun_path, path);
1552     unlink (local.sun_path);
1553
1554     len = strlen (local.sun_path) + sizeof (local.sun_family);
1555     if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1556         if (uzbl.state.verbose)
1557             printf ("init_socket: opened in %s\n", path);
1558         listen (sock, 5);
1559
1560         if( (chan = g_io_channel_unix_new(sock)) ) {
1561             g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1562             uzbl.comm.socket_path = path;
1563             return dir;
1564         }
1565     } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1566
1567     /* if we got this far, there was an error; cleanup */
1568     g_free(path);
1569     g_free(dir);
1570     return NULL;
1571 }
1572
1573 /*
1574  NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1575  it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1576 */
1577 // this function may be called very early when the templates are not set (yet), hence the checks
1578 static void
1579 update_title (void) {
1580     Behaviour *b = &uzbl.behave;
1581     gchar *parsed;
1582
1583     if (b->show_status) {
1584         if (b->title_format_short) {
1585             parsed = expand_template(b->title_format_short);
1586             gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1587             g_free(parsed);
1588         }
1589         if (b->status_format) {
1590             parsed = expand_template(b->status_format);
1591             gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1592             g_free(parsed);
1593         }
1594         if (b->status_background) {
1595             GdkColor color;
1596             gdk_color_parse (b->status_background, &color);
1597             //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)
1598             gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1599         }
1600     } else {
1601         if (b->title_format_long) {
1602             parsed = expand_template(b->title_format_long);
1603             gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1604             g_free(parsed);
1605         }
1606     }
1607 }
1608
1609 static gboolean
1610 key_press_cb (GtkWidget* window, GdkEventKey* event)
1611 {
1612     //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1613
1614     (void) window;
1615
1616     if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1617         || 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)
1618         return FALSE;
1619
1620     /* turn off insert mode (if always_insert_mode is not used) */
1621     if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1622         uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1623         update_title();
1624         return TRUE;
1625     }
1626
1627     if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1628         return FALSE;
1629
1630     if (event->keyval == GDK_Escape) {
1631         g_string_truncate(uzbl.state.keycmd, 0);
1632         update_title();
1633         return TRUE;
1634     }
1635
1636     //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1637     if (event->keyval == GDK_Insert) {
1638         gchar * str;
1639         if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1640             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1641         } else {
1642             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1643         }
1644         if (str) {
1645             g_string_append (uzbl.state.keycmd, str);
1646             update_title ();
1647             g_free (str);
1648         }
1649         return TRUE;
1650     }
1651
1652     if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1653         g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1654         update_title();
1655     }
1656
1657     gboolean key_ret = FALSE;
1658     if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1659         key_ret = TRUE;
1660     if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1661
1662     run_keycmd(key_ret);
1663     update_title();
1664     if (key_ret) return (!uzbl.behave.insert_mode);
1665     return TRUE;
1666 }
1667
1668 static void
1669 run_keycmd(const gboolean key_ret) {
1670     /* run the keycmd immediately if it isn't incremental and doesn't take args */
1671     Action *action;
1672     if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1673         g_string_truncate(uzbl.state.keycmd, 0);
1674         parse_command(action->name, action->param);
1675         return;
1676     }
1677
1678     /* try if it's an incremental keycmd or one that takes args, and run it */
1679     GString* short_keys = g_string_new ("");
1680     GString* short_keys_inc = g_string_new ("");
1681     unsigned int i;
1682     for (i=0; i<(uzbl.state.keycmd->len); i++) {
1683         g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1684         g_string_assign(short_keys_inc, short_keys->str);
1685         g_string_append_c(short_keys, '_');
1686         g_string_append_c(short_keys_inc, '*');
1687
1688         gboolean exec_now = FALSE;
1689         if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1690             if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1691         } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1692             if (key_ret) { /* just quit the incremental command on return */
1693                 g_string_truncate(uzbl.state.keycmd, 0);
1694                 break;
1695             } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1696         }
1697
1698         if (exec_now) {
1699             GString* parampart = g_string_new (uzbl.state.keycmd->str);
1700             GString* actionname = g_string_new ("");
1701             GString* actionparam = g_string_new ("");
1702             g_string_erase (parampart, 0, i+1);
1703             if (action->name)
1704                 g_string_printf (actionname, action->name, parampart->str);
1705             if (action->param)
1706                 g_string_printf (actionparam, action->param, parampart->str);
1707             parse_command(actionname->str, actionparam->str);
1708             g_string_free (actionname, TRUE);
1709             g_string_free (actionparam, TRUE);
1710             g_string_free (parampart, TRUE);
1711             if (key_ret)
1712                 g_string_truncate(uzbl.state.keycmd, 0);
1713             break;
1714         }
1715
1716         g_string_truncate(short_keys, short_keys->len - 1);
1717     }
1718     g_string_free (short_keys, TRUE);
1719     g_string_free (short_keys_inc, TRUE);
1720 }
1721
1722 static GtkWidget*
1723 create_browser () {
1724     GUI *g = &uzbl.gui;
1725
1726     GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1727     //main_window_ref = g_object_ref(scrolled_window);
1728     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
1729
1730     g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1731     gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1732
1733     g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1734     g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1735     g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1736     g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1737     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1738     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1739     g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1740     g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1741     g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1742     g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1743
1744     return scrolled_window;
1745 }
1746
1747 static GtkWidget*
1748 create_mainbar () {
1749     GUI *g = &uzbl.gui;
1750
1751     g->mainbar = gtk_hbox_new (FALSE, 0);
1752
1753     /* keep a reference to the bar so we can re-pack it at runtime*/
1754     //sbar_ref = g_object_ref(g->mainbar);
1755
1756     g->mainbar_label = gtk_label_new ("");
1757     gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1758     gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1759     gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1760     gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1761     gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1762     return g->mainbar;
1763 }
1764
1765 static
1766 GtkWidget* create_window () {
1767     GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1768     gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1769     gtk_widget_set_name (window, "Uzbl browser");
1770     g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1771     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
1772
1773     return window;
1774 }
1775
1776 static void
1777 run_handler (const gchar *act, const gchar *args) {
1778     char **parts = g_strsplit(act, " ", 2);
1779     if (!parts) return;
1780     else if ((g_strcmp0(parts[0], "spawn") == 0)
1781              || (g_strcmp0(parts[0], "sh") == 0)
1782              || (g_strcmp0(parts[0], "sync_spawn") == 0)
1783              || (g_strcmp0(parts[0], "sync_sh") == 0)) {
1784         guint i;
1785         GString *a = g_string_new ("");
1786         char **spawnparts;
1787         spawnparts = split_quoted(parts[1], FALSE);
1788         g_string_append_printf(a, "%s", spawnparts[0]);
1789         if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1790         
1791         for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1792             g_string_append_printf(a, " %s", spawnparts[i]);
1793         parse_command(parts[0], a->str);
1794         g_string_free (a, TRUE);
1795         g_strfreev (spawnparts);
1796     } else
1797         parse_command(parts[0], parts[1]);
1798     g_strfreev (parts);
1799 }
1800
1801 static void
1802 add_binding (const gchar *key, const gchar *act) {
1803     char **parts = g_strsplit(act, " ", 2);
1804     Action *action;
1805
1806     if (!parts)
1807         return;
1808
1809     //Debug:
1810     if (uzbl.state.verbose)
1811         printf ("Binding %-10s : %s\n", key, act);
1812     action = new_action(parts[0], parts[1]);
1813
1814     g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
1815     g_strfreev(parts);
1816 }
1817
1818 static gchar*
1819 get_xdg_var (XDG_Var xdg) {
1820     const gchar* actual_value = getenv (xdg.environmental);
1821     const gchar* home         = getenv ("HOME");
1822
1823     gchar* return_value = str_replace ("~", home, actual_value);
1824
1825     if (! actual_value || strcmp (actual_value, "") == 0) {
1826         if (xdg.default_value) {
1827             return_value = str_replace ("~", home, xdg.default_value);
1828         } else {
1829             return_value = NULL;
1830         }
1831     }
1832     return return_value;
1833 }
1834
1835 static gchar*
1836 find_xdg_file (int xdg_type, char* filename) {
1837     /* xdg_type = 0 => config
1838        xdg_type = 1 => data
1839        xdg_type = 2 => cache*/
1840
1841     gchar* xdgv = get_xdg_var (XDG[xdg_type]);
1842     gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
1843     g_free (xdgv);
1844
1845     gchar* temporary_string;
1846     char*  saveptr;
1847     char*  buf;
1848
1849     if (! file_exists (temporary_file) && xdg_type != 2) {
1850         buf = get_xdg_var (XDG[3 + xdg_type]);
1851         temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1852         g_free(buf);
1853
1854         while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
1855             g_free (temporary_file);
1856             temporary_file = g_strconcat (temporary_string, filename, NULL);
1857         }
1858     }
1859     
1860     //g_free (temporary_string); - segfaults.
1861
1862     if (file_exists (temporary_file)) {
1863         return temporary_file;
1864     } else {
1865         return NULL;
1866     }
1867 }
1868 static void
1869 settings_init () {
1870     State *s = &uzbl.state;
1871     Network *n = &uzbl.net;
1872     int i;
1873     for (i = 0; default_config[i].command != NULL; i++) {
1874         parse_cmd_line(default_config[i].command);
1875     }
1876
1877     if (!s->config_file) {
1878         s->config_file = find_xdg_file (0, "/uzbl/config");
1879     }
1880
1881     if (s->config_file) {
1882         GArray* lines = read_file_by_line (s->config_file);
1883         int i = 0;
1884         gchar* line;
1885
1886         while ((line = g_array_index(lines, gchar*, i))) {
1887             parse_cmd_line (line);
1888             i ++;
1889             g_free (line);
1890         }
1891         g_array_free (lines, TRUE);
1892     } else {
1893         if (uzbl.state.verbose)
1894             printf ("No configuration file loaded.\n");
1895     }
1896
1897     g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1898 }
1899
1900 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1901     (void) session;
1902     (void) user_data;
1903     if (!uzbl.behave.cookie_handler) return;
1904
1905     soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1906     GString *s = g_string_new ("");
1907     SoupURI * soup_uri = soup_message_get_uri(msg);
1908     g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
1909     run_handler(uzbl.behave.cookie_handler, s->str);
1910
1911     if(uzbl.comm.sync_stdout)
1912         soup_message_headers_replace (msg->request_headers, "Cookie", uzbl.comm.sync_stdout);
1913     //printf("stdout: %s\n", uzbl.comm.sync_stdout);   // debugging
1914     if (uzbl.comm.sync_stdout) uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
1915         
1916     g_string_free(s, TRUE);
1917 }
1918
1919 static void
1920 save_cookies (SoupMessage *msg, gpointer user_data){
1921     (void) user_data;
1922     GSList *ck;
1923     char *cookie;
1924     for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1925         cookie = soup_cookie_to_set_cookie_header(ck->data);
1926         SoupURI * soup_uri = soup_message_get_uri(msg);
1927         GString *s = g_string_new ("");
1928         g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
1929         run_handler(uzbl.behave.cookie_handler, s->str);
1930         g_free (cookie);
1931         g_string_free(s, TRUE);
1932     }
1933     g_slist_free(ck);
1934 }
1935
1936 int
1937 main (int argc, char* argv[]) {
1938     gtk_init (&argc, &argv);
1939     if (!g_thread_supported ())
1940         g_thread_init (NULL);
1941     uzbl.state.executable_path = g_strdup(argv[0]);
1942     uzbl.state.selected_url = NULL;
1943     uzbl.state.searchtx = NULL;
1944
1945     GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1946     g_option_context_add_main_entries (context, entries, NULL);
1947     g_option_context_add_group (context, gtk_get_option_group (TRUE));
1948     g_option_context_parse (context, &argc, &argv, NULL);
1949     g_option_context_free(context);
1950     /* initialize hash table */
1951     uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1952
1953     uzbl.net.soup_session = webkit_get_default_session();
1954     uzbl.state.keycmd = g_string_new("");
1955
1956     if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1957         fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1958     if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1959         fprintf(stderr, "uzbl: error hooking SIGINT\n");
1960
1961     if(uname(&uzbl.state.unameinfo) == -1)
1962         g_printerr("Can't retrieve unameinfo.  Your useragent might appear wrong.\n");
1963
1964     uzbl.gui.sbar.progress_s = g_strdup("=");
1965     uzbl.gui.sbar.progress_u = g_strdup("ยท");
1966     uzbl.gui.sbar.progress_w = 10;
1967
1968     setup_regex();
1969     setup_scanner();
1970     commands_hash ();
1971     make_var_to_name_hash();
1972
1973     uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1974
1975     uzbl.gui.scrolled_win = create_browser();
1976     create_mainbar();
1977
1978     /* initial packing */
1979     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1980     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1981
1982     uzbl.gui.main_window = create_window ();
1983     gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
1984
1985
1986     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1987     gtk_widget_show_all (uzbl.gui.main_window);
1988     uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1989
1990     if (uzbl.state.verbose) {
1991         printf("Uzbl start location: %s\n", argv[0]);
1992         printf("window_id %i\n",(int) uzbl.xwin);
1993         printf("pid %i\n", getpid ());
1994         printf("name: %s\n", uzbl.state.instance_name);
1995     }
1996
1997     uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1998     uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1999     uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2000     uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2001     gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2002
2003     settings_init ();
2004
2005     if (!uzbl.behave.show_status)
2006         gtk_widget_hide(uzbl.gui.mainbar);
2007     else
2008         update_title();
2009
2010     create_stdin();
2011
2012     if(uzbl.state.uri) {
2013         GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
2014         g_array_append_val(a, uzbl.state.uri);
2015         load_uri (uzbl.gui.web_view, a);
2016         g_array_free (a, TRUE);
2017     }
2018
2019     gtk_main ();
2020     clean_up();
2021
2022     return EXIT_SUCCESS;
2023 }
2024
2025 /* vi: set et ts=4: */