fix modkey
[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     gboolean result;
807     if (sync) result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
808                                     NULL, NULL, stdout, NULL, NULL, &err);
809     else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
810                                 NULL, NULL, NULL, &err);
811
812     if (uzbl.state.verbose) {
813         GString *s = g_string_new("spawned:");
814         for (i = 0; i < (a->len); i++) {
815             gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
816             g_string_append_printf(s, " %s", qarg);
817             g_free (qarg);
818         }
819         g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
820         printf("%s\n", s->str);
821         g_string_free(s, TRUE);
822     }
823     if (err) {
824         g_printerr("error on run_command: %s\n", err->message);
825         g_error_free (err);
826     }
827     g_free (pid);
828     g_free (xwin);
829     g_array_free (a, TRUE);
830     return result;
831 }
832
833 static gchar**
834 split_quoted(const gchar* src, const gboolean unquote) {
835     /* split on unquoted space, return array of strings;
836        remove a layer of quotes and backslashes if unquote */
837     if (!src) return NULL;
838     
839     gboolean dq = FALSE;
840     gboolean sq = FALSE;
841     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
842     GString *s = g_string_new ("");
843     const gchar *p;
844     gchar **ret;
845     gchar *dup;
846     for (p = src; *p != '\0'; p++) {
847         if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
848         else if (*p == '\\') { g_string_append_c(s, *p++);
849                                g_string_append_c(s, *p); }
850         else if ((*p == '"') && unquote && !sq) dq = !dq;
851         else if (*p == '"' && !sq) { g_string_append_c(s, *p);
852                                      dq = !dq; }
853         else if ((*p == '\'') && unquote && !dq) sq = !sq;
854         else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
855                                       sq = ! sq; }
856         else if ((*p == ' ') && !dq && !sq) {
857             dup = g_strdup(s->str);
858             g_array_append_val(a, dup);
859             g_string_truncate(s, 0);
860         } else g_string_append_c(s, *p);
861     }
862     dup = g_strdup(s->str);
863     g_array_append_val(a, dup);
864     ret = (gchar**)a->data;
865     g_array_free (a, FALSE);
866     g_string_free (s, FALSE);
867     return ret;
868 }
869
870 static void
871 spawn(WebKitWebView *web_view, GArray *argv) {
872     (void)web_view;
873     //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
874     if (argv_idx(argv, 0)) run_command(argv_idx(argv, 0), 0, argv->data + sizeof(gchar*), FALSE, NULL);
875 }
876
877 static void
878 spawn_sh(WebKitWebView *web_view, GArray *argv) {
879     (void)web_view;
880     if (!uzbl.behave.shell_cmd) {
881         g_printerr ("spawn_sh: shell_cmd is not set!\n");
882         return;
883     }
884     
885     guint i;
886     gchar *spacer = g_strdup("");
887     g_array_insert_val(argv, 1, spacer);
888     gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
889
890     for (i = 1; i < g_strv_length(cmd); i++)
891         g_array_prepend_val(argv, cmd[i]);
892
893     if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, argv->data, FALSE, NULL);
894     g_free (spacer);
895     g_strfreev (cmd);
896 }
897
898 static void
899 parse_command(const char *cmd, const char *param) {
900     Command *c;
901
902     if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
903
904             guint i;
905             gchar **par = split_quoted(param, TRUE);
906             GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
907
908             if (c[1]) { /* don't split */
909                 sharg_append(a, param);
910             } else {
911                 for (i = 0; i < g_strv_length(par); i++)
912                     sharg_append(a, par[i]);
913             }
914             c[0](uzbl.gui.web_view, a);
915             g_strfreev (par);
916             g_array_free (a, TRUE);
917
918     } else
919         g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
920 }
921
922 /* command parser */
923 static void
924 setup_regex() {
925     uzbl.comm.get_regex  = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
926             G_REGEX_OPTIMIZE, 0, NULL);
927     uzbl.comm.set_regex  = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
928             G_REGEX_OPTIMIZE, 0, NULL);
929     uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
930             G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
931     uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
932             G_REGEX_OPTIMIZE, 0, NULL);
933     uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
934             G_REGEX_OPTIMIZE, 0, NULL);
935 }
936
937 static gboolean
938 get_var_value(gchar *name) {
939     uzbl_cmdprop *c;
940
941     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
942         if(c->type == TYPE_STRING)
943             printf("VAR: %s VALUE: %s\n", name, (char *)*c->ptr);
944         else if(c->type == TYPE_INT)
945             printf("VAR: %s VALUE: %d\n", name, (int)*c->ptr);
946     }
947     return TRUE;
948 }
949
950 static void
951 set_proxy_url() {
952     SoupURI *suri;
953
954     if(*uzbl.net.proxy_url == ' '
955        || uzbl.net.proxy_url == NULL) {
956         soup_session_remove_feature_by_type(uzbl.net.soup_session,
957                 (GType) SOUP_SESSION_PROXY_URI);
958     }
959     else {
960         suri = soup_uri_new(uzbl.net.proxy_url);
961         g_object_set(G_OBJECT(uzbl.net.soup_session),
962                 SOUP_SESSION_PROXY_URI,
963                 suri, NULL);
964         soup_uri_free(suri);
965     }
966     return;
967 }
968
969 static void
970 cmd_load_uri() {
971     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
972     g_array_append_val (a, uzbl.state.uri);
973     load_uri(uzbl.gui.web_view, a);
974     g_array_free (a, TRUE);
975 }
976
977 static void 
978 cmd_always_insert_mode() {
979     uzbl.behave.insert_mode =
980         uzbl.behave.always_insert_mode ?  TRUE : FALSE;
981     update_title();
982 }
983
984 static void
985 cmd_max_conns() {
986     g_object_set(G_OBJECT(uzbl.net.soup_session),
987             SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
988 }
989
990 static void
991 cmd_max_conns_host() {
992     g_object_set(G_OBJECT(uzbl.net.soup_session),
993             SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
994 }
995
996 static void
997 cmd_http_debug() {
998     soup_session_remove_feature
999         (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1000     /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1001     /*g_free(uzbl.net.soup_logger);*/
1002
1003     uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1004     soup_session_add_feature(uzbl.net.soup_session,
1005             SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1006 }
1007
1008 static void
1009 cmd_default_font_size() {
1010     WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1011     g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.default_font_size, NULL);
1012 }
1013
1014 static void
1015 cmd_minimum_font_size() {
1016     WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1017     g_object_set (G_OBJECT(ws), "minimum-font-size", uzbl.behave.minimum_font_size, NULL);
1018 }
1019
1020 static void
1021 cmd_fifo_dir() {
1022     char *buf;
1023
1024     buf = init_fifo(uzbl.behave.fifo_dir);
1025     if(uzbl.behave.fifo_dir) 
1026         free(uzbl.behave.fifo_dir);
1027
1028     uzbl.behave.fifo_dir = buf?buf:g_strdup("");
1029 }
1030
1031 static void
1032 cmd_socket_dir() {
1033     char *buf;
1034
1035     buf = init_socket(uzbl.behave.fifo_dir);
1036     if(uzbl.behave.socket_dir) 
1037         free(uzbl.behave.socket_dir);
1038
1039     uzbl.behave.socket_dir = buf?buf:g_strdup("");
1040 }
1041
1042 static void
1043 cmd_modkey() {
1044     int i;
1045     char *buf;
1046
1047     buf = g_utf8_strup(uzbl.behave.modkey, -1);
1048     uzbl.behave.modmask = 0;
1049
1050     if(uzbl.behave.modkey) 
1051         free(uzbl.behave.modkey);
1052     uzbl.behave.modkey = buf;
1053
1054     for (i = 0; modkeys[i].key != NULL; i++) {
1055         if (g_strrstr(uzbl.behave.modkey, modkeys[i].key))
1056             uzbl.behave.modmask |= modkeys[i].mask;
1057     }
1058 }
1059
1060 static void
1061 cmd_useragent() {
1062     char *buf;
1063
1064     buf = set_useragent(uzbl.net.useragent);
1065     if(uzbl.net.useragent) 
1066         free(uzbl.net.useragent);
1067
1068     uzbl.net.useragent = buf?buf:g_strdup("");
1069 }
1070
1071 static void
1072 move_statusbar() {
1073     gtk_widget_ref(uzbl.gui.scrolled_win);
1074     gtk_widget_ref(uzbl.gui.mainbar);
1075     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1076     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1077
1078     if(uzbl.behave.status_top) {
1079         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1080         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1081     }
1082     else {
1083         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1084         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1085     }
1086     gtk_widget_unref(uzbl.gui.scrolled_win);
1087     gtk_widget_unref(uzbl.gui.mainbar);
1088     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1089     return;
1090 }
1091
1092 static gboolean
1093 set_var_value(gchar *name, gchar *val) {
1094     uzbl_cmdprop *c = NULL;
1095     char *endp = NULL;
1096
1097     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1098         /* check for the variable type */
1099         if (c->type == TYPE_STRING) {
1100             free(*c->ptr);
1101             *c->ptr = g_strdup(val);
1102         } else if(c->type == TYPE_INT) {
1103             int *ip = GPOINTER_TO_INT(c->ptr);
1104             *ip = (int)strtoul(val, &endp, 10);
1105         }
1106
1107         /* invoke a command specific function */
1108         if(c->func) c->func();
1109     }
1110     return TRUE;
1111 }
1112
1113 static void
1114 runcmd(WebKitWebView* page, GArray *argv) {
1115     (void) page;
1116     parse_cmd_line(argv_idx(argv, 0));
1117 }
1118
1119 static void
1120 parse_cmd_line(const char *ctl_line) {
1121     gchar **tokens;
1122
1123     /* SET command */
1124     if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1125         tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1126         if(tokens[0][0] == 0) {
1127             set_var_value(tokens[1], tokens[2]);
1128             g_strfreev(tokens);
1129         }
1130         else
1131             printf("Error in command: %s\n", tokens[0]);
1132     }
1133     /* GET command */
1134     else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1135         tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1136         if(tokens[0][0] == 0) {
1137             get_var_value(tokens[1]);
1138             g_strfreev(tokens);
1139         }
1140         else
1141             printf("Error in command: %s\n", tokens[0]);
1142     }
1143     /* BIND command */
1144     else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1145         tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1146         if(tokens[0][0] == 0) {
1147             add_binding(tokens[1], tokens[2]);
1148             g_strfreev(tokens);
1149         }
1150         else
1151             printf("Error in command: %s\n", tokens[0]);
1152     }
1153     /* ACT command */
1154     else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1155         tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1156         if(tokens[0][0] == 0) {
1157             parse_command(tokens[1], tokens[2]);
1158             g_strfreev(tokens);
1159         }
1160         else
1161             printf("Error in command: %s\n", tokens[0]);
1162     }
1163     /* KEYCMD command */
1164     else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1165         tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1166         if(tokens[0][0] == 0) {
1167             /* should incremental commands want each individual "keystroke"
1168                sent in a loop or the whole string in one go like now? */
1169             g_string_assign(uzbl.state.keycmd, tokens[1]);
1170             run_keycmd(FALSE);
1171             if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
1172                 run_keycmd(TRUE);
1173             update_title();
1174             g_strfreev(tokens);
1175         }
1176     }
1177     /* Comments */
1178     else if(   (ctl_line[0] == '#')
1179             || (ctl_line[0] == ' ')
1180             || (ctl_line[0] == '\n'))
1181         ; /* ignore these lines */
1182     else
1183         printf("Command not understood (%s)\n", ctl_line);
1184
1185     return;
1186 }
1187
1188 static gchar*
1189 build_stream_name(int type, const gchar* dir) {
1190     char *xwin_str;
1191     State *s = &uzbl.state;
1192     gchar *str;
1193
1194     xwin_str = itos((int)uzbl.xwin);
1195     if (type == FIFO) {
1196         str = g_strdup_printf
1197             ("%s/uzbl_fifo_%s", dir,
1198              s->instance_name ? s->instance_name : xwin_str);
1199     } else if (type == SOCKET) {
1200         str = g_strdup_printf
1201             ("%s/uzbl_socket_%s", dir,
1202              s->instance_name ? s->instance_name : xwin_str );
1203     }
1204     g_free(xwin_str);
1205     return str;
1206 }
1207
1208 static gboolean
1209 control_fifo(GIOChannel *gio, GIOCondition condition) {
1210     if (uzbl.state.verbose)
1211         printf("triggered\n");
1212     gchar *ctl_line;
1213     GIOStatus ret;
1214     GError *err = NULL;
1215
1216     if (condition & G_IO_HUP)
1217         g_error ("Fifo: Read end of pipe died!\n");
1218
1219     if(!gio)
1220        g_error ("Fifo: GIOChannel broke\n");
1221
1222     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1223     if (ret == G_IO_STATUS_ERROR) {
1224         g_error ("Fifo: Error reading: %s\n", err->message);
1225         g_error_free (err);
1226     }
1227
1228     parse_cmd_line(ctl_line);
1229     g_free(ctl_line);
1230
1231     return TRUE;
1232 }
1233
1234 static gchar*
1235 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1236     if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1237         if (unlink(uzbl.comm.fifo_path) == -1)
1238             g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1239         g_free(uzbl.comm.fifo_path);
1240         uzbl.comm.fifo_path = NULL;
1241     }
1242
1243     if (*dir == ' ') { /* space unsets the variable */
1244         return NULL;
1245     }
1246
1247     GIOChannel *chan = NULL;
1248     GError *error = NULL;
1249     gchar *path = build_stream_name(FIFO, dir);
1250
1251     if (!file_exists(path)) {
1252         if (mkfifo (path, 0666) == 0) {
1253             // 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.
1254             chan = g_io_channel_new_file(path, "r+", &error);
1255             if (chan) {
1256                 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1257                     if (uzbl.state.verbose)
1258                         printf ("init_fifo: created successfully as %s\n", path);
1259                     uzbl.comm.fifo_path = path;
1260                     return dir;
1261                 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1262             } else g_warning ("init_fifo: can't open: %s\n", error->message);
1263         } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1264     } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1265
1266     /* if we got this far, there was an error; cleanup */
1267     if (error) g_error_free (error);
1268     g_free(path);
1269     return NULL;
1270 }
1271
1272 static gboolean
1273 control_stdin(GIOChannel *gio, GIOCondition condition) {
1274     (void) condition;
1275     gchar *ctl_line = NULL;
1276     GIOStatus ret;
1277
1278     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1279     if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1280         return FALSE;
1281
1282     parse_cmd_line(ctl_line);
1283     g_free(ctl_line);
1284
1285     return TRUE;
1286 }
1287
1288 static void
1289 create_stdin () {
1290     GIOChannel *chan = NULL;
1291     GError *error = NULL;
1292
1293     chan = g_io_channel_unix_new(fileno(stdin));
1294     if (chan) {
1295         if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1296             g_error ("Stdin: could not add watch\n");
1297         } else {
1298             if (uzbl.state.verbose)
1299                 printf ("Stdin: watch added successfully\n");
1300         }
1301     } else {
1302         g_error ("Stdin: Error while opening: %s\n", error->message);
1303     }
1304     if (error) g_error_free (error);
1305 }
1306
1307 static gboolean
1308 control_socket(GIOChannel *chan) {
1309     struct sockaddr_un remote;
1310     char buffer[512], *ctl_line;
1311     char temp[128];
1312     int sock, clientsock, n, done;
1313     unsigned int t;
1314
1315     sock = g_io_channel_unix_get_fd(chan);
1316
1317     memset (buffer, 0, sizeof (buffer));
1318
1319     t          = sizeof (remote);
1320     clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1321
1322     done = 0;
1323     do {
1324         memset (temp, 0, sizeof (temp));
1325         n = recv (clientsock, temp, 128, 0);
1326         if (n == 0) {
1327             buffer[strlen (buffer)] = '\0';
1328             done = 1;
1329         }
1330         if (!done)
1331             strcat (buffer, temp);
1332     } while (!done);
1333
1334     if (strcmp (buffer, "\n") < 0) {
1335         buffer[strlen (buffer) - 1] = '\0';
1336     } else {
1337         buffer[strlen (buffer)] = '\0';
1338     }
1339     close (clientsock);
1340     ctl_line = g_strdup(buffer);
1341     parse_cmd_line (ctl_line);
1342
1343 /*
1344    TODO: we should be able to do it with this.  but glib errors out with "Invalid argument"
1345     GError *error = NULL;
1346     gsize len;
1347     GIOStatus ret;
1348     ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1349     if (ret == G_IO_STATUS_ERROR)
1350         g_error ("Error reading: %s\n", error->message);
1351
1352     printf("Got line %s (%u bytes) \n",ctl_line, len);
1353     if(ctl_line) {
1354        parse_line(ctl_line);
1355 */
1356
1357     g_free(ctl_line);
1358     return TRUE;
1359 }
1360
1361 static gchar*
1362 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1363     if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1364         if (unlink(uzbl.comm.socket_path) == -1)
1365             g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1366         g_free(uzbl.comm.socket_path);
1367         uzbl.comm.socket_path = NULL;
1368     }
1369
1370     if (*dir == ' ') {
1371         g_free(dir);
1372         return NULL;
1373     }
1374
1375     GIOChannel *chan = NULL;
1376     int sock, len;
1377     struct sockaddr_un local;
1378     gchar *path = build_stream_name(SOCKET, dir);
1379
1380     sock = socket (AF_UNIX, SOCK_STREAM, 0);
1381
1382     local.sun_family = AF_UNIX;
1383     strcpy (local.sun_path, path);
1384     unlink (local.sun_path);
1385
1386     len = strlen (local.sun_path) + sizeof (local.sun_family);
1387     if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1388         if (uzbl.state.verbose)
1389             printf ("init_socket: opened in %s\n", path);
1390         listen (sock, 5);
1391
1392         if( (chan = g_io_channel_unix_new(sock)) ) {
1393             g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1394             uzbl.comm.socket_path = path;
1395             return dir;
1396         }
1397     } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1398
1399     /* if we got this far, there was an error; cleanup */
1400     g_free(path);
1401     g_free(dir);
1402     return NULL;
1403 }
1404
1405 /*
1406  NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1407  it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1408 */
1409 // this function may be called very early when the templates are not set (yet), hence the checks
1410 static void
1411 update_title (void) {
1412     Behaviour *b = &uzbl.behave;
1413     gchar *parsed;
1414
1415     if (b->show_status) {
1416         if (b->title_format_short) {
1417             parsed = expand_template(b->title_format_short);
1418             gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1419             g_free(parsed);
1420         }
1421         if (b->status_format) {
1422             parsed = expand_template(b->status_format);
1423             gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1424             g_free(parsed);
1425         }
1426         if (b->status_background) {
1427             GdkColor color;
1428             gdk_color_parse (b->status_background, &color);
1429             //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)
1430             gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1431         }
1432     } else {
1433         if (b->title_format_long) {
1434             parsed = expand_template(b->title_format_long);
1435             gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1436             g_free(parsed);
1437         }
1438     }
1439 }
1440
1441 static gboolean
1442 key_press_cb (WebKitWebView* page, GdkEventKey* event)
1443 {
1444     //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1445
1446     (void) page;
1447
1448     if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1449         || 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)
1450         return FALSE;
1451
1452     /* turn off insert mode (if always_insert_mode is not used) */
1453     if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1454         uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1455         update_title();
1456         return TRUE;
1457     }
1458
1459     if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1460         return FALSE;
1461
1462     if (event->keyval == GDK_Escape) {
1463         g_string_truncate(uzbl.state.keycmd, 0);
1464         update_title();
1465         return TRUE;
1466     }
1467
1468     //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1469     if (event->keyval == GDK_Insert) {
1470         gchar * str;
1471         if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1472             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1473         } else {
1474             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1475         }
1476         if (str) {
1477             g_string_append (uzbl.state.keycmd, str);
1478             update_title ();
1479             free (str);
1480         }
1481         return TRUE;
1482     }
1483
1484     if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1485         g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1486         update_title();
1487     }
1488
1489     gboolean key_ret = FALSE;
1490     if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1491         key_ret = TRUE;
1492     if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1493
1494     run_keycmd(key_ret);
1495     update_title();
1496     if (key_ret) return (!uzbl.behave.insert_mode);
1497     return TRUE;
1498 }
1499
1500 static void
1501 run_keycmd(const gboolean key_ret) {
1502     /* run the keycmd immediately if it isn't incremental and doesn't take args */
1503     Action *action;
1504     if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1505         g_string_truncate(uzbl.state.keycmd, 0);
1506         parse_command(action->name, action->param);
1507         return;
1508     }
1509
1510     /* try if it's an incremental keycmd or one that takes args, and run it */
1511     GString* short_keys = g_string_new ("");
1512     GString* short_keys_inc = g_string_new ("");
1513     unsigned int i;
1514     for (i=0; i<(uzbl.state.keycmd->len); i++) {
1515         g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1516         g_string_assign(short_keys_inc, short_keys->str);
1517         g_string_append_c(short_keys, '_');
1518         g_string_append_c(short_keys_inc, '*');
1519
1520         gboolean exec_now = FALSE;
1521         if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1522             if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1523         } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1524             if (key_ret) { /* just quit the incremental command on return */
1525                 g_string_truncate(uzbl.state.keycmd, 0);
1526                 break;
1527             } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1528         }
1529
1530         if (exec_now) {
1531             GString* parampart = g_string_new (uzbl.state.keycmd->str);
1532             GString* actionname = g_string_new ("");
1533             GString* actionparam = g_string_new ("");
1534             g_string_erase (parampart, 0, i+1);
1535             if (action->name)
1536                 g_string_printf (actionname, action->name, parampart->str);
1537             if (action->param)
1538                 g_string_printf (actionparam, action->param, parampart->str);
1539             parse_command(actionname->str, actionparam->str);
1540             g_string_free (actionname, TRUE);
1541             g_string_free (actionparam, TRUE);
1542             g_string_free (parampart, TRUE);
1543             if (key_ret)
1544                 g_string_truncate(uzbl.state.keycmd, 0);
1545             break;
1546         }
1547
1548         g_string_truncate(short_keys, short_keys->len - 1);
1549     }
1550     g_string_free (short_keys, TRUE);
1551     g_string_free (short_keys_inc, TRUE);
1552 }
1553
1554 static GtkWidget*
1555 create_browser () {
1556     GUI *g = &uzbl.gui;
1557
1558     GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1559     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
1560
1561     g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1562     gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1563
1564     g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1565     g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1566     g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1567     g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1568     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1569     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1570     g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1571     g_signal_connect (G_OBJECT (g->web_view), "key-press-event", G_CALLBACK (key_press_cb), g->web_view);
1572     g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1573     g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1574     g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1575
1576     return scrolled_window;
1577 }
1578
1579 static GtkWidget*
1580 create_mainbar () {
1581     GUI *g = &uzbl.gui;
1582
1583     g->mainbar = gtk_hbox_new (FALSE, 0);
1584
1585     g->mainbar_label = gtk_label_new ("");
1586     gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1587     gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1588     gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1589     gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1590     gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1591     return g->mainbar;
1592 }
1593
1594 static
1595 GtkWidget* create_window () {
1596     GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1597     gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1598     gtk_widget_set_name (window, "Uzbl browser");
1599     g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1600
1601     return window;
1602 }
1603
1604 static void
1605 run_handler (const gchar *act, const gchar *args) {
1606     char **parts = g_strsplit(act, " ", 2);
1607     if (!parts) return;
1608     else if ((g_strcmp0(parts[0], "spawn") == 0)
1609              || (g_strcmp0(parts[0], "sh") == 0)) {
1610         guint i;
1611         GString *a = g_string_new ("");
1612         char **spawnparts;
1613         spawnparts = split_quoted(parts[1], FALSE);
1614         g_string_append_printf(a, "%s", spawnparts[0]);
1615         if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1616         for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1617             g_string_append_printf(a, " %s", spawnparts[i]);
1618         parse_command(parts[0], a->str);
1619         g_string_free (a, TRUE);
1620         g_strfreev (spawnparts);
1621     } else
1622         parse_command(parts[0], parts[1]);
1623     g_strfreev (parts);
1624 }
1625
1626 static void
1627 add_binding (const gchar *key, const gchar *act) {
1628     char **parts = g_strsplit(act, " ", 2);
1629     Action *action;
1630
1631     if (!parts)
1632         return;
1633
1634     //Debug:
1635     if (uzbl.state.verbose)
1636         printf ("Binding %-10s : %s\n", key, act);
1637
1638     action = new_action(parts[0], parts[1]);
1639     g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
1640
1641     g_strfreev(parts);
1642 }
1643
1644 static gchar*
1645 get_xdg_var (XDG_Var xdg) {
1646     const gchar* actual_value = getenv (xdg.environmental);
1647     const gchar* home         = getenv ("HOME");
1648
1649     gchar* return_value = str_replace ("~", home, actual_value);
1650
1651     if (! actual_value || strcmp (actual_value, "") == 0) {
1652         if (xdg.default_value) {
1653             return_value = str_replace ("~", home, xdg.default_value);
1654         } else {
1655             return_value = NULL;
1656         }
1657     }
1658     return return_value;
1659 }
1660
1661 static gchar*
1662 find_xdg_file (int xdg_type, char* filename) {
1663     /* xdg_type = 0 => config
1664        xdg_type = 1 => data
1665        xdg_type = 2 => cache*/
1666
1667     gchar* temporary_file   = malloc (1024);
1668     gchar* temporary_string = NULL;
1669     char*  saveptr;
1670     char*  buf;
1671
1672     buf = get_xdg_var (XDG[xdg_type]);
1673     strcpy (temporary_file, buf);
1674     strcat (temporary_file, filename);
1675     free(buf);
1676
1677     if (! file_exists (temporary_file) && xdg_type != 2) {
1678         buf = get_xdg_var (XDG[3 + xdg_type]);
1679         temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1680         free(buf);
1681
1682         while (temporary_string && ! file_exists (temporary_file)) {
1683             strcpy (temporary_file, temporary_string);
1684             strcat (temporary_file, filename);
1685             temporary_string = (char * ) strtok_r (NULL, ":", &saveptr);
1686         }
1687     }
1688
1689     if (file_exists (temporary_file)) {
1690         return temporary_file;
1691     } else {
1692         return NULL;
1693     }
1694 }
1695
1696 static void
1697 settings_init () {
1698     State *s = &uzbl.state;
1699     Network *n = &uzbl.net;
1700
1701     uzbl.behave.reset_command_mode = 1;
1702
1703     if (!s->config_file) {
1704         s->config_file = find_xdg_file (0, "/uzbl/config");
1705     }
1706
1707     if (s->config_file) {
1708         GIOChannel *chan = NULL;
1709         gchar *readbuf = NULL;
1710         gsize len;
1711
1712         chan = g_io_channel_new_file(s->config_file, "r", NULL);
1713
1714         if (chan) {
1715             while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL)
1716                     == G_IO_STATUS_NORMAL) {
1717                 parse_cmd_line(readbuf);
1718                 g_free (readbuf);
1719             }
1720
1721             g_io_channel_unref (chan);
1722             if (uzbl.state.verbose)
1723                 printf ("Config %s loaded\n", s->config_file);
1724         } else {
1725             fprintf(stderr, "uzbl: error loading file%s\n", s->config_file);
1726         }
1727     } else {
1728         if (uzbl.state.verbose)
1729             printf ("No configuration file loaded.\n");
1730     }
1731     if (!uzbl.behave.status_format)
1732         set_var_value("status_format", STATUS_DEFAULT);
1733     if (!uzbl.behave.title_format_long)
1734         set_var_value("title_format_long", TITLE_LONG_DEFAULT);
1735     if (!uzbl.behave.title_format_short)
1736         set_var_value("title_format_short", TITLE_SHORT_DEFAULT);
1737
1738
1739     g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1740 }
1741
1742 static gchar*
1743 set_useragent(gchar *val) {
1744     if (*val == ' ') {
1745         g_free(val);
1746         return NULL;
1747     }
1748     gchar *ua = expand_template(val);
1749     if (ua)
1750         g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1751     return ua;
1752 }
1753
1754 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1755     (void) session;
1756     (void) user_data;
1757     if (!uzbl.behave.cookie_handler) return;
1758
1759     gchar * stdout = NULL;
1760     soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1761     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1762     gchar *action = g_strdup ("GET");
1763     SoupURI * soup_uri = soup_message_get_uri(msg);
1764     sharg_append(a, action);
1765     sharg_append(a, soup_uri->host);
1766     sharg_append(a, soup_uri->path);
1767     run_command(uzbl.behave.cookie_handler, 0, a->data, TRUE, &stdout); /* TODO: use handler */
1768     //run_handler(uzbl.behave.cookie_handler); /* TODO: global stdout pointer, spawn_sync */
1769     if(stdout) {
1770         soup_message_headers_replace (msg->request_headers, "Cookie", stdout);
1771     }
1772     g_free (action);
1773     g_array_free(a, TRUE);
1774 }
1775
1776 static void
1777 save_cookies (SoupMessage *msg, gpointer user_data){
1778     (void) user_data;
1779     GSList *ck;
1780     char *cookie;
1781     for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1782         cookie = soup_cookie_to_set_cookie_header(ck->data);
1783         GArray *a = g_array_new(TRUE, FALSE, sizeof(gchar*));
1784         SoupURI * soup_uri = soup_message_get_uri(msg);
1785         gchar *action = strdup("PUT");
1786         sharg_append(a, action);
1787         sharg_append(a, soup_uri->host);
1788         sharg_append(a, soup_uri->path);
1789         sharg_append(a, cookie);
1790         run_command(uzbl.behave.cookie_handler, 0, a->data, FALSE, NULL);
1791         g_free (cookie);
1792         g_free (action);
1793         g_array_free(a, TRUE);
1794     }
1795     g_slist_free(ck);
1796 }
1797
1798
1799 int
1800 main (int argc, char* argv[]) {
1801     gtk_init (&argc, &argv);
1802     if (!g_thread_supported ())
1803         g_thread_init (NULL);
1804
1805     uzbl.state.executable_path = g_strdup(argv[0]);
1806     uzbl.state.selected_url = NULL;
1807     uzbl.state.searchtx = NULL;
1808
1809     GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1810     g_option_context_add_main_entries (context, entries, NULL);
1811     g_option_context_add_group (context, gtk_get_option_group (TRUE));
1812     g_option_context_parse (context, &argc, &argv, NULL);
1813     g_option_context_free(context);
1814     /* initialize hash table */
1815     uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1816
1817     uzbl.net.soup_session = webkit_get_default_session();
1818     uzbl.state.keycmd = g_string_new("");
1819
1820     if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1821         fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1822     if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1823         fprintf(stderr, "uzbl: error hooking SIGINT\n");
1824
1825     if(uname(&uzbl.state.unameinfo) == -1)
1826         g_printerr("Can't retrieve unameinfo.  Your useragent might appear wrong.\n");
1827
1828     setup_regex();
1829     setup_scanner();
1830     commands_hash ();
1831     make_var_to_name_hash();
1832
1833
1834     uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1835
1836     uzbl.gui.scrolled_win = create_browser();
1837     create_mainbar();
1838
1839     /* initial packing */
1840     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1841     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1842
1843     uzbl.gui.main_window = create_window ();
1844     gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
1845
1846
1847     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1848     gtk_widget_show_all (uzbl.gui.main_window);
1849     uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1850
1851     if (uzbl.state.verbose) {
1852         printf("Uzbl start location: %s\n", argv[0]);
1853         printf("window_id %i\n",(int) uzbl.xwin);
1854         printf("pid %i\n", getpid ());
1855         printf("name: %s\n", uzbl.state.instance_name);
1856     }
1857
1858     uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1859     uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1860     uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
1861     uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
1862     gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
1863
1864     settings_init ();
1865
1866     if (!uzbl.behave.show_status)
1867         gtk_widget_hide(uzbl.gui.mainbar);
1868     else
1869         update_title();
1870
1871     create_stdin();
1872
1873     if(uzbl.state.uri) {
1874         GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1875         g_array_append_val(a, uzbl.state.uri);
1876         load_uri (uzbl.gui.web_view, a);
1877         g_array_free (a, TRUE);
1878     }
1879
1880     gtk_main ();
1881     clean_up();
1882
1883     return EXIT_SUCCESS;
1884 }
1885
1886 /* vi: set et ts=4: */