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