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