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