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