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