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