Merge commit 'diet/experimental' into experimental
[uzbl-mobile] / uzbl.c
1 /* -*- c-basic-offset: 4; -*- */
2 // Original code taken from the example webkit-gtk+ application. see notice below.
3 // Modified code is licensed under the GPL 3.  See LICENSE file.
4
5
6 /*
7  * Copyright (C) 2006, 2007 Apple Inc.
8  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32
33 #define LENGTH(x) (sizeof x / sizeof x[0])
34 #define MAX_BINDINGS 256
35
36 #include <gtk/gtk.h>
37 #include <gdk/gdkx.h>
38 #include <gdk/gdkkeysyms.h>
39 #include <sys/socket.h>
40 #include <sys/stat.h>
41 #include <sys/types.h>
42 #include <sys/un.h>
43 #include <sys/utsname.h>
44 #include <webkit/webkit.h>
45 #include <stdio.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include <stdlib.h>
49 #include <errno.h>
50 #include <string.h>
51 #include <fcntl.h>
52 #include <sys/socket.h>
53 #include <sys/un.h>
54 #include <libsoup/soup.h>
55 #include <signal.h>
56 #include "uzbl.h"
57
58
59 static Uzbl uzbl;
60
61 /* define names and pointers to all config specific variables */
62 const struct {
63     char *name;
64     void **ptr;
65 } var_name_to_ptr[] = {
66     { "uri",                (void *)&uzbl.state.uri                 },
67     { "status_message",     (void *)&uzbl.gui.sbar.msg              },
68     { "show_status",        (void *)&uzbl.behave.show_status        },
69     { "status_top",         (void *)&uzbl.behave.status_top         },
70     { "status_format",      (void *)&uzbl.behave.status_format      },
71     { "status_background",  (void *)&uzbl.behave.status_background  },
72     { "title_format_long",  (void *)&uzbl.behave.title_format_long  },
73     { "title_format_short", (void *)&uzbl.behave.title_format_short },
74     { "insert_mode",        (void *)&uzbl.behave.insert_mode        },
75     { "always_insert_mode", (void *)&uzbl.behave.always_insert_mode },
76     { "reset_command_mode", (void *)&uzbl.behave.reset_command_mode },
77     { "modkey"     ,        (void *)&uzbl.behave.modkey             },
78     { "load_finish_handler",(void *)&uzbl.behave.load_finish_handler},
79     { "load_start_handler", (void *)&uzbl.behave.load_start_handler },
80     { "load_commit_handler",(void *)&uzbl.behave.load_commit_handler},
81     { "history_handler",    (void *)&uzbl.behave.history_handler    },
82     { "download_handler",   (void *)&uzbl.behave.download_handler   },
83     { "cookie_handler",     (void *)&uzbl.behave.cookie_handler     },
84     { "fifo_dir",           (void *)&uzbl.behave.fifo_dir           },
85     { "socket_dir",         (void *)&uzbl.behave.socket_dir         },
86     { "http_debug",         (void *)&uzbl.behave.http_debug         },
87     { "default_font_size",  (void *)&uzbl.behave.default_font_size  },
88     { "minimum_font_size",  (void *)&uzbl.behave.minimum_font_size  },
89     { "shell_cmd",          (void *)&uzbl.behave.shell_cmd          },
90     { "proxy_url",          (void *)&uzbl.net.proxy_url             },
91     { "max_conns",          (void *)&uzbl.net.max_conns             },
92     { "max_conns_host",     (void *)&uzbl.net.max_conns_host        },
93     { "useragent",          (void *)&uzbl.net.useragent             },
94     { NULL,                 NULL                                    }
95 }, *n2v_p = var_name_to_ptr;
96
97 const struct {
98     char *key;
99     guint mask;
100 } modkeys[] = {
101     { "SHIFT",   GDK_SHIFT_MASK   }, // shift
102     { "LOCK",    GDK_LOCK_MASK    }, // capslock or shiftlock, depending on xserver's modmappings
103     { "CONTROL", GDK_CONTROL_MASK }, // control
104     { "MOD1",    GDK_MOD1_MASK    }, // 4th mod - normally alt but depends on modmappings
105     { "MOD2",    GDK_MOD2_MASK    }, // 5th mod
106     { "MOD3",    GDK_MOD3_MASK    }, // 6th mod
107     { "MOD4",    GDK_MOD4_MASK    }, // 7th mod
108     { "MOD5",    GDK_MOD5_MASK    }, // 8th mod
109     { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
110     { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
111     { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
112     { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
113     { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
114     { "SUPER",   GDK_SUPER_MASK   }, // super (since 2.10)
115     { "HYPER",   GDK_HYPER_MASK   }, // hyper (since 2.10)
116     { "META",    GDK_META_MASK    }, // meta (since 2.10)
117     { NULL,      0                }
118 };
119
120 /* construct a hash from the var_name_to_ptr array for quick access */
121 static void
122 make_var_to_name_hash() {
123     uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
124     while(n2v_p->name) {
125         g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, n2v_p->ptr);
126         n2v_p++;
127     }
128 }
129
130 /* commandline arguments (set initial values for the state variables) */
131 static GOptionEntry entries[] =
132 {
133     { "uri",     'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,           "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
134     { "verbose", 'v', 0, G_OPTION_ARG_NONE,   &uzbl.state.verbose,       "Whether to print all messages or just errors.", NULL },
135     { "name",    'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name, "Name of the current instance (defaults to Xorg window id)", "NAME" },
136     { "config",  'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,   "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
137     { NULL,      0, 0, 0, NULL, NULL, NULL }
138 };
139
140 typedef void (*Command)(WebKitWebView*, const char *);
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 char *
153 str_replace (const char* search, const char* replace, const char* string) {
154     gchar **buf;
155     char *ret;
156
157     buf = g_strsplit (string, search, -1);
158     ret = g_strjoinv (replace, buf);
159     g_strfreev(buf);
160
161     return ret;
162 }
163
164 static sigfunc*
165 setup_signal(int signr, sigfunc *shandler) {
166     struct sigaction nh, oh;
167
168     nh.sa_handler = shandler;
169     sigemptyset(&nh.sa_mask);
170     nh.sa_flags = 0;
171
172     if(sigaction(signr, &nh, &oh) < 0)
173         return SIG_ERR;
174
175     return NULL;
176 }
177
178 static void
179 clean_up(void) {
180     if (uzbl.behave.fifo_dir)
181         unlink (uzbl.comm.fifo_path);
182     if (uzbl.behave.socket_dir)
183         unlink (uzbl.comm.socket_path);
184
185     g_free(uzbl.state.executable_path);
186     g_string_free(uzbl.state.keycmd, TRUE);
187     g_hash_table_destroy(uzbl.bindings);
188     g_hash_table_destroy(uzbl.behave.commands);
189 }
190
191
192 /* --- SIGNAL HANDLER --- */
193
194 static void
195 catch_sigterm(int s) {
196     (void) s;
197     clean_up();
198 }
199
200 static void
201 catch_sigint(int s) {
202     (void) s;
203     clean_up();
204     exit(EXIT_SUCCESS);
205 }
206
207 /* --- CALLBACKS --- */
208
209 static gboolean
210 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
211     (void) web_view;
212     (void) frame;
213     (void) navigation_action;
214     (void) policy_decision;
215     (void) user_data;
216     const gchar* uri = webkit_network_request_get_uri (request);
217     if (uzbl.state.verbose)
218         printf("New window requested -> %s \n", uri);
219     new_window_load_uri(uri);
220     return (FALSE);
221 }
222
223 WebKitWebView*
224 create_web_view_cb (WebKitWebView  *web_view, WebKitWebFrame *frame, gpointer user_data) {
225     (void) web_view;
226     (void) frame;
227     (void) user_data;
228     if (uzbl.state.selected_url != NULL) {
229         if (uzbl.state.verbose)
230             printf("\nNew web view -> %s\n",uzbl.state.selected_url);
231         new_window_load_uri(uzbl.state.selected_url);
232     } else {
233         if (uzbl.state.verbose)
234             printf("New web view -> %s\n","Nothing to open, exiting");
235     }
236     return (NULL);
237 }
238
239 static gboolean
240 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
241     (void) web_view;
242     (void) user_data;
243     if (uzbl.behave.download_handler) {
244         const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
245         if (uzbl.state.verbose)
246             printf("Download -> %s\n",uri);
247         /* if urls not escaped, we may have to escape and quote uri before this call */
248         run_handler(uzbl.behave.download_handler, uri);
249     }
250     return (FALSE);
251 }
252
253 /* scroll a bar in a given direction */
254 static void
255 scroll (GtkAdjustment* bar, const char *param) {
256     gdouble amount;
257     gchar *end;
258
259     amount = g_ascii_strtod(param, &end);
260
261     if (*end)
262         fprintf(stderr, "found something after double: %s\n", end);
263
264     gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
265 }
266
267 static void scroll_begin(WebKitWebView* page, const char *param) {
268     (void) page; (void) param;
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, const char *param) {
273     (void) page; (void) param;
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, const char *param) {
279     (void) page;
280     scroll(uzbl.gui.bar_v, param);
281 }
282
283 static void scroll_horz(WebKitWebView* page, const char *param) {
284     (void) page;
285     scroll(uzbl.gui.bar_h, param);
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, const char *param) {
300     (void)page;
301     (void)param;
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, const char *param){(void)param; 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;} cmdlist[] =
415 {
416     { "back",             view_go_back            },
417     { "forward",          view_go_forward         },
418     { "scroll_vert",      scroll_vert             },
419     { "scroll_horz",      scroll_horz             },
420     { "scroll_begin",     scroll_begin            },
421     { "scroll_end",       scroll_end              },
422     { "reload",           view_reload,            },
423     { "reload_ign_cache", view_reload_bypass_cache},
424     { "stop",             view_stop_loading,      },
425     { "zoom_in",          view_zoom_in,           }, //Can crash (when max zoom reached?).
426     { "zoom_out",         view_zoom_out,          },
427     { "uri",              load_uri                },
428     { "script",           run_js                  },
429     { "toggle_status",    toggle_status_cb        },
430     { "spawn",            spawn                   },
431     { "sh",               spawn_sh                },
432     { "exit",             close_uzbl              },
433     { "search",           search_forward_text     },
434     { "search_reverse",   search_reverse_text     },
435     { "insert_mode",      set_insert_mode         },
436     { "runcmd",           runcmd                  }
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, const gchar *param) {
480     (void)page;
481     (void)param;
482
483     uzbl.behave.insert_mode = TRUE;
484     update_title();
485 }
486
487 static void
488 load_uri (WebKitWebView * web_view, const gchar *param) {
489     if (param) {
490         GString* newuri = g_string_new (param);
491         if (g_strrstr (param, "://") == 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, const gchar *param) {
501     if (param)
502         webkit_web_view_execute_script (web_view, param);
503 }
504
505 static void
506 search_text (WebKitWebView *page, const char *param, const gboolean forward) {
507     if ((param) && (param[0] != '\0')) {
508         uzbl.state.searchtx = g_strdup(param);
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, const char *param) {
524     search_text(page, param, TRUE);
525 }
526
527 static void
528 search_reverse_text (WebKitWebView *page, const char *param) {
529     search_text(page, param, 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, const char *param) {
553     (void)page;
554     (void)param;
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     gboolean quote = FALSE;
826     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
827     GString *s = g_string_new ("");
828     const gchar *p;
829     gchar **ret;
830     gchar *dup;
831     for (p = src; *p != '\0'; p++) {
832         if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
833         else if (*p == '\\') { g_string_append_c(s, *p++);
834                                g_string_append_c(s, *p); }
835         else if ((*p == '"') && unquote) quote = !quote;
836         else if (*p == '"') { g_string_append_c(s, *p);
837                               quote = !quote; }
838         else if ((*p == ' ') && (!quote)) {
839             dup = g_strdup(s->str);
840             g_array_append_val(a, dup);
841             g_string_truncate(s, 0);
842         } else g_string_append_c(s, *p);
843     }
844     dup = g_strdup(s->str);
845     g_array_append_val(a, dup);
846     ret = (gchar**)a->data;
847     g_array_free (a, FALSE);
848     g_string_free (s, FALSE);
849     return ret;
850 }
851
852 static void
853 spawn(WebKitWebView *web_view, const char *param) {
854     (void)web_view;
855     //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
856     gchar **cmd = split_quoted(param, TRUE);
857     if (cmd) run_command(cmd[0], 0, &cmd[1], FALSE, NULL);
858     g_strfreev ((gchar**)cmd);
859 }
860
861 static void
862 spawn_sh(WebKitWebView *web_view, const char *param) {
863     (void)web_view;
864     if (!uzbl.behave.shell_cmd) {
865         g_printerr ("spawn_sh: shell_cmd is not set!\n");
866         return;
867     }
868     
869     guint i;
870     gchar *spacer = g_strdup("");
871     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
872     gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
873     gchar **p = split_quoted(param, TRUE);
874     for (i = 1; i < g_strv_length(cmd); i++)
875         sharg_append(a, cmd[i]);
876     sharg_append(a, p[0]); /* the first param comes right after shell_cmd;
877                               the rest come after default args */
878     sharg_append(a, spacer);
879     for (i = 1; i < g_strv_length(p); i++)
880         sharg_append(a, p[i]);
881     if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, a->data, FALSE, NULL);
882     g_free (spacer);
883     g_strfreev (cmd);
884     g_strfreev (p);
885     g_array_free (a, FALSE);
886 }
887
888 static void
889 parse_command(const char *cmd, const char *param) {
890     Command c;
891
892     if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd)))
893         c(uzbl.gui.web_view, param);
894     else
895         fprintf (stderr, "command \"%s\" not understood. ignoring.\n", cmd);
896 }
897
898 /* command parser */
899 static void
900 setup_regex() {
901     uzbl.comm.get_regex  = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
902             G_REGEX_OPTIMIZE, 0, NULL);
903     uzbl.comm.set_regex  = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
904             G_REGEX_OPTIMIZE, 0, NULL);
905     uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
906             G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
907     uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
908             G_REGEX_OPTIMIZE, 0, NULL);
909     uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
910             G_REGEX_OPTIMIZE, 0, NULL);
911 }
912
913 static gboolean
914 get_var_value(gchar *name) {
915     void **p = NULL;
916
917     if( (p = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
918         if(var_is("uri", name)
919            || var_is("status_message", name)
920            || var_is("status_format", name)
921            || var_is("status_background", name)
922            || var_is("title_format_short", name)
923            || var_is("title_format_long", name)
924            || var_is("modkey", name)
925            || var_is("load_finish_handler", name)
926            || var_is("load_start_handler", name)
927            || var_is("load_commit_handler", name)
928            || var_is("history_handler", name)
929            || var_is("download_handler", name)
930            || var_is("cookie_handler", name)
931            || var_is("fifo_dir", name)
932            || var_is("socket_dir", name)
933            || var_is("shell_cmd", name)
934            || var_is("proxy_url", name)
935            || var_is("useragent", name))
936            {
937             printf("VAR: %s VALUE: %s\n", name, (char *)*p);
938         } else printf("VAR: %s VALUE: %d\n", name, (int)*p);
939     }
940     return TRUE;
941 }
942
943 static void
944 set_proxy_url() {
945     SoupURI *suri;
946
947     if(*uzbl.net.proxy_url == ' '
948        || uzbl.net.proxy_url == NULL) {
949         soup_session_remove_feature_by_type(uzbl.net.soup_session,
950                 (GType) SOUP_SESSION_PROXY_URI);
951     }
952     else {
953         suri = soup_uri_new(uzbl.net.proxy_url);
954         g_object_set(G_OBJECT(uzbl.net.soup_session),
955                 SOUP_SESSION_PROXY_URI,
956                 suri, NULL);
957         soup_uri_free(suri);
958     }
959     return;
960 }
961
962
963 static void
964 move_statusbar() {
965     gtk_widget_ref(uzbl.gui.scrolled_win);
966     gtk_widget_ref(uzbl.gui.mainbar);
967     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
968     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
969
970     if(uzbl.behave.status_top) {
971         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
972         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
973     }
974     else {
975         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
976         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
977     }
978     gtk_widget_unref(uzbl.gui.scrolled_win);
979     gtk_widget_unref(uzbl.gui.mainbar);
980     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
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     char *endp = NULL;
992     char *buf=NULL;
993
994     if( (p = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
995         if(var_is("status_message", name)
996            || var_is("status_background", name)
997            || var_is("status_format", name)
998            || var_is("title_format_long", name)
999            || var_is("title_format_short", name)
1000            || var_is("load_finish_handler", name)
1001            || var_is("load_start_handler", name)
1002            || var_is("load_commit_handler", name)
1003            || var_is("history_handler", name)
1004            || var_is("download_handler", name)
1005            || var_is("cookie_handler", name)) {
1006             if(*p)
1007                 free(*p);
1008             *p = g_strdup(val);
1009             update_title();
1010         }
1011         else if(var_is("uri", name)) {
1012             if(*p) free(*p);
1013             *p = g_strdup(val);
1014             load_uri(uzbl.gui.web_view, (const gchar*)*p);
1015         }
1016         else if(var_is("proxy_url", name)) {
1017             if(*p) free(*p);
1018             *p = g_strdup(val);
1019             set_proxy_url();
1020         }
1021         else if(var_is("fifo_dir", name)) {
1022             if(*p) free(*p);
1023             buf = init_fifo(val);
1024             *p = buf?buf:g_strdup("");
1025         }
1026         else if(var_is("socket_dir", name)) {
1027             if(*p) free(*p);
1028             buf = init_socket(val);
1029             *p = buf?buf:g_strdup("");
1030         }
1031         else if(var_is("modkey", name)) {
1032             if(*p) free(*p);
1033             int i;
1034             *p = g_utf8_strup(val, -1);
1035             uzbl.behave.modmask = 0;
1036             for (i = 0; modkeys[i].key != NULL; i++) {
1037                 if (g_strrstr(*p, modkeys[i].key))
1038                     uzbl.behave.modmask |= modkeys[i].mask;
1039             }
1040         }
1041         else if(var_is("useragent", name)) {
1042             if(*p) free(*p);
1043             buf = set_useragent(val);
1044             *p = buf?buf:g_strdup("");
1045         }
1046         else if(var_is("shell_cmd", name)) {
1047             if(*p) free(*p);
1048             *p = g_strdup(val);
1049         }
1050         /* variables that take int values */
1051         else {
1052             int *ip = (int *)p;
1053             *ip = (int)strtoul(val, &endp, 10);
1054
1055             if(var_is("show_status", name)) {
1056                 cmd_set_status();
1057             }
1058             else if(var_is("always_insert_mode", name)) {
1059                 uzbl.behave.insert_mode =
1060                     uzbl.behave.always_insert_mode ?  TRUE : FALSE;
1061                 update_title();
1062             }
1063             else if (var_is("max_conns", name)) {
1064                 g_object_set(G_OBJECT(uzbl.net.soup_session),
1065                              SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1066             }
1067             else if (var_is("max_conns_host", name)) {
1068                 g_object_set(G_OBJECT(uzbl.net.soup_session),
1069                              SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1070             }
1071             else if (var_is("http_debug", name)) {
1072                 soup_session_remove_feature
1073                     (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1074                 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1075                 /*g_free(uzbl.net.soup_logger);*/
1076
1077                 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1078                 soup_session_add_feature(uzbl.net.soup_session,
1079                                          SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1080             }
1081             else if (var_is("status_top", name)) {
1082                 move_statusbar();
1083             }
1084             else if (var_is("default_font_size", name)) {
1085                 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1086                 g_object_set (G_OBJECT(ws), "default-font-size", *ip, NULL);
1087             }
1088             else if (var_is("minimum_font_size", name)) {
1089                 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1090                 g_object_set (G_OBJECT(ws), "minimum-font-size", *ip, NULL);
1091             }
1092         }
1093     }
1094     return TRUE;
1095 }
1096
1097 static void
1098 runcmd(WebKitWebView* page, const char *param) {
1099     (void) page;
1100     parse_cmd_line(param);
1101 }
1102
1103 static void
1104 parse_cmd_line(const char *ctl_line) {
1105     gchar **tokens;
1106
1107     /* SET command */
1108     if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1109         tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1110         if(tokens[0][0] == 0) {
1111             set_var_value(tokens[1], tokens[2]);
1112             g_strfreev(tokens);
1113         }
1114         else
1115             printf("Error in command: %s\n", tokens[0]);
1116     }
1117     /* GET command */
1118     else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1119         tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1120         if(tokens[0][0] == 0) {
1121             get_var_value(tokens[1]);
1122             g_strfreev(tokens);
1123         }
1124         else
1125             printf("Error in command: %s\n", tokens[0]);
1126     }
1127     /* BIND command */
1128     else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1129         tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1130         if(tokens[0][0] == 0) {
1131             add_binding(tokens[1], tokens[2]);
1132             g_strfreev(tokens);
1133         }
1134         else
1135             printf("Error in command: %s\n", tokens[0]);
1136     }
1137     /* ACT command */
1138     else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1139         tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1140         if(tokens[0][0] == 0) {
1141             parse_command(tokens[1], tokens[2]);
1142             g_strfreev(tokens);
1143         }
1144         else
1145             printf("Error in command: %s\n", tokens[0]);
1146     }
1147     /* KEYCMD command */
1148     else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1149         tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1150         if(tokens[0][0] == 0) {
1151             /* should incremental commands want each individual "keystroke"
1152                sent in a loop or the whole string in one go like now? */
1153             g_string_assign(uzbl.state.keycmd, tokens[1]);
1154             run_keycmd(FALSE);
1155             if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
1156                 run_keycmd(TRUE);
1157             update_title();
1158             g_strfreev(tokens);
1159         }
1160     }
1161     /* Comments */
1162     else if(   (ctl_line[0] == '#')
1163             || (ctl_line[0] == ' ')
1164             || (ctl_line[0] == '\n'))
1165         ; /* ignore these lines */
1166     else
1167         printf("Command not understood (%s)\n", ctl_line);
1168
1169     return;
1170 }
1171
1172 static gchar*
1173 build_stream_name(int type, const gchar* dir) {
1174     char *xwin_str;
1175     State *s = &uzbl.state;
1176     gchar *str;
1177
1178     xwin_str = itos((int)uzbl.xwin);
1179     if (type == FIFO) {
1180         str = g_strdup_printf
1181             ("%s/uzbl_fifo_%s", dir,
1182              s->instance_name ? s->instance_name : xwin_str);
1183     } else if (type == SOCKET) {
1184         str = g_strdup_printf
1185             ("%s/uzbl_socket_%s", dir,
1186              s->instance_name ? s->instance_name : xwin_str );
1187     }
1188     g_free(xwin_str);
1189     return str;
1190 }
1191
1192 static gboolean
1193 control_fifo(GIOChannel *gio, GIOCondition condition) {
1194     if (uzbl.state.verbose)
1195         printf("triggered\n");
1196     gchar *ctl_line;
1197     GIOStatus ret;
1198     GError *err = NULL;
1199
1200     if (condition & G_IO_HUP)
1201         g_error ("Fifo: Read end of pipe died!\n");
1202
1203     if(!gio)
1204        g_error ("Fifo: GIOChannel broke\n");
1205
1206     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1207     if (ret == G_IO_STATUS_ERROR) {
1208         g_error ("Fifo: Error reading: %s\n", err->message);
1209         g_error_free (err);
1210     }
1211
1212     parse_cmd_line(ctl_line);
1213     g_free(ctl_line);
1214
1215     return TRUE;
1216 }
1217
1218 static gchar*
1219 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1220     if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1221         if (unlink(uzbl.comm.fifo_path) == -1)
1222             g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1223         g_free(uzbl.comm.fifo_path);
1224         uzbl.comm.fifo_path = NULL;
1225     }
1226
1227     if (*dir == ' ') { /* space unsets the variable */
1228         return NULL;
1229     }
1230
1231     GIOChannel *chan = NULL;
1232     GError *error = NULL;
1233     gchar *path = build_stream_name(FIFO, dir);
1234
1235     if (!file_exists(path)) {
1236         if (mkfifo (path, 0666) == 0) {
1237             // 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.
1238             chan = g_io_channel_new_file(path, "r+", &error);
1239             if (chan) {
1240                 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1241                     if (uzbl.state.verbose)
1242                         printf ("init_fifo: created successfully as %s\n", path);
1243                     uzbl.comm.fifo_path = path;
1244                     return dir;
1245                 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1246             } else g_warning ("init_fifo: can't open: %s\n", error->message);
1247         } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1248     } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1249
1250     /* if we got this far, there was an error; cleanup */
1251     if (error) g_error_free (error);
1252     g_free(path);
1253     return NULL;
1254 }
1255
1256 static gboolean
1257 control_stdin(GIOChannel *gio, GIOCondition condition) {
1258     (void) condition;
1259     gchar *ctl_line = NULL;
1260     GIOStatus ret;
1261
1262     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1263     if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1264         return FALSE;
1265
1266     parse_cmd_line(ctl_line);
1267     g_free(ctl_line);
1268
1269     return TRUE;
1270 }
1271
1272 static void
1273 create_stdin () {
1274     GIOChannel *chan = NULL;
1275     GError *error = NULL;
1276
1277     chan = g_io_channel_unix_new(fileno(stdin));
1278     if (chan) {
1279         if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1280             g_error ("Stdin: could not add watch\n");
1281         } else {
1282             if (uzbl.state.verbose)
1283                 printf ("Stdin: watch added successfully\n");
1284         }
1285     } else {
1286         g_error ("Stdin: Error while opening: %s\n", error->message);
1287     }
1288     if (error) g_error_free (error);
1289 }
1290
1291 static gboolean
1292 control_socket(GIOChannel *chan) {
1293     struct sockaddr_un remote;
1294     char buffer[512], *ctl_line;
1295     char temp[128];
1296     int sock, clientsock, n, done;
1297     unsigned int t;
1298
1299     sock = g_io_channel_unix_get_fd(chan);
1300
1301     memset (buffer, 0, sizeof (buffer));
1302
1303     t          = sizeof (remote);
1304     clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1305
1306     done = 0;
1307     do {
1308         memset (temp, 0, sizeof (temp));
1309         n = recv (clientsock, temp, 128, 0);
1310         if (n == 0) {
1311             buffer[strlen (buffer)] = '\0';
1312             done = 1;
1313         }
1314         if (!done)
1315             strcat (buffer, temp);
1316     } while (!done);
1317
1318     if (strcmp (buffer, "\n") < 0) {
1319         buffer[strlen (buffer) - 1] = '\0';
1320     } else {
1321         buffer[strlen (buffer)] = '\0';
1322     }
1323     close (clientsock);
1324     ctl_line = g_strdup(buffer);
1325     parse_cmd_line (ctl_line);
1326
1327 /*
1328    TODO: we should be able to do it with this.  but glib errors out with "Invalid argument"
1329     GError *error = NULL;
1330     gsize len;
1331     GIOStatus ret;
1332     ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1333     if (ret == G_IO_STATUS_ERROR)
1334         g_error ("Error reading: %s\n", error->message);
1335
1336     printf("Got line %s (%u bytes) \n",ctl_line, len);
1337     if(ctl_line) {
1338        parse_line(ctl_line);
1339 */
1340
1341     g_free(ctl_line);
1342     return TRUE;
1343 }
1344
1345 static gchar*
1346 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1347     if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1348         if (unlink(uzbl.comm.socket_path) == -1)
1349             g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1350         g_free(uzbl.comm.socket_path);
1351         uzbl.comm.socket_path = NULL;
1352     }
1353
1354     if (*dir == ' ') {
1355         g_free(dir);
1356         return NULL;
1357     }
1358
1359     GIOChannel *chan = NULL;
1360     int sock, len;
1361     struct sockaddr_un local;
1362     gchar *path = build_stream_name(SOCKET, dir);
1363
1364     sock = socket (AF_UNIX, SOCK_STREAM, 0);
1365
1366     local.sun_family = AF_UNIX;
1367     strcpy (local.sun_path, path);
1368     unlink (local.sun_path);
1369
1370     len = strlen (local.sun_path) + sizeof (local.sun_family);
1371     if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1372         if (uzbl.state.verbose)
1373             printf ("init_socket: opened in %s\n", path);
1374         listen (sock, 5);
1375
1376         if( (chan = g_io_channel_unix_new(sock)) ) {
1377             g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1378             uzbl.comm.socket_path = path;
1379             return dir;
1380         }
1381     } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1382
1383     /* if we got this far, there was an error; cleanup */
1384     g_free(path);
1385     g_free(dir);
1386     return NULL;
1387 }
1388
1389 /*
1390  NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1391  it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1392 */
1393 // this function may be called very early when the templates are not set (yet), hence the checks
1394 static void
1395 update_title (void) {
1396     Behaviour *b = &uzbl.behave;
1397     gchar *parsed;
1398
1399     if (b->show_status) {
1400         if (b->title_format_short) {
1401             parsed = expand_template(b->title_format_short);
1402             gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1403             g_free(parsed);
1404         }
1405         if (b->status_format) {
1406             parsed = expand_template(b->status_format);
1407             gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1408             g_free(parsed);
1409         }
1410         if (b->status_background) {
1411             GdkColor color;
1412             gdk_color_parse (b->status_background, &color);
1413             //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)
1414             gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1415         }
1416     } else {
1417         if (b->title_format_long) {
1418             parsed = expand_template(b->title_format_long);
1419             gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1420             g_free(parsed);
1421         }
1422     }
1423 }
1424
1425 static gboolean
1426 key_press_cb (WebKitWebView* page, GdkEventKey* event)
1427 {
1428     //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1429
1430     (void) page;
1431
1432     if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1433         || 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)
1434         return FALSE;
1435
1436     /* turn off insert mode (if always_insert_mode is not used) */
1437     if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1438         uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1439         update_title();
1440         return TRUE;
1441     }
1442
1443     if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1444         return FALSE;
1445
1446     if (event->keyval == GDK_Escape) {
1447         g_string_truncate(uzbl.state.keycmd, 0);
1448         update_title();
1449         return TRUE;
1450     }
1451
1452     //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1453     if (event->keyval == GDK_Insert) {
1454         gchar * str;
1455         if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1456             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1457         } else {
1458             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1459         }
1460         if (str) {
1461             g_string_append (uzbl.state.keycmd, str);
1462             update_title ();
1463             free (str);
1464         }
1465         return TRUE;
1466     }
1467
1468     if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1469         g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1470         update_title();
1471     }
1472
1473     gboolean key_ret = FALSE;
1474     if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1475         key_ret = TRUE;
1476     if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1477
1478     run_keycmd(key_ret);
1479     update_title();
1480     if (key_ret) return (!uzbl.behave.insert_mode);
1481     return TRUE;
1482 }
1483
1484 static void
1485 run_keycmd(const gboolean key_ret) {
1486     /* run the keycmd immediately if it isn't incremental and doesn't take args */
1487     Action *action;
1488     if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1489         g_string_truncate(uzbl.state.keycmd, 0);
1490         parse_command(action->name, action->param);
1491         return;
1492     }
1493
1494     /* try if it's an incremental keycmd or one that takes args, and run it */
1495     GString* short_keys = g_string_new ("");
1496     GString* short_keys_inc = g_string_new ("");
1497     unsigned int i;
1498     for (i=0; i<(uzbl.state.keycmd->len); i++) {
1499         g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1500         g_string_assign(short_keys_inc, short_keys->str);
1501         g_string_append_c(short_keys, '_');
1502         g_string_append_c(short_keys_inc, '*');
1503
1504         gboolean exec_now = FALSE;
1505         if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1506             if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1507         } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1508             if (key_ret) { /* just quit the incremental command on return */
1509                 g_string_truncate(uzbl.state.keycmd, 0);
1510                 break;
1511             } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1512         }
1513
1514         if (exec_now) {
1515             GString* parampart = g_string_new (uzbl.state.keycmd->str);
1516             GString* actionname = g_string_new ("");
1517             GString* actionparam = g_string_new ("");
1518             g_string_erase (parampart, 0, i+1);
1519             if (action->name)
1520                 g_string_printf (actionname, action->name, parampart->str);
1521             if (action->param)
1522                 g_string_printf (actionparam, action->param, parampart->str);
1523             parse_command(actionname->str, actionparam->str);
1524             g_string_free (actionname, TRUE);
1525             g_string_free (actionparam, TRUE);
1526             g_string_free (parampart, TRUE);
1527             if (key_ret)
1528                 g_string_truncate(uzbl.state.keycmd, 0);
1529             break;
1530         }
1531
1532         g_string_truncate(short_keys, short_keys->len - 1);
1533     }
1534     g_string_free (short_keys, TRUE);
1535     g_string_free (short_keys_inc, TRUE);
1536 }
1537
1538 static GtkWidget*
1539 create_browser () {
1540     GUI *g = &uzbl.gui;
1541
1542     GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1543     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
1544
1545     g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1546     gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1547
1548     g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1549     g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1550     g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1551     g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1552     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1553     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1554     g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1555     g_signal_connect (G_OBJECT (g->web_view), "key-press-event", G_CALLBACK (key_press_cb), g->web_view);
1556     g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1557     g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1558     g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1559
1560     return scrolled_window;
1561 }
1562
1563 static GtkWidget*
1564 create_mainbar () {
1565     GUI *g = &uzbl.gui;
1566
1567     g->mainbar = gtk_hbox_new (FALSE, 0);
1568
1569     g->mainbar_label = gtk_label_new ("");
1570     gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1571     gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1572     gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1573     gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1574     gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1575     return g->mainbar;
1576 }
1577
1578 static
1579 GtkWidget* create_window () {
1580     GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1581     gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1582     gtk_widget_set_name (window, "Uzbl browser");
1583     g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1584
1585     return window;
1586 }
1587
1588 static void
1589 run_handler (const gchar *act, const gchar *args) {
1590     char **parts = g_strsplit(act, " ", 2);
1591     if (!parts) return;
1592     else if ((g_strcmp0(parts[0], "spawn") == 0)
1593              || (g_strcmp0(parts[0], "sh") == 0)) {
1594         guint i;
1595         GString *a = g_string_new ("");
1596         char **spawnparts;
1597         spawnparts = split_quoted(parts[1], FALSE);
1598         g_string_append_printf(a, "%s", spawnparts[0]);
1599         if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1600         for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1601             g_string_append_printf(a, " %s", spawnparts[i]);
1602         parse_command(parts[0], a->str);
1603         g_string_free (a, TRUE);
1604         g_strfreev (spawnparts);
1605     } else
1606         parse_command(parts[0], parts[1]);
1607     g_strfreev (parts);
1608 }
1609
1610 static void
1611 add_binding (const gchar *key, const gchar *act) {
1612     char **parts = g_strsplit(act, " ", 2);
1613     Action *action;
1614
1615     if (!parts)
1616         return;
1617
1618     //Debug:
1619     if (uzbl.state.verbose)
1620         printf ("Binding %-10s : %s\n", key, act);
1621
1622     action = new_action(parts[0], parts[1]);
1623     g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
1624
1625     g_strfreev(parts);
1626 }
1627
1628 static gchar*
1629 get_xdg_var (XDG_Var xdg) {
1630     const gchar* actual_value = getenv (xdg.environmental);
1631     const gchar* home         = getenv ("HOME");
1632
1633     gchar* return_value = str_replace ("~", home, actual_value);
1634
1635     if (! actual_value || strcmp (actual_value, "") == 0) {
1636         if (xdg.default_value) {
1637             return_value = str_replace ("~", home, xdg.default_value);
1638         } else {
1639             return_value = NULL;
1640         }
1641     }
1642     return return_value;
1643 }
1644
1645 static gchar*
1646 find_xdg_file (int xdg_type, char* filename) {
1647     /* xdg_type = 0 => config
1648        xdg_type = 1 => data
1649        xdg_type = 2 => cache*/
1650
1651     gchar* temporary_file   = malloc (1024);
1652     gchar* temporary_string = NULL;
1653     char*  saveptr;
1654     char*  buf;
1655
1656     buf = get_xdg_var (XDG[xdg_type]);
1657     strcpy (temporary_file, buf);
1658     strcat (temporary_file, filename);
1659     free(buf);
1660
1661     if (! file_exists (temporary_file) && xdg_type != 2) {
1662         buf = get_xdg_var (XDG[3 + xdg_type]);
1663         temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1664         free(buf);
1665
1666         while (temporary_string && ! file_exists (temporary_file)) {
1667             strcpy (temporary_file, temporary_string);
1668             strcat (temporary_file, filename);
1669             temporary_string = (char * ) strtok_r (NULL, ":", &saveptr);
1670         }
1671     }
1672
1673     if (file_exists (temporary_file)) {
1674         return temporary_file;
1675     } else {
1676         return NULL;
1677     }
1678 }
1679
1680 static void
1681 settings_init () {
1682     State *s = &uzbl.state;
1683     Network *n = &uzbl.net;
1684
1685     uzbl.behave.reset_command_mode = 1;
1686
1687     if (!s->config_file) {
1688         s->config_file = find_xdg_file (0, "/uzbl/config");
1689     }
1690
1691     if (s->config_file) {
1692         GIOChannel *chan = NULL;
1693         gchar *readbuf = NULL;
1694         gsize len;
1695
1696         chan = g_io_channel_new_file(s->config_file, "r", NULL);
1697
1698         if (chan) {
1699             while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL)
1700                     == G_IO_STATUS_NORMAL) {
1701                 parse_cmd_line(readbuf);
1702                 g_free (readbuf);
1703             }
1704
1705             g_io_channel_unref (chan);
1706             if (uzbl.state.verbose)
1707                 printf ("Config %s loaded\n", s->config_file);
1708         } else {
1709             fprintf(stderr, "uzbl: error loading file%s\n", s->config_file);
1710         }
1711     } else {
1712         if (uzbl.state.verbose)
1713             printf ("No configuration file loaded.\n");
1714     }
1715     if (!uzbl.behave.status_format)
1716         set_var_value("status_format", STATUS_DEFAULT);
1717     if (!uzbl.behave.title_format_long)
1718         set_var_value("title_format_long", TITLE_LONG_DEFAULT);
1719     if (!uzbl.behave.title_format_short)
1720         set_var_value("title_format_short", TITLE_SHORT_DEFAULT);
1721
1722
1723     g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1724 }
1725
1726 static gchar*
1727 set_useragent(gchar *val) {
1728     if (*val == ' ') {
1729         g_free(val);
1730         return NULL;
1731     }
1732     gchar *ua = expand_template(val);
1733     if (ua)
1734         g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1735     return ua;
1736 }
1737
1738 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1739     (void) session;
1740     (void) user_data;
1741     if (!uzbl.behave.cookie_handler) return;
1742
1743     gchar * stdout = NULL;
1744     soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1745     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1746     gchar *action = g_strdup ("GET");
1747     SoupURI * soup_uri = soup_message_get_uri(msg);
1748     sharg_append(a, action);
1749     sharg_append(a, soup_uri->host);
1750     sharg_append(a, soup_uri->path);
1751     run_command(uzbl.behave.cookie_handler, 0, a->data, TRUE, &stdout); /* TODO: use handler */
1752     //run_handler(uzbl.behave.cookie_handler); /* TODO: global stdout pointer, spawn_sync */
1753     if(stdout) {
1754         soup_message_headers_replace (msg->request_headers, "Cookie", stdout);
1755     }
1756     g_free (action);
1757     g_array_free(a, TRUE);
1758 }
1759
1760 static void
1761 save_cookies (SoupMessage *msg, gpointer user_data){
1762     (void) user_data;
1763     GSList *ck;
1764     char *cookie;
1765     for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1766         cookie = soup_cookie_to_set_cookie_header(ck->data);
1767         GArray *a = g_array_new(TRUE, FALSE, sizeof(gchar*));
1768         SoupURI * soup_uri = soup_message_get_uri(msg);
1769         gchar *action = strdup("PUT");
1770         sharg_append(a, action);
1771         sharg_append(a, soup_uri->host);
1772         sharg_append(a, soup_uri->path);
1773         sharg_append(a, cookie);
1774         run_command(uzbl.behave.cookie_handler, 0, a->data, FALSE, NULL);
1775         g_free (cookie);
1776         g_free (action);
1777         g_array_free(a, TRUE);
1778     }
1779     g_slist_free(ck);
1780 }
1781
1782
1783 int
1784 main (int argc, char* argv[]) {
1785     gtk_init (&argc, &argv);
1786     if (!g_thread_supported ())
1787         g_thread_init (NULL);
1788
1789     uzbl.state.executable_path = g_strdup(argv[0]);
1790     uzbl.state.selected_url = NULL;
1791     uzbl.state.searchtx = NULL;
1792
1793     GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1794     g_option_context_add_main_entries (context, entries, NULL);
1795     g_option_context_add_group (context, gtk_get_option_group (TRUE));
1796     g_option_context_parse (context, &argc, &argv, NULL);
1797     g_option_context_free(context);
1798     /* initialize hash table */
1799     uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1800
1801     uzbl.net.soup_session = webkit_get_default_session();
1802     uzbl.state.keycmd = g_string_new("");
1803
1804     if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1805         fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1806     if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1807         fprintf(stderr, "uzbl: error hooking SIGINT\n");
1808
1809     if(uname(&uzbl.state.unameinfo) == -1)
1810         g_printerr("Can't retrieve unameinfo.  Your useragent might appear wrong.\n");
1811
1812     setup_regex();
1813     setup_scanner();
1814     commands_hash ();
1815     make_var_to_name_hash();
1816
1817
1818     uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1819
1820     uzbl.gui.scrolled_win = create_browser();
1821     create_mainbar();
1822
1823     /* initial packing */
1824     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1825     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1826
1827     uzbl.gui.main_window = create_window ();
1828     gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
1829
1830
1831     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1832     gtk_widget_show_all (uzbl.gui.main_window);
1833     uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1834
1835     if (uzbl.state.verbose) {
1836         printf("Uzbl start location: %s\n", argv[0]);
1837         printf("window_id %i\n",(int) uzbl.xwin);
1838         printf("pid %i\n", getpid ());
1839         printf("name: %s\n", uzbl.state.instance_name);
1840     }
1841
1842     uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1843     uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1844     uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
1845     uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
1846     gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
1847
1848     settings_init ();
1849
1850     if (!uzbl.behave.show_status)
1851         gtk_widget_hide(uzbl.gui.mainbar);
1852     else
1853         update_title();
1854
1855     create_stdin();
1856
1857     if(uzbl.state.uri)
1858         load_uri (uzbl.gui.web_view, uzbl.state.uri);
1859
1860
1861     gtk_main ();
1862     clean_up();
1863
1864     return EXIT_SUCCESS;
1865 }
1866
1867 /* vi: set et ts=4: */