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