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