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