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