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