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