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