removing more static keywords
[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 #define _POSIX_SOURCE
36
37 #include <gtk/gtk.h>
38 #include <gdk/gdkx.h>
39 #include <gdk/gdkkeysyms.h>
40 #include <sys/socket.h>
41 #include <sys/stat.h>
42 #include <sys/types.h>
43 #include <sys/un.h>
44 #include <sys/utsname.h>
45 #include <sys/time.h>
46 #include <webkit/webkit.h>
47 #include <libsoup/soup.h>
48 #include <JavaScriptCore/JavaScript.h>
49
50 #include <stdio.h>
51 #include <string.h>
52 #include <unistd.h>
53 #include <stdlib.h>
54 #include <errno.h>
55 #include <fcntl.h>
56 #include <signal.h>
57 #include "uzbl.h"
58 #include "config.h"
59
60 Uzbl uzbl;
61
62 /* commandline arguments (set initial values for the state variables) */
63 const 
64 GOptionEntry entries[] =
65 {
66     { "uri",     'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
67         "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
68     { "verbose", 'v', 0, G_OPTION_ARG_NONE,   &uzbl.state.verbose,
69         "Whether to print all messages or just errors.", NULL },
70     { "name",    'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name, 
71         "Name of the current instance (defaults to Xorg window id)", "NAME" },
72     { "config",  'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,   
73         "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
74     { "socket",  's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,   
75         "Socket ID", "SOCKET" },
76     { NULL,      0, 0, 0, NULL, NULL, NULL }
77 };
78
79 /* associate command names to their properties */
80 typedef const struct {
81     /* TODO: Make this ambiguous void **ptr into a union { char *char_p; int *int_p; float *float_p; } val;
82              the PTR() macro is kind of preventing this change at the moment. */
83     void **ptr;
84     int type;
85     int dump;
86     void (*func)(void);
87 } uzbl_cmdprop;
88
89 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
90
91 /* an abbreviation to help keep the table's width humane */
92 #define PTR(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .func = fun }
93
94 const struct {
95     char *name;
96     uzbl_cmdprop cp;
97 } var_name_to_ptr[] = {
98 /*    variable name         pointer to variable in code          type  dump callback function    */
99 /*  --------------------------------------------------------------------------------------- */
100     { "uri",                 PTR(uzbl.state.uri,                  STR,  1,   cmd_load_uri)},
101     { "verbose",             PTR(uzbl.state.verbose,              INT,  1,   NULL)},
102     { "mode",                PTR(uzbl.behave.mode,                INT,  0,   NULL)},
103     { "inject_html",         PTR(uzbl.behave.inject_html,         STR,  0,   cmd_inject_html)},
104     { "base_url",            PTR(uzbl.behave.base_url,            STR,  1,   NULL)},
105     { "html_endmarker",      PTR(uzbl.behave.html_endmarker,      STR,  1,   NULL)},
106     { "html_mode_timeout",   PTR(uzbl.behave.html_timeout,        INT,  1,   NULL)},
107     { "status_message",      PTR(uzbl.gui.sbar.msg,               STR,  1,   update_title)},
108     { "show_status",         PTR(uzbl.behave.show_status,         INT,  1,   cmd_set_status)},
109     { "status_top",          PTR(uzbl.behave.status_top,          INT,  1,   move_statusbar)},
110     { "status_format",       PTR(uzbl.behave.status_format,       STR,  1,   update_title)},
111     { "status_pbar_done",    PTR(uzbl.gui.sbar.progress_s,        STR,  1,   update_title)},
112     { "status_pbar_pending", PTR(uzbl.gui.sbar.progress_u,        STR,  1,   update_title)},
113     { "status_pbar_width",   PTR(uzbl.gui.sbar.progress_w,        INT,  1,   update_title)},
114     { "status_background",   PTR(uzbl.behave.status_background,   STR,  1,   update_title)},
115     { "insert_indicator",    PTR(uzbl.behave.insert_indicator,    STR,  1,   update_title)},
116     { "command_indicator",   PTR(uzbl.behave.cmd_indicator,       STR,  1,   update_title)},
117     { "title_format_long",   PTR(uzbl.behave.title_format_long,   STR,  1,   update_title)},
118     { "title_format_short",  PTR(uzbl.behave.title_format_short,  STR,  1,   update_title)},
119     { "icon",                PTR(uzbl.gui.icon,                   STR,  1,   set_icon)},
120     { "insert_mode",         PTR(uzbl.behave.insert_mode,         INT,  1,   NULL)},
121     { "always_insert_mode",  PTR(uzbl.behave.always_insert_mode,  INT,  1,   cmd_always_insert_mode)},
122     { "reset_command_mode",  PTR(uzbl.behave.reset_command_mode,  INT,  1,   NULL)},
123     { "modkey",              PTR(uzbl.behave.modkey,              STR,  1,   cmd_modkey)},
124     { "load_finish_handler", PTR(uzbl.behave.load_finish_handler, STR,  1,   NULL)},
125     { "load_start_handler",  PTR(uzbl.behave.load_start_handler,  STR,  1,   NULL)},
126     { "load_commit_handler", PTR(uzbl.behave.load_commit_handler, STR,  1,   NULL)},
127     { "history_handler",     PTR(uzbl.behave.history_handler,     STR,  1,   NULL)},
128     { "download_handler",    PTR(uzbl.behave.download_handler,    STR,  1,   NULL)},
129     { "cookie_handler",      PTR(uzbl.behave.cookie_handler,      STR,  1,   cmd_cookie_handler)},
130     { "fifo_dir",            PTR(uzbl.behave.fifo_dir,            STR,  1,   cmd_fifo_dir)},
131     { "socket_dir",          PTR(uzbl.behave.socket_dir,          STR,  1,   cmd_socket_dir)},
132     { "http_debug",          PTR(uzbl.behave.http_debug,          INT,  1,   cmd_http_debug)},
133     { "shell_cmd",           PTR(uzbl.behave.shell_cmd,           STR,  1,   NULL)},
134     { "proxy_url",           PTR(uzbl.net.proxy_url,              STR,  1,   set_proxy_url)},
135     { "max_conns",           PTR(uzbl.net.max_conns,              INT,  1,   cmd_max_conns)},
136     { "max_conns_host",      PTR(uzbl.net.max_conns_host,         INT,  1,   cmd_max_conns_host)},
137     { "useragent",           PTR(uzbl.net.useragent,              STR,  1,   cmd_useragent)},
138     /* exported WebKitWebSettings properties */
139     { "zoom_level",          PTR(uzbl.behave.zoom_level,          FLOAT,1,   cmd_zoom_level)},
140     { "font_size",           PTR(uzbl.behave.font_size,           INT,  1,   cmd_font_size)},
141     { "monospace_size",      PTR(uzbl.behave.monospace_size,      INT,  1,   cmd_font_size)},
142     { "minimum_font_size",   PTR(uzbl.behave.minimum_font_size,   INT,  1,   cmd_minimum_font_size)},
143     { "disable_plugins",     PTR(uzbl.behave.disable_plugins,     INT,  1,   cmd_disable_plugins)},
144     { "disable_scripts",     PTR(uzbl.behave.disable_scripts,     INT,  1,   cmd_disable_scripts)},
145     { "autoload_images",     PTR(uzbl.behave.autoload_img,        INT,  1,   cmd_autoload_img)},
146     { "autoshrink_images",   PTR(uzbl.behave.autoshrink_img,      INT,  1,   cmd_autoshrink_img)},
147     { "enable_spellcheck",   PTR(uzbl.behave.enable_spellcheck,   INT,  1,   cmd_enable_spellcheck)},
148     { "enable_private",      PTR(uzbl.behave.enable_private,      INT,  1,   cmd_enable_private)},
149     { "print_backgrounds",   PTR(uzbl.behave.print_bg,            INT,  1,   cmd_print_bg)},
150     { "stylesheet_uri",      PTR(uzbl.behave.style_uri,           STR,  1,   cmd_style_uri)},
151     { "resizable_text_areas",PTR(uzbl.behave.resizable_txt,       INT,  1,   cmd_resizable_txt)},
152     { "default_encoding",    PTR(uzbl.behave.default_encoding,    STR,  1,   cmd_default_encoding)},
153     { "enforce_96_dpi",      PTR(uzbl.behave.enforce_96dpi,       INT,  1,   cmd_enforce_96dpi)},
154     { "caret_browsing",      PTR(uzbl.behave.caret_browsing,      INT,  1,   cmd_caret_browsing)},
155
156     { NULL,                  {.ptr = NULL, .type = TYPE_INT, .dump = 0, .func = NULL}}
157 }, *n2v_p = var_name_to_ptr;
158
159 const struct {
160     char *key;
161     guint mask;
162 } modkeys[] = {
163     { "SHIFT",   GDK_SHIFT_MASK   }, // shift
164     { "LOCK",    GDK_LOCK_MASK    }, // capslock or shiftlock, depending on xserver's modmappings
165     { "CONTROL", GDK_CONTROL_MASK }, // control
166     { "MOD1",    GDK_MOD1_MASK    }, // 4th mod - normally alt but depends on modmappings
167     { "MOD2",    GDK_MOD2_MASK    }, // 5th mod
168     { "MOD3",    GDK_MOD3_MASK    }, // 6th mod
169     { "MOD4",    GDK_MOD4_MASK    }, // 7th mod
170     { "MOD5",    GDK_MOD5_MASK    }, // 8th mod
171     { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
172     { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
173     { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
174     { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
175     { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
176     { "SUPER",   GDK_SUPER_MASK   }, // super (since 2.10)
177     { "HYPER",   GDK_HYPER_MASK   }, // hyper (since 2.10)
178     { "META",    GDK_META_MASK    }, // meta (since 2.10)
179     { NULL,      0                }
180 };
181
182
183 /* construct a hash from the var_name_to_ptr array for quick access */
184 void
185 make_var_to_name_hash() {
186     uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
187     while(n2v_p->name) {
188         g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
189         n2v_p++;
190     }
191 }
192
193 /* --- UTILITY FUNCTIONS --- */
194 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS};
195 guint
196 get_exp_type(gchar *s) {
197     /* variables */
198     if(*(s+1) == '(')
199         return EXP_EXPR;
200     else if(*(s+1) == '{')
201         return EXP_BRACED_VAR;
202     else if(*(s+1) == '<')
203         return EXP_JS;
204     else
205         return EXP_SIMPLE_VAR;
206
207 return EXP_ERR;
208 }
209
210 /* 
211  * recurse == 1: don't expand '@(command)@'
212  * recurse == 2: don't expand '@<java script>@'
213  */
214 gchar *
215 expand(char *s, guint recurse) {
216     uzbl_cmdprop *c;
217     guint etype;
218     char upto = ' ';
219     char *end_simple_var = "^°!\"§$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]¹²³¼½";
220     char str_end[2];
221     char ret[4096];
222     char *vend;
223     GError *err = NULL;
224     gchar *cmd_stdout = NULL;
225     gchar *mycmd = NULL;
226     GString *buf = g_string_new("");
227     GString *js_ret = g_string_new("");
228
229     while(*s) {
230         switch(*s) {
231             case '\\':
232                 g_string_append_c(buf, *++s);
233                 s++;
234                 break;
235
236             case '@':
237                 etype = get_exp_type(s);
238                 s++;
239
240                 switch(etype) {
241                     case EXP_SIMPLE_VAR:
242                         if( (vend = strpbrk(s, end_simple_var)) ||
243                             (vend = strchr(s, '\0')) ) {
244                             strncpy(ret, s, vend-s);
245                             ret[vend-s] = '\0';
246                         }
247                         break;
248                     case EXP_BRACED_VAR:
249                         s++; upto = '}';
250                         if( (vend = strchr(s, upto)) ||
251                             (vend = strchr(s, '\0')) ) {
252                             strncpy(ret, s, vend-s);
253                             ret[vend-s] = '\0';
254                         }
255                         break;
256                     case EXP_EXPR:
257                         s++;
258                         strcpy(str_end, ")@");
259                         str_end[2] = '\0';
260                         if( (vend = strstr(s, str_end)) ||
261                             (vend = strchr(s, '\0')) ) {
262                             strncpy(ret, s, vend-s);
263                             ret[vend-s] = '\0';
264                         }
265                         break;
266                     case EXP_JS: 
267                         s++;
268                         strcpy(str_end, ">@");
269                         str_end[2] = '\0';
270                         if( (vend = strstr(s, str_end)) ||
271                             (vend = strchr(s, '\0')) ) {
272                             strncpy(ret, s, vend-s);
273                             ret[vend-s] = '\0';
274                         }
275                         break;
276                 }
277
278                 if(etype == EXP_SIMPLE_VAR || 
279                    etype == EXP_BRACED_VAR) {
280                     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
281                         if(c->type == TYPE_STR)
282                             g_string_append(buf, (gchar *)*c->ptr);
283                         else if(c->type == TYPE_INT) {
284                             g_string_append_printf(buf, "%d", (int)*c->ptr);
285                         }
286                         else if(c->type == TYPE_FLOAT) {
287                             g_string_append_printf(buf, "%f", *(float *)c->ptr);
288                         }
289                     }
290                     if(etype == EXP_SIMPLE_VAR)
291                         s = vend;
292                     else
293                         s = vend+1;
294                 }
295                 else if(recurse != 1 && 
296                         etype == EXP_EXPR) {
297                     mycmd = expand(ret, 1);
298                     g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
299                     g_free(mycmd);
300
301                     if (err) {
302                         g_printerr("error on running command: %s\n", err->message);
303                         g_error_free (err);
304                     }
305                     else if (*cmd_stdout) {
306                         g_string_append(buf, cmd_stdout);
307                         g_free(cmd_stdout);
308                     }
309                     s = vend+2;
310                 }
311                 else if(recurse != 2 && 
312                         etype == EXP_JS) {
313                     mycmd = expand(ret, 2);
314                     eval_js(uzbl.gui.web_view, mycmd, js_ret);
315                     g_free(mycmd);
316
317                     if(js_ret->str) {
318                         g_string_append(buf, js_ret->str);
319                         g_string_free(js_ret, TRUE);
320                         js_ret = g_string_new("");
321                     }
322                     s = vend+2;
323                 }
324                 break;
325
326             default:
327                 g_string_append_c(buf, *s);
328                 s++;
329                 break;
330         }
331     }
332     g_string_free(js_ret, TRUE);
333     return g_string_free(buf, FALSE);
334 }
335
336 char *
337 itos(int val) {
338     char tmp[20];
339
340     snprintf(tmp, sizeof(tmp), "%i", val);
341     return g_strdup(tmp);
342 }
343
344 gchar*
345 strfree(gchar *str) { g_free(str); return NULL; }  // for freeing & setting to null in one go
346
347 gchar*
348 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
349
350 char *
351 str_replace (const char* search, const char* replace, const char* string) {
352     gchar **buf;
353     char *ret;
354
355     buf = g_strsplit (string, search, -1);
356     ret = g_strjoinv (replace, buf);
357     g_strfreev(buf); // somebody said this segfaults
358
359     return ret;
360 }
361
362 GArray*
363 read_file_by_line (gchar *path) {
364     GIOChannel *chan = NULL;
365     gchar *readbuf = NULL;
366     gsize len;
367     GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
368     int i = 0;
369     
370     chan = g_io_channel_new_file(path, "r", NULL);
371     
372     if (chan) {
373         while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
374             const gchar* val = g_strdup (readbuf);
375             g_array_append_val (lines, val);
376             g_free (readbuf);
377             i ++;
378         }
379         
380         g_io_channel_unref (chan);
381     } else {
382         fprintf(stderr, "File '%s' not be read.\n", path);
383     }
384     
385     return lines;
386 }
387
388 gchar* 
389 parseenv (char* string) {
390     extern char** environ;
391     gchar* tmpstr = NULL;
392     int i = 0;
393     
394
395     while (environ[i] != NULL) {
396         gchar** env = g_strsplit (environ[i], "=", 2);
397         gchar* envname = g_strconcat ("$", env[0], NULL);
398
399         if (g_strrstr (string, envname) != NULL) {
400             tmpstr = g_strdup(string);
401             g_free (string);
402             string = str_replace(envname, env[1], tmpstr);
403             g_free (tmpstr);
404         }
405
406         g_free (envname);
407         g_strfreev (env); // somebody said this breaks uzbl
408         i++;
409     }
410
411     return string;
412 }
413
414 sigfunc*
415 setup_signal(int signr, sigfunc *shandler) {
416     struct sigaction nh, oh;
417
418     nh.sa_handler = shandler;
419     sigemptyset(&nh.sa_mask);
420     nh.sa_flags = 0;
421
422     if(sigaction(signr, &nh, &oh) < 0)
423         return SIG_ERR;
424
425     return NULL;
426 }
427
428 void
429 clean_up(void) {
430     if (uzbl.behave.fifo_dir)
431         unlink (uzbl.comm.fifo_path);
432     if (uzbl.behave.socket_dir)
433         unlink (uzbl.comm.socket_path);
434
435     g_free(uzbl.state.executable_path);
436     g_string_free(uzbl.state.keycmd, TRUE);
437     g_hash_table_destroy(uzbl.bindings);
438     g_hash_table_destroy(uzbl.behave.commands);
439     g_scanner_destroy(uzbl.scan);
440 }
441
442 /* used for html_mode_timeout 
443  * be sure to extend this function to use
444  * more timers if needed in other places
445 */
446 void
447 set_timeout(int seconds) {
448     struct itimerval t;
449     memset(&t, 0, sizeof t);
450
451     t.it_value.tv_sec =  seconds;
452     t.it_value.tv_usec = 0;
453     setitimer(ITIMER_REAL, &t, NULL);
454 }
455
456 /* --- SIGNAL HANDLER --- */
457
458 void
459 catch_sigterm(int s) {
460     (void) s;
461     clean_up();
462 }
463
464 void
465 catch_sigint(int s) {
466     (void) s;
467     clean_up();
468     exit(EXIT_SUCCESS);
469 }
470
471 void
472 catch_alrm(int s) {
473     (void) s;
474
475     set_var_value("mode", "0");
476     render_html();
477 }
478
479
480 /* --- CALLBACKS --- */
481
482 gboolean
483 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
484     (void) web_view;
485     (void) frame;
486     (void) navigation_action;
487     (void) policy_decision;
488     (void) user_data;
489     const gchar* uri = webkit_network_request_get_uri (request);
490     if (uzbl.state.verbose)
491         printf("New window requested -> %s \n", uri);
492     new_window_load_uri(uri);
493     return (FALSE);
494 }
495
496 gboolean
497 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type,  WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
498     (void) frame;
499     (void) request;
500     (void) user_data;
501
502     /* If we can display it, let's display it... */
503     if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
504         webkit_web_policy_decision_use (policy_decision);
505         return TRUE;
506     }
507
508     /* ...everything we can't displayed is downloaded */
509     webkit_web_policy_decision_download (policy_decision);
510     return TRUE;
511 }
512
513 WebKitWebView*
514 create_web_view_cb (WebKitWebView  *web_view, WebKitWebFrame *frame, gpointer user_data) {
515     (void) web_view;
516     (void) frame;
517     (void) user_data;
518     if (uzbl.state.selected_url != NULL) {
519         if (uzbl.state.verbose)
520             printf("\nNew web view -> %s\n",uzbl.state.selected_url);
521         new_window_load_uri(uzbl.state.selected_url);
522     } else {
523         if (uzbl.state.verbose)
524             printf("New web view -> %s\n","Nothing to open, exiting");
525     }
526     return (NULL);
527 }
528
529 gboolean
530 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
531     (void) web_view;
532     (void) user_data;
533     if (uzbl.behave.download_handler) {
534         const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
535         if (uzbl.state.verbose)
536             printf("Download -> %s\n",uri);
537         /* if urls not escaped, we may have to escape and quote uri before this call */
538         run_handler(uzbl.behave.download_handler, uri);
539     }
540     return (FALSE);
541 }
542
543 /* scroll a bar in a given direction */
544 void
545 scroll (GtkAdjustment* bar, GArray *argv) {
546     gchar *end;
547     gdouble max_value;
548
549     gdouble page_size = gtk_adjustment_get_page_size(bar);
550     gdouble value = gtk_adjustment_get_value(bar);
551     gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
552
553     if (*end == '%')
554         value += page_size * amount * 0.01;
555     else
556         value += amount;
557
558     max_value = gtk_adjustment_get_upper(bar) - page_size;
559
560     if (value > max_value)
561         value = max_value; /* don't scroll past the end of the page */
562
563     gtk_adjustment_set_value (bar, value);
564 }
565
566 void
567 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
568     (void) page; (void) argv; (void) result;
569     gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
570 }
571
572 void
573 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
574     (void) page; (void) argv; (void) result;
575     gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
576                               gtk_adjustment_get_page_size(uzbl.gui.bar_v));
577 }
578
579 void
580 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
581     (void) page; (void) result;
582     scroll(uzbl.gui.bar_v, argv);
583 }
584
585 void
586 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
587     (void) page; (void) result;
588     scroll(uzbl.gui.bar_h, argv);
589 }
590
591 void
592 cmd_set_status() {
593     if (!uzbl.behave.show_status) {
594         gtk_widget_hide(uzbl.gui.mainbar);
595     } else {
596         gtk_widget_show(uzbl.gui.mainbar);
597     }
598     update_title();
599 }
600
601 void
602 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
603     (void)page;
604     (void)argv;
605     (void)result;
606  
607     webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
608 }
609
610 void
611 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
612     (void)page;
613     (void)argv;
614     (void)result;
615
616     if (uzbl.behave.show_status) {
617         gtk_widget_hide(uzbl.gui.mainbar);
618     } else {
619         gtk_widget_show(uzbl.gui.mainbar);
620     }
621     uzbl.behave.show_status = !uzbl.behave.show_status;
622     update_title();
623 }
624
625 void
626 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
627     (void) page;
628     (void) title;
629     (void) data;
630     //Set selected_url state variable
631     g_free(uzbl.state.selected_url);
632     uzbl.state.selected_url = NULL;
633     if (link) {
634         uzbl.state.selected_url = g_strdup(link);
635     }
636     update_title();
637 }
638
639 void
640 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
641     (void) web_view;
642     (void) web_frame;
643     (void) data;
644     if (uzbl.gui.main_title)
645         g_free (uzbl.gui.main_title);
646     uzbl.gui.main_title = g_strdup (title);
647     update_title();
648 }
649
650 void
651 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
652     (void) page;
653     (void) data;
654     uzbl.gui.sbar.load_progress = progress;
655     update_title();
656 }
657
658 void
659 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
660     (void) page;
661     (void) frame;
662     (void) data;
663     if (uzbl.behave.load_finish_handler)
664         run_handler(uzbl.behave.load_finish_handler, "");
665 }
666
667 void
668 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
669     (void) page;
670     (void) frame;
671     (void) data;
672     uzbl.gui.sbar.load_progress = 0;
673     g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
674     if (uzbl.behave.load_start_handler)
675         run_handler(uzbl.behave.load_start_handler, "");
676 }
677
678 void
679 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
680     (void) page;
681     (void) data;
682     g_free (uzbl.state.uri);
683     GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
684     uzbl.state.uri = g_string_free (newuri, FALSE);
685     if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
686         uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
687         update_title();
688     }
689     if (uzbl.behave.load_commit_handler)
690         run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
691 }
692
693 void
694 destroy_cb (GtkWidget* widget, gpointer data) {
695     (void) widget;
696     (void) data;
697     gtk_main_quit ();
698 }
699
700 void
701 log_history_cb () {
702    if (uzbl.behave.history_handler) {
703        GTimeVal the_time;
704        gchar *date;
705        
706        g_get_current_time(&the_time);
707        /* no need to wrap this string with quotes since it contains no spaces.
708           format is like: 2009-06-26T20:02:05.262864Z */
709        date = g_time_val_to_iso8601(&the_time);
710        run_handler(uzbl.behave.history_handler, date);
711        g_free(date);
712    }
713 }
714
715
716 /* VIEW funcs (little webkit wrappers) */
717 #define VIEWFUNC(name) void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
718 VIEWFUNC(reload)
719 VIEWFUNC(reload_bypass_cache)
720 VIEWFUNC(stop_loading)
721 VIEWFUNC(zoom_in)
722 VIEWFUNC(zoom_out)
723 VIEWFUNC(go_back)
724 VIEWFUNC(go_forward)
725 #undef VIEWFUNC
726
727 /* -- command to callback/function map for things we cannot attach to any signals */
728 struct {char *key; CommandInfo value;} cmdlist[] =
729 {   /* key                   function      no_split      */
730     { "back",               {view_go_back, 0}              },
731     { "forward",            {view_go_forward, 0}           },
732     { "scroll_vert",        {scroll_vert, 0}               },
733     { "scroll_horz",        {scroll_horz, 0}               },
734     { "scroll_begin",       {scroll_begin, 0}              },
735     { "scroll_end",         {scroll_end, 0}                },
736     { "reload",             {view_reload, 0},              },
737     { "reload_ign_cache",   {view_reload_bypass_cache, 0}  },
738     { "stop",               {view_stop_loading, 0},        },
739     { "zoom_in",            {view_zoom_in, 0},             }, //Can crash (when max zoom reached?).
740     { "zoom_out",           {view_zoom_out, 0},            },
741     { "toggle_zoom_type",   {toggle_zoom_type, 0},         },
742     { "uri",                {load_uri, TRUE}               },
743     { "js",                 {run_js, TRUE}                 },
744     { "script",             {run_external_js, 0}           },
745     { "toggle_status",      {toggle_status_cb, 0}          },
746     { "spawn",              {spawn, 0}                     },
747     { "sync_spawn",         {spawn_sync, 0}                }, // needed for cookie handler
748     { "sh",                 {spawn_sh, 0}                  },
749     { "sync_sh",            {spawn_sh_sync, 0}             }, // needed for cookie handler
750     { "exit",               {close_uzbl, 0}                },
751     { "quit",               {close_uzbl, 0}                },
752     { "search",             {search_forward_text, TRUE}    },
753     { "search_reverse",     {search_reverse_text, TRUE}    },
754     { "dehilight",          {dehilight, 0}                 },
755     { "toggle_insert_mode", {toggle_insert_mode, 0}        },
756     { "set",                {set_var, TRUE}                },
757   //{ "get",                {get_var, TRUE}                },
758     { "bind",               {act_bind, TRUE}               },
759     { "dump_config",        {act_dump_config, 0}           },
760     { "keycmd",             {keycmd, TRUE}                 },
761     { "keycmd_nl",          {keycmd_nl, TRUE}              },
762     { "keycmd_bs",          {keycmd_bs, 0}                 },
763     { "chain",              {chain, 0}                     },
764     { "print",              {print, TRUE}                  }
765 };
766
767 void
768 commands_hash(void)
769 {
770     unsigned int i;
771     uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
772
773     for (i = 0; i < LENGTH(cmdlist); i++)
774         g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
775 }
776
777 /* -- CORE FUNCTIONS -- */
778
779 void
780 free_action(gpointer act) {
781     Action *action = (Action*)act;
782     g_free(action->name);
783     if (action->param)
784         g_free(action->param);
785     g_free(action);
786 }
787
788 Action*
789 new_action(const gchar *name, const gchar *param) {
790     Action *action = g_new(Action, 1);
791
792     action->name = g_strdup(name);
793     if (param)
794         action->param = g_strdup(param);
795     else
796         action->param = NULL;
797
798     return action;
799 }
800
801 bool
802 file_exists (const char * filename) {
803     return (access(filename, F_OK) == 0);
804 }
805
806 void
807 set_var(WebKitWebView *page, GArray *argv, GString *result) {
808     (void) page; (void) result;
809     gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
810     gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
811     set_var_value(g_strstrip(split[0]), value);
812     g_free(value);
813     g_strfreev(split);
814 }
815
816 void
817 print(WebKitWebView *page, GArray *argv, GString *result) {
818     (void) page; (void) result;
819     gchar* buf;
820
821     buf = expand(argv_idx(argv, 0), 0);
822     g_string_assign(result, buf);
823     g_free(buf);
824 }
825
826 void
827 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
828     (void) page; (void) result;
829     gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
830     gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
831     add_binding(g_strstrip(split[0]), value);
832     g_free(value);
833     g_strfreev(split);
834 }
835
836
837 void
838 act_dump_config() {
839     dump_config();
840 }
841
842 void
843 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
844     (void) page; (void) result;
845
846     if (argv_idx(argv, 0)) {
847         if (strcmp (argv_idx(argv, 0), "0") == 0) {
848             uzbl.behave.insert_mode = FALSE;
849         } else {
850             uzbl.behave.insert_mode = TRUE;
851         }
852     } else {
853         uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
854     }
855
856     update_title();
857 }
858
859 void
860 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
861     (void) result;
862
863     if (argv_idx(argv, 0)) {
864         GString* newuri = g_string_new (argv_idx(argv, 0));
865         if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
866             run_js(web_view, argv, NULL);
867             return;
868         }
869         if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
870             g_string_prepend (newuri, "http://");
871         /* if we do handle cookies, ask our handler for them */
872         webkit_web_view_load_uri (web_view, newuri->str);
873         g_string_free (newuri, TRUE);
874     }
875 }
876
877 /* Javascript*/
878
879 JSValueRef
880 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
881                 size_t argumentCount, const JSValueRef arguments[],
882                 JSValueRef* exception) {
883     (void) function;
884     (void) thisObject;
885     (void) exception;
886     
887     JSStringRef js_result_string;
888     GString *result = g_string_new("");
889
890     if (argumentCount >= 1) {
891         JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
892         size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
893         char ctl_line[arg_size];
894         JSStringGetUTF8CString(arg, ctl_line, arg_size);
895
896         parse_cmd_line(ctl_line, result);
897
898         JSStringRelease(arg);
899     }
900     js_result_string = JSStringCreateWithUTF8CString(result->str);
901
902     g_string_free(result, TRUE);
903
904     return JSValueMakeString(ctx, js_result_string);
905 }
906
907 JSStaticFunction js_static_functions[] = {
908     {"run", js_run_command, kJSPropertyAttributeNone},
909 };
910
911 void
912 js_init() {
913     /* This function creates the class and its definition, only once */
914     if (!uzbl.js.initialized) {
915         /* it would be pretty cool to make this dynamic */
916         uzbl.js.classdef = kJSClassDefinitionEmpty;
917         uzbl.js.classdef.staticFunctions = js_static_functions;
918
919         uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
920     }
921 }
922
923
924 void 
925 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
926     WebKitWebFrame *frame;
927     JSGlobalContextRef context;
928     JSObjectRef globalobject;
929     JSStringRef var_name;
930
931     JSStringRef js_script;
932     JSValueRef js_result;
933     JSStringRef js_result_string;
934     size_t js_result_size;
935     
936     js_init();
937
938     frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
939     context = webkit_web_frame_get_global_context(frame);
940     globalobject = JSContextGetGlobalObject(context);
941     
942     /* uzbl javascript namespace */
943     var_name = JSStringCreateWithUTF8CString("Uzbl");
944     JSObjectSetProperty(context, globalobject, var_name,
945                         JSObjectMake(context, uzbl.js.classref, NULL),  
946                         kJSClassAttributeNone, NULL);
947     
948     /* evaluate the script and get return value*/ 
949     js_script = JSStringCreateWithUTF8CString(script);
950     js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
951     if (js_result && !JSValueIsUndefined(context, js_result)) {
952         js_result_string = JSValueToStringCopy(context, js_result, NULL);
953         js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
954
955         if (js_result_size) {
956             char js_result_utf8[js_result_size];
957             JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
958             g_string_assign(result, js_result_utf8);
959         }
960
961         JSStringRelease(js_result_string);
962     }
963
964     /* cleanup */
965     JSObjectDeleteProperty(context, globalobject, var_name, NULL);
966
967     JSStringRelease(var_name);
968     JSStringRelease(js_script);
969 }
970
971 void
972 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
973     if (argv_idx(argv, 0))
974         eval_js(web_view, argv_idx(argv, 0), result);
975 }
976
977 void
978 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
979     (void) result;
980     if (argv_idx(argv, 0)) {
981         GArray* lines = read_file_by_line (argv_idx (argv, 0));
982         gchar*  js = NULL;
983         int i = 0;
984         gchar* line;
985
986         while ((line = g_array_index(lines, gchar*, i))) {
987             if (js == NULL) {
988                 js = g_strdup (line);
989             } else {
990                 gchar* newjs = g_strconcat (js, line, NULL);
991                 js = newjs;
992             }
993             i ++;
994             g_free (line);
995         }
996         
997         if (uzbl.state.verbose)
998             printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
999
1000         if (argv_idx (argv, 1)) {
1001             gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1002             g_free (js);
1003             js = newjs;
1004         }
1005         eval_js (web_view, js, result);
1006         g_free (js);
1007         g_array_free (lines, TRUE);
1008     }
1009 }
1010
1011 void
1012 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1013     if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1014         if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1015             webkit_web_view_unmark_text_matches (page);
1016             webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1017             uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1018         }
1019     }
1020     
1021     if (uzbl.state.searchtx) {
1022         if (uzbl.state.verbose)
1023             printf ("Searching: %s\n", uzbl.state.searchtx);
1024         webkit_web_view_set_highlight_text_matches (page, TRUE);
1025         webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1026     }
1027 }
1028
1029 void
1030 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1031     (void) result;
1032     search_text(page, argv, TRUE);
1033 }
1034
1035 void
1036 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1037     (void) result;
1038     search_text(page, argv, FALSE);
1039 }
1040
1041 void
1042 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1043     (void) argv; (void) result;
1044     webkit_web_view_set_highlight_text_matches (page, FALSE);
1045 }
1046
1047
1048 void
1049 new_window_load_uri (const gchar * uri) {
1050     GString* to_execute = g_string_new ("");
1051     g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1052     int i;
1053     for (i = 0; entries[i].long_name != NULL; i++) {
1054         if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1055             gchar** str = (gchar**)entries[i].arg_data;
1056             if (*str!=NULL) {
1057                 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1058             }
1059         }
1060     }
1061     if (uzbl.state.verbose)
1062         printf("\n%s\n", to_execute->str);
1063     g_spawn_command_line_async (to_execute->str, NULL);
1064     g_string_free (to_execute, TRUE);
1065 }
1066
1067 void
1068 chain (WebKitWebView *page, GArray *argv, GString *result) {
1069     (void) page; (void) result;
1070     gchar *a = NULL;
1071     gchar **parts = NULL;
1072     guint i = 0;    
1073     while ((a = argv_idx(argv, i++))) {
1074         parts = g_strsplit (a, " ", 2);
1075         parse_command(parts[0], parts[1], result);
1076         g_strfreev (parts);
1077     }
1078 }
1079
1080 void
1081 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1082     (void)page;
1083     (void)argv;
1084     (void)result;
1085     g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1086     run_keycmd(FALSE);
1087     update_title();
1088 }
1089
1090 void
1091 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1092     (void)page;
1093     (void)argv;
1094     (void)result;
1095     g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1096     run_keycmd(TRUE);
1097     update_title();
1098 }
1099
1100 void
1101 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1102     gchar *prev;
1103     (void)page;
1104     (void)argv;
1105     (void)result;
1106     prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1107     if (prev)
1108       g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1109     update_title();
1110 }
1111
1112 void
1113 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1114     (void)page;
1115     (void)argv;
1116     (void)result;
1117     gtk_main_quit ();
1118 }
1119
1120 /* --Statusbar functions-- */
1121 char*
1122 build_progressbar_ascii(int percent) {
1123    int width=uzbl.gui.sbar.progress_w;
1124    int i;
1125    double l;
1126    GString *bar = g_string_new("");
1127
1128    l = (double)percent*((double)width/100.);
1129    l = (int)(l+.5)>=(int)l ? l+.5 : l;
1130
1131    for(i=0; i<(int)l; i++)
1132        g_string_append(bar, uzbl.gui.sbar.progress_s);
1133
1134    for(; i<width; i++)
1135        g_string_append(bar, uzbl.gui.sbar.progress_u);
1136
1137    return g_string_free(bar, FALSE);
1138 }
1139
1140 void
1141 setup_scanner() {
1142      const GScannerConfig scan_config = {
1143              (
1144               "\t\r\n"
1145              )            /* cset_skip_characters */,
1146              (
1147               G_CSET_a_2_z
1148               "_#"
1149               G_CSET_A_2_Z
1150              )            /* cset_identifier_first */,
1151              (
1152               G_CSET_a_2_z
1153               "_0123456789"
1154               G_CSET_A_2_Z
1155               G_CSET_LATINS
1156               G_CSET_LATINC
1157              )            /* cset_identifier_nth */,
1158              ( "" )    /* cpair_comment_single */,
1159
1160              TRUE         /* case_sensitive */,
1161
1162              FALSE        /* skip_comment_multi */,
1163              FALSE        /* skip_comment_single */,
1164              FALSE        /* scan_comment_multi */,
1165              TRUE         /* scan_identifier */,
1166              TRUE         /* scan_identifier_1char */,
1167              FALSE        /* scan_identifier_NULL */,
1168              TRUE         /* scan_symbols */,
1169              FALSE        /* scan_binary */,
1170              FALSE        /* scan_octal */,
1171              FALSE        /* scan_float */,
1172              FALSE        /* scan_hex */,
1173              FALSE        /* scan_hex_dollar */,
1174              FALSE        /* scan_string_sq */,
1175              FALSE        /* scan_string_dq */,
1176              TRUE         /* numbers_2_int */,
1177              FALSE        /* int_2_float */,
1178              FALSE        /* identifier_2_string */,
1179              FALSE        /* char_2_token */,
1180              FALSE        /* symbol_2_token */,
1181              TRUE         /* scope_0_fallback */,
1182              FALSE,
1183              TRUE
1184      };
1185
1186      uzbl.scan = g_scanner_new(&scan_config);
1187      while(symp->symbol_name) {
1188          g_scanner_scope_add_symbol(uzbl.scan, 0,
1189                          symp->symbol_name,
1190                          GINT_TO_POINTER(symp->symbol_token));
1191          symp++;
1192      }
1193 }
1194
1195 gchar *
1196 expand_template(const char *template, gboolean escape_markup) {
1197      if(!template) return NULL;
1198
1199      GTokenType token = G_TOKEN_NONE;
1200      GString *ret = g_string_new("");
1201      char *buf=NULL;
1202      int sym;
1203
1204      g_scanner_input_text(uzbl.scan, template, strlen(template));
1205      while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1206          token = g_scanner_get_next_token(uzbl.scan);
1207
1208          if(token == G_TOKEN_SYMBOL) {
1209              sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1210              switch(sym) {
1211                  case SYM_URI:
1212                      if(escape_markup) {
1213                          buf = uzbl.state.uri ?
1214                              g_markup_printf_escaped("%s", uzbl.state.uri) : g_strdup("");
1215                          g_string_append(ret, buf);
1216                          g_free(buf);
1217                      }
1218                      else
1219                          g_string_append(ret, uzbl.state.uri ?
1220                                  uzbl.state.uri : g_strdup(""));
1221                      break;
1222                  case SYM_LOADPRGS:
1223                      buf = itos(uzbl.gui.sbar.load_progress);
1224                      g_string_append(ret, buf);
1225                      g_free(buf);
1226                      break;
1227                  case SYM_LOADPRGSBAR:
1228                      buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1229                      g_string_append(ret, buf);
1230                      g_free(buf);
1231                      break;
1232                  case SYM_TITLE:
1233                      if(escape_markup) {
1234                          buf = uzbl.gui.main_title ?
1235                              g_markup_printf_escaped("%s", uzbl.gui.main_title) : g_strdup("");
1236                          g_string_append(ret, buf);
1237                          g_free(buf);
1238                      }
1239                      else
1240                          g_string_append(ret, uzbl.gui.main_title ?
1241                                  uzbl.gui.main_title : "");
1242                      break;
1243                  case SYM_SELECTED_URI:
1244                      if(escape_markup) {
1245                          buf = uzbl.state.selected_url ?
1246                              g_markup_printf_escaped("%s", uzbl.state.selected_url) : g_strdup("");
1247                          g_string_append(ret, buf);
1248                          g_free(buf);
1249                      }
1250                      else
1251                          g_string_append(ret, uzbl.state.selected_url ?
1252                                  uzbl.state.selected_url : "");
1253                      break;
1254                  case SYM_NAME:
1255                      buf = itos(uzbl.xwin);
1256                      g_string_append(ret,
1257                              uzbl.state.instance_name ? uzbl.state.instance_name : buf);
1258                      g_free(buf);
1259                      break;
1260                  case SYM_KEYCMD:
1261                      if(escape_markup) {
1262                          buf = uzbl.state.keycmd->str ?
1263                              g_markup_printf_escaped("%s", uzbl.state.keycmd->str) : g_strdup("");
1264                          g_string_append(ret, buf);
1265                          g_free(buf);
1266                      }
1267                      else
1268                          g_string_append(ret, uzbl.state.keycmd->str ?
1269                                  uzbl.state.keycmd->str : "");
1270                      break;
1271                  case SYM_MODE:
1272                      g_string_append(ret,
1273                              uzbl.behave.insert_mode ?
1274                              uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
1275                      break;
1276                  case SYM_MSG:
1277                      g_string_append(ret,
1278                              uzbl.gui.sbar.msg ? uzbl.gui.sbar.msg : "");
1279                      break;
1280                      /* useragent syms */
1281                  case SYM_WK_MAJ:
1282                      buf = itos(WEBKIT_MAJOR_VERSION);
1283                      g_string_append(ret, buf);
1284                      g_free(buf);
1285                      break;
1286                  case SYM_WK_MIN:
1287                      buf = itos(WEBKIT_MINOR_VERSION);
1288                      g_string_append(ret, buf);
1289                      g_free(buf);
1290                      break;
1291                  case SYM_WK_MIC:
1292                      buf = itos(WEBKIT_MICRO_VERSION);
1293                      g_string_append(ret, buf);
1294                      g_free(buf);
1295                      break;
1296                  case SYM_SYSNAME:
1297                      g_string_append(ret, uzbl.state.unameinfo.sysname);
1298                      break;
1299                  case SYM_NODENAME:
1300                      g_string_append(ret, uzbl.state.unameinfo.nodename);
1301                      break;
1302                  case SYM_KERNREL:
1303                      g_string_append(ret, uzbl.state.unameinfo.release);
1304                      break;
1305                  case SYM_KERNVER:
1306                      g_string_append(ret, uzbl.state.unameinfo.version);
1307                      break;
1308                  case SYM_ARCHSYS:
1309                      g_string_append(ret, uzbl.state.unameinfo.machine);
1310                      break;
1311                  case SYM_ARCHUZBL:
1312                      g_string_append(ret, ARCH);
1313                      break;
1314 #ifdef _GNU_SOURCE
1315                  case SYM_DOMAINNAME:
1316                      g_string_append(ret, uzbl.state.unameinfo.domainname);
1317                      break;
1318 #endif
1319                  case SYM_COMMIT:
1320                      g_string_append(ret, COMMIT);
1321                      break;
1322                  default:
1323                      break;
1324              }
1325          }
1326          else if(token == G_TOKEN_INT) {
1327              g_string_append_printf(ret, "%lu", g_scanner_cur_value(uzbl.scan).v_int);
1328          }
1329          else if(token == G_TOKEN_IDENTIFIER) {
1330              g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1331          }
1332          else if(token == G_TOKEN_CHAR) {
1333              g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1334          }
1335          else if(token == G_TOKEN_ERROR) {
1336              g_scanner_error(uzbl.scan, "Token error in template ('%s') at line %d, column %d.", 
1337                                         template, 
1338                                         g_scanner_cur_line(uzbl.scan), 
1339                                         g_scanner_cur_position(uzbl.scan));
1340          }
1341      }
1342
1343      return g_string_free(ret, FALSE);
1344 }
1345 /* --End Statusbar functions-- */
1346
1347 void
1348 sharg_append(GArray *a, const gchar *str) {
1349     const gchar *s = (str ? str : "");
1350     g_array_append_val(a, s);
1351 }
1352
1353 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1354 gboolean
1355 run_command (const gchar *command, const guint npre, const gchar **args,
1356              const gboolean sync, char **output_stdout) {
1357    //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1358     GError *err = NULL;
1359     
1360     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1361     gchar *pid = itos(getpid());
1362     gchar *xwin = itos(uzbl.xwin);
1363     guint i;
1364     sharg_append(a, command);
1365     for (i = 0; i < npre; i++) /* add n args before the default vars */
1366         sharg_append(a, args[i]);
1367     sharg_append(a, uzbl.state.config_file);
1368     sharg_append(a, pid);
1369     sharg_append(a, xwin);
1370     sharg_append(a, uzbl.comm.fifo_path);
1371     sharg_append(a, uzbl.comm.socket_path);
1372     sharg_append(a, uzbl.state.uri);
1373     sharg_append(a, uzbl.gui.main_title);
1374
1375     for (i = npre; i < g_strv_length((gchar**)args); i++)
1376         sharg_append(a, args[i]);
1377     
1378     gboolean result;
1379     if (sync) {
1380         if (*output_stdout) *output_stdout = strfree(*output_stdout);
1381         
1382         result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1383                               NULL, NULL, output_stdout, NULL, NULL, &err);
1384     } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1385                                   NULL, NULL, NULL, &err);
1386
1387     if (uzbl.state.verbose) {
1388         GString *s = g_string_new("spawned:");
1389         for (i = 0; i < (a->len); i++) {
1390             gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1391             g_string_append_printf(s, " %s", qarg);
1392             g_free (qarg);
1393         }
1394         g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1395         printf("%s\n", s->str);
1396         g_string_free(s, TRUE);
1397         if(output_stdout) {
1398             printf("Stdout: %s\n", *output_stdout);
1399         }
1400     }
1401     if (err) {
1402         g_printerr("error on run_command: %s\n", err->message);
1403         g_error_free (err);
1404     }
1405     g_free (pid);
1406     g_free (xwin);
1407     g_array_free (a, TRUE);
1408     return result;
1409 }
1410
1411 gchar**
1412 split_quoted(const gchar* src, const gboolean unquote) {
1413     /* split on unquoted space, return array of strings;
1414        remove a layer of quotes and backslashes if unquote */
1415     if (!src) return NULL;
1416     
1417     gboolean dq = FALSE;
1418     gboolean sq = FALSE;
1419     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1420     GString *s = g_string_new ("");
1421     const gchar *p;
1422     gchar **ret;
1423     gchar *dup;
1424     for (p = src; *p != '\0'; p++) {
1425         if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1426         else if (*p == '\\') { g_string_append_c(s, *p++);
1427                                g_string_append_c(s, *p); }
1428         else if ((*p == '"') && unquote && !sq) dq = !dq;
1429         else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1430                                      dq = !dq; }
1431         else if ((*p == '\'') && unquote && !dq) sq = !sq;
1432         else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1433                                       sq = ! sq; }
1434         else if ((*p == ' ') && !dq && !sq) {
1435             dup = g_strdup(s->str);
1436             g_array_append_val(a, dup);
1437             g_string_truncate(s, 0);
1438         } else g_string_append_c(s, *p);
1439     }
1440     dup = g_strdup(s->str);
1441     g_array_append_val(a, dup);
1442     ret = (gchar**)a->data;
1443     g_array_free (a, FALSE);
1444     g_string_free (s, TRUE);
1445     return ret;
1446 }
1447
1448 void
1449 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1450     (void)web_view; (void)result;
1451     //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1452     if (argv_idx(argv, 0))
1453         run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1454 }
1455
1456 void
1457 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1458     (void)web_view; (void)result;
1459     
1460     if (argv_idx(argv, 0))
1461         run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1462                     TRUE, &uzbl.comm.sync_stdout);
1463 }
1464
1465 void
1466 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1467     (void)web_view; (void)result;
1468     if (!uzbl.behave.shell_cmd) {
1469         g_printerr ("spawn_sh: shell_cmd is not set!\n");
1470         return;
1471     }
1472     
1473     guint i;
1474     gchar *spacer = g_strdup("");
1475     g_array_insert_val(argv, 1, spacer);
1476     gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1477
1478     for (i = 1; i < g_strv_length(cmd); i++)
1479         g_array_prepend_val(argv, cmd[i]);
1480
1481     if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1482     g_free (spacer);
1483     g_strfreev (cmd);
1484 }
1485
1486 void
1487 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1488     (void)web_view; (void)result;
1489     if (!uzbl.behave.shell_cmd) {
1490         g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1491         return;
1492     }
1493     
1494     guint i;
1495     gchar *spacer = g_strdup("");
1496     g_array_insert_val(argv, 1, spacer);
1497     gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1498
1499     for (i = 1; i < g_strv_length(cmd); i++)
1500         g_array_prepend_val(argv, cmd[i]);
1501          
1502     if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1503                          TRUE, &uzbl.comm.sync_stdout);
1504     g_free (spacer);
1505     g_strfreev (cmd);
1506 }
1507
1508 void
1509 parse_command(const char *cmd, const char *param, GString *result) {
1510     CommandInfo *c;
1511
1512     if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1513             guint i;
1514             gchar **par = split_quoted(param, TRUE);
1515             GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1516
1517             if (c->no_split) { /* don't split */
1518                 sharg_append(a, param);
1519             } else if (par) {
1520                 for (i = 0; i < g_strv_length(par); i++)
1521                     sharg_append(a, par[i]);
1522             }
1523
1524             if (result == NULL) {
1525                 GString *result_print = g_string_new("");
1526
1527                 c->function(uzbl.gui.web_view, a, result_print);
1528                 if (result_print->len)
1529                     printf("%*s\n", result_print->len, result_print->str);
1530
1531                 g_string_free(result_print, TRUE);
1532             } else {
1533                 c->function(uzbl.gui.web_view, a, result);
1534             }
1535             g_strfreev (par);
1536             g_array_free (a, TRUE);
1537
1538     } else
1539         g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1540 }
1541
1542 void
1543 set_proxy_url() {
1544     SoupURI *suri;
1545
1546     if(*uzbl.net.proxy_url == ' '
1547        || uzbl.net.proxy_url == NULL) {
1548         soup_session_remove_feature_by_type(uzbl.net.soup_session,
1549                 (GType) SOUP_SESSION_PROXY_URI);
1550     }
1551     else {
1552         suri = soup_uri_new(uzbl.net.proxy_url);
1553         g_object_set(G_OBJECT(uzbl.net.soup_session),
1554                 SOUP_SESSION_PROXY_URI,
1555                 suri, NULL);
1556         soup_uri_free(suri);
1557     }
1558     return;
1559 }
1560
1561 void
1562 set_icon() {
1563     if(file_exists(uzbl.gui.icon)) {
1564         if (uzbl.gui.main_window)
1565             gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1566     } else {
1567         g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1568     }
1569 }
1570
1571 void
1572 cmd_load_uri() {
1573     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1574     g_array_append_val (a, uzbl.state.uri);
1575     load_uri(uzbl.gui.web_view, a, NULL);
1576     g_array_free (a, TRUE);
1577 }
1578
1579 void 
1580 cmd_always_insert_mode() {
1581     uzbl.behave.insert_mode =
1582         uzbl.behave.always_insert_mode ?  TRUE : FALSE;
1583     update_title();
1584 }
1585
1586 void
1587 cmd_max_conns() {
1588     g_object_set(G_OBJECT(uzbl.net.soup_session),
1589             SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1590 }
1591
1592 void
1593 cmd_max_conns_host() {
1594     g_object_set(G_OBJECT(uzbl.net.soup_session),
1595             SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1596 }
1597
1598 void
1599 cmd_http_debug() {
1600     soup_session_remove_feature
1601         (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1602     /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1603     /*g_free(uzbl.net.soup_logger);*/
1604
1605     uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1606     soup_session_add_feature(uzbl.net.soup_session,
1607             SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1608 }
1609
1610 WebKitWebSettings*
1611 view_settings() {
1612     return webkit_web_view_get_settings(uzbl.gui.web_view);
1613 }
1614
1615 void
1616 cmd_font_size() {
1617     WebKitWebSettings *ws = view_settings();
1618     if (uzbl.behave.font_size > 0) {
1619         g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1620     }
1621     
1622     if (uzbl.behave.monospace_size > 0) {
1623         g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1624                       uzbl.behave.monospace_size, NULL);
1625     } else {
1626         g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1627                       uzbl.behave.font_size, NULL);
1628     }
1629 }
1630
1631 void
1632 cmd_zoom_level() {
1633     webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1634 }
1635
1636 void
1637 cmd_disable_plugins() {
1638     g_object_set (G_OBJECT(view_settings()), "enable-plugins", 
1639             !uzbl.behave.disable_plugins, NULL);
1640 }
1641
1642 void
1643 cmd_disable_scripts() {
1644     g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1645             !uzbl.behave.disable_scripts, NULL);
1646 }
1647
1648 void
1649 cmd_minimum_font_size() {
1650     g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1651             uzbl.behave.minimum_font_size, NULL);
1652 }
1653 void
1654 cmd_autoload_img() {
1655     g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1656             uzbl.behave.autoload_img, NULL);
1657 }
1658
1659
1660 void
1661 cmd_autoshrink_img() {
1662     g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1663             uzbl.behave.autoshrink_img, NULL);
1664 }
1665
1666
1667 void
1668 cmd_enable_spellcheck() {
1669     g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1670             uzbl.behave.enable_spellcheck, NULL);
1671 }
1672
1673 void
1674 cmd_enable_private() {
1675     g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1676             uzbl.behave.enable_private, NULL);
1677 }
1678
1679 void
1680 cmd_print_bg() {
1681     g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1682             uzbl.behave.print_bg, NULL);
1683 }
1684
1685 void 
1686 cmd_style_uri() {
1687     g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1688             uzbl.behave.style_uri, NULL);
1689 }
1690
1691 void 
1692 cmd_resizable_txt() {
1693     g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1694             uzbl.behave.resizable_txt, NULL);
1695 }
1696
1697 void 
1698 cmd_default_encoding() {
1699     g_object_set (G_OBJECT(view_settings()), "default-encoding",
1700             uzbl.behave.default_encoding, NULL);
1701 }
1702
1703 void 
1704 cmd_enforce_96dpi() {
1705     g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1706             uzbl.behave.enforce_96dpi, NULL);
1707 }
1708
1709 void 
1710 cmd_caret_browsing() {
1711     g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1712             uzbl.behave.caret_browsing, NULL);
1713 }
1714
1715 void
1716 cmd_cookie_handler() {
1717     gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1718     /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1719     if ((g_strcmp0(split[0], "sh") == 0) ||
1720         (g_strcmp0(split[0], "spawn") == 0)) {
1721         g_free (uzbl.behave.cookie_handler);
1722         uzbl.behave.cookie_handler =
1723             g_strdup_printf("sync_%s %s", split[0], split[1]);
1724     }
1725     g_strfreev (split);
1726 }
1727
1728 void
1729 cmd_fifo_dir() {
1730     uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1731 }
1732
1733 void
1734 cmd_socket_dir() {
1735     uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1736 }
1737
1738 void
1739 cmd_inject_html() {
1740     if(uzbl.behave.inject_html) {
1741         webkit_web_view_load_html_string (uzbl.gui.web_view,
1742                 uzbl.behave.inject_html, NULL);
1743     }
1744 }
1745
1746 void
1747 cmd_modkey() {
1748     int i;
1749     char *buf;
1750
1751     buf = g_utf8_strup(uzbl.behave.modkey, -1);
1752     uzbl.behave.modmask = 0;
1753
1754     if(uzbl.behave.modkey) 
1755         g_free(uzbl.behave.modkey);
1756     uzbl.behave.modkey = buf;
1757
1758     for (i = 0; modkeys[i].key != NULL; i++) {
1759         if (g_strrstr(buf, modkeys[i].key))
1760             uzbl.behave.modmask |= modkeys[i].mask;
1761     }
1762 }
1763
1764 void
1765 cmd_useragent() {
1766     if (*uzbl.net.useragent == ' ') {
1767         g_free (uzbl.net.useragent);
1768         uzbl.net.useragent = NULL;
1769     } else {
1770         gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1771         if (ua)
1772             g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1773         g_free(uzbl.net.useragent);
1774         uzbl.net.useragent = ua;
1775     }
1776 }
1777
1778 void
1779 move_statusbar() {
1780     gtk_widget_ref(uzbl.gui.scrolled_win);
1781     gtk_widget_ref(uzbl.gui.mainbar);
1782     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1783     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1784
1785     if(uzbl.behave.status_top) {
1786         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1787         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1788     }
1789     else {
1790         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1791         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1792     }
1793     gtk_widget_unref(uzbl.gui.scrolled_win);
1794     gtk_widget_unref(uzbl.gui.mainbar);
1795     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1796     return;
1797 }
1798
1799 gboolean
1800 set_var_value(gchar *name, gchar *val) {
1801     uzbl_cmdprop *c = NULL;
1802     char *endp = NULL;
1803     char *buf = NULL;
1804
1805     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1806         /* check for the variable type */
1807         if (c->type == TYPE_STR) {
1808             buf = expand(val, 0);
1809             g_free(*c->ptr);
1810             *c->ptr = buf;
1811         } else if(c->type == TYPE_INT) {
1812             int *ip = (int *)c->ptr;
1813             buf = expand(val, 0);
1814             *ip = (int)strtoul(buf, &endp, 10);
1815             g_free(buf);
1816         } else if (c->type == TYPE_FLOAT) {
1817             float *fp = (float *)c->ptr;
1818             buf = expand(val, 0);
1819             *fp = strtod(buf, &endp);
1820             g_free(buf);
1821         }
1822
1823         /* invoke a command specific function */
1824         if(c->func) c->func();
1825     }
1826     return TRUE;
1827 }
1828
1829 void
1830 render_html() {
1831     Behaviour *b = &uzbl.behave;
1832
1833     if(b->html_buffer->str) {
1834         webkit_web_view_load_html_string (uzbl.gui.web_view,
1835                 b->html_buffer->str, b->base_url);
1836         g_string_free(b->html_buffer, TRUE);
1837         b->html_buffer = g_string_new("");
1838     }
1839 }
1840
1841 enum {M_CMD, M_HTML};
1842 void
1843 parse_cmd_line(const char *ctl_line, GString *result) {
1844     Behaviour *b = &uzbl.behave;
1845     size_t len=0;
1846
1847     if(b->mode == M_HTML) {
1848         len = strlen(b->html_endmarker);
1849         /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1850         if(len == strlen(ctl_line)-1 &&
1851            !strncmp(b->html_endmarker, ctl_line, len)) {
1852             set_timeout(0);
1853             set_var_value("mode", "0");
1854             render_html();
1855             return;
1856         }
1857         else {
1858             set_timeout(b->html_timeout);
1859             g_string_append(b->html_buffer, ctl_line);
1860         }
1861     }
1862     else if((ctl_line[0] == '#') /* Comments */
1863             || (ctl_line[0] == ' ')
1864             || (ctl_line[0] == '\n'))
1865         ; /* ignore these lines */
1866     else { /* parse a command */
1867         gchar *ctlstrip;
1868         gchar **tokens = NULL;
1869         len = strlen(ctl_line);
1870
1871         if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1872             ctlstrip = g_strndup(ctl_line, len - 1);
1873         else ctlstrip = g_strdup(ctl_line);
1874
1875         tokens = g_strsplit(ctlstrip, " ", 2);
1876         parse_command(tokens[0], tokens[1], result);
1877         g_free(ctlstrip);
1878         g_strfreev(tokens);
1879     }
1880 }
1881
1882 gchar*
1883 build_stream_name(int type, const gchar* dir) {
1884     char *xwin_str = NULL;
1885     State *s = &uzbl.state;
1886     gchar *str = NULL;
1887
1888     xwin_str = itos((int)uzbl.xwin);
1889     if (type == FIFO) {
1890         str = g_strdup_printf
1891             ("%s/uzbl_fifo_%s", dir,
1892              s->instance_name ? s->instance_name : xwin_str);
1893     } else if (type == SOCKET) {
1894         str = g_strdup_printf
1895             ("%s/uzbl_socket_%s", dir,
1896              s->instance_name ? s->instance_name : xwin_str );
1897     }
1898     g_free(xwin_str);
1899     return str;
1900 }
1901
1902 gboolean
1903 control_fifo(GIOChannel *gio, GIOCondition condition) {
1904     if (uzbl.state.verbose)
1905         printf("triggered\n");
1906     gchar *ctl_line;
1907     GIOStatus ret;
1908     GError *err = NULL;
1909
1910     if (condition & G_IO_HUP)
1911         g_error ("Fifo: Read end of pipe died!\n");
1912
1913     if(!gio)
1914        g_error ("Fifo: GIOChannel broke\n");
1915
1916     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1917     if (ret == G_IO_STATUS_ERROR) {
1918         g_error ("Fifo: Error reading: %s\n", err->message);
1919         g_error_free (err);
1920     }
1921
1922     parse_cmd_line(ctl_line, NULL);
1923     g_free(ctl_line);
1924
1925     return TRUE;
1926 }
1927
1928 gchar*
1929 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1930     GIOChannel *chan = NULL;
1931     GError *error = NULL;
1932     gchar *path = build_stream_name(FIFO, dir);
1933
1934     if (!file_exists(path)) {
1935         if (mkfifo (path, 0666) == 0) {
1936             // 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.
1937             chan = g_io_channel_new_file(path, "r+", &error);
1938             if (chan) {
1939                 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1940                     if (uzbl.state.verbose)
1941                         printf ("init_fifo: created successfully as %s\n", path);
1942                     uzbl.comm.fifo_path = path;
1943                     return dir;
1944                 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1945             } else g_warning ("init_fifo: can't open: %s\n", error->message);
1946         } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1947     } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1948
1949     /* if we got this far, there was an error; cleanup */
1950     if (error) g_error_free (error);
1951     g_free(dir);
1952     g_free(path);
1953     return NULL;
1954 }
1955
1956 gboolean
1957 control_stdin(GIOChannel *gio, GIOCondition condition) {
1958     (void) condition;
1959     gchar *ctl_line = NULL;
1960     GIOStatus ret;
1961
1962     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1963     if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1964         return FALSE;
1965
1966     parse_cmd_line(ctl_line, NULL);
1967     g_free(ctl_line);
1968
1969     return TRUE;
1970 }
1971
1972 void
1973 create_stdin () {
1974     GIOChannel *chan = NULL;
1975     GError *error = NULL;
1976
1977     chan = g_io_channel_unix_new(fileno(stdin));
1978     if (chan) {
1979         if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1980             g_error ("Stdin: could not add watch\n");
1981         } else {
1982             if (uzbl.state.verbose)
1983                 printf ("Stdin: watch added successfully\n");
1984         }
1985     } else {
1986         g_error ("Stdin: Error while opening: %s\n", error->message);
1987     }
1988     if (error) g_error_free (error);
1989 }
1990
1991 gboolean
1992 control_socket(GIOChannel *chan) {
1993     struct sockaddr_un remote;
1994     unsigned int t = sizeof(remote);
1995     int clientsock;
1996     GIOChannel *clientchan;
1997
1998     clientsock = accept (g_io_channel_unix_get_fd(chan),
1999                          (struct sockaddr *) &remote, &t);
2000     
2001     if ((clientchan = g_io_channel_unix_new(clientsock))) {
2002         g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2003                        (GIOFunc) control_client_socket, clientchan);
2004     }
2005
2006     return TRUE;
2007 }
2008
2009 gboolean
2010 control_client_socket(GIOChannel *clientchan) {
2011     char *ctl_line;
2012     GString *result = g_string_new("");
2013     GError *error = NULL;
2014     GIOStatus ret;
2015     gsize len;
2016
2017     ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2018     if (ret == G_IO_STATUS_ERROR) {
2019         g_warning ("Error reading: %s\n", error->message);
2020         g_io_channel_shutdown(clientchan, TRUE, &error);
2021         return FALSE;
2022     } else if (ret == G_IO_STATUS_EOF) {
2023         /* shutdown and remove channel watch from main loop */
2024         g_io_channel_shutdown(clientchan, TRUE, &error);
2025         return FALSE;
2026     }
2027
2028     if (ctl_line) {
2029         parse_cmd_line (ctl_line, result);
2030         g_string_append_c(result, '\n');
2031         ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2032                                         &len, &error);
2033         if (ret == G_IO_STATUS_ERROR) {
2034             g_warning ("Error writing: %s", error->message);
2035         }
2036         g_io_channel_flush(clientchan, &error);
2037     }
2038
2039     if (error) g_error_free (error);
2040     g_string_free(result, TRUE);
2041     g_free(ctl_line);
2042     return TRUE;
2043 }
2044
2045 gchar*
2046 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2047     if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2048         if (unlink(uzbl.comm.socket_path) == -1)
2049             g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2050         g_free(uzbl.comm.socket_path);
2051         uzbl.comm.socket_path = NULL;
2052     }
2053
2054     if (*dir == ' ') {
2055         g_free(dir);
2056         return NULL;
2057     }
2058
2059     GIOChannel *chan = NULL;
2060     int sock, len;
2061     struct sockaddr_un local;
2062     gchar *path = build_stream_name(SOCKET, dir);
2063
2064     sock = socket (AF_UNIX, SOCK_STREAM, 0);
2065
2066     local.sun_family = AF_UNIX;
2067     strcpy (local.sun_path, path);
2068     unlink (local.sun_path);
2069
2070     len = strlen (local.sun_path) + sizeof (local.sun_family);
2071     if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2072         if (uzbl.state.verbose)
2073             printf ("init_socket: opened in %s\n", path);
2074         listen (sock, 5);
2075
2076         if( (chan = g_io_channel_unix_new(sock)) ) {
2077             g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2078             uzbl.comm.socket_path = path;
2079             return dir;
2080         }
2081     } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2082
2083     /* if we got this far, there was an error; cleanup */
2084     g_free(path);
2085     g_free(dir);
2086     return NULL;
2087 }
2088
2089 /*
2090  NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2091  it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2092 */
2093 // this function may be called very early when the templates are not set (yet), hence the checks
2094 void
2095 update_title (void) {
2096     Behaviour *b = &uzbl.behave;
2097     gchar *parsed;
2098
2099     if (b->show_status) {
2100         if (b->title_format_short) {
2101             parsed = expand_template(b->title_format_short, FALSE);
2102             if (uzbl.gui.main_window)
2103                 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2104             g_free(parsed);
2105         }
2106         if (b->status_format) {
2107             parsed = expand_template(b->status_format, TRUE);
2108             gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2109             g_free(parsed);
2110         }
2111         if (b->status_background) {
2112             GdkColor color;
2113             gdk_color_parse (b->status_background, &color);
2114             //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)
2115             if (uzbl.gui.main_window)
2116                 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2117         }
2118     } else {
2119         if (b->title_format_long) {
2120             parsed = expand_template(b->title_format_long, FALSE);
2121             if (uzbl.gui.main_window)
2122                 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2123             g_free(parsed);
2124         }
2125     }
2126 }
2127
2128 gboolean
2129 key_press_cb (GtkWidget* window, GdkEventKey* event)
2130 {
2131     //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2132
2133     (void) window;
2134
2135     if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2136         || 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)
2137         return FALSE;
2138
2139     /* turn off insert mode (if always_insert_mode is not used) */
2140     if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2141         uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2142         update_title();
2143         return TRUE;
2144     }
2145
2146     if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2147         return FALSE;
2148
2149     if (event->keyval == GDK_Escape) {
2150         g_string_truncate(uzbl.state.keycmd, 0);
2151         update_title();
2152         dehilight(uzbl.gui.web_view, NULL, NULL);
2153         return TRUE;
2154     }
2155
2156     //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2157     if (event->keyval == GDK_Insert) {
2158         gchar * str;
2159         if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2160             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2161         } else {
2162             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2163         }
2164         if (str) {
2165             g_string_append (uzbl.state.keycmd, str);
2166             update_title ();
2167             g_free (str);
2168         }
2169         return TRUE;
2170     }
2171
2172     if (event->keyval == GDK_BackSpace)
2173         keycmd_bs(NULL, NULL, NULL);
2174
2175     gboolean key_ret = FALSE;
2176     if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2177         key_ret = TRUE;
2178     if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2179
2180     run_keycmd(key_ret);
2181     update_title();
2182     if (key_ret) return (!uzbl.behave.insert_mode);
2183     return TRUE;
2184 }
2185
2186 void
2187 run_keycmd(const gboolean key_ret) {
2188     /* run the keycmd immediately if it isn't incremental and doesn't take args */
2189     Action *act;
2190     if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2191         g_string_truncate(uzbl.state.keycmd, 0);
2192         parse_command(act->name, act->param, NULL);
2193         return;
2194     }
2195
2196     /* try if it's an incremental keycmd or one that takes args, and run it */
2197     GString* short_keys = g_string_new ("");
2198     GString* short_keys_inc = g_string_new ("");
2199     guint i;
2200     for (i=0; i<(uzbl.state.keycmd->len); i++) {
2201         g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2202         g_string_assign(short_keys_inc, short_keys->str);
2203         g_string_append_c(short_keys, '_');
2204         g_string_append_c(short_keys_inc, '*');
2205
2206         if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2207             /* run normal cmds only if return was pressed */
2208             exec_paramcmd(act, i);
2209             g_string_truncate(uzbl.state.keycmd, 0);
2210             break;
2211         } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2212             if (key_ret)  /* just quit the incremental command on return */
2213                 g_string_truncate(uzbl.state.keycmd, 0);
2214             else exec_paramcmd(act, i); /* otherwise execute the incremental */
2215             break;
2216         }
2217         
2218         g_string_truncate(short_keys, short_keys->len - 1);
2219     }
2220     g_string_free (short_keys, TRUE);
2221     g_string_free (short_keys_inc, TRUE);
2222 }
2223
2224 void
2225 exec_paramcmd(const Action *act, const guint i) {
2226     GString *parampart = g_string_new (uzbl.state.keycmd->str);
2227     GString *actionname = g_string_new ("");
2228     GString *actionparam = g_string_new ("");
2229     g_string_erase (parampart, 0, i+1);
2230     if (act->name)
2231         g_string_printf (actionname, act->name, parampart->str);
2232     if (act->param)
2233         g_string_printf (actionparam, act->param, parampart->str);
2234     parse_command(actionname->str, actionparam->str, NULL);
2235     g_string_free(actionname, TRUE);
2236     g_string_free(actionparam, TRUE);
2237     g_string_free(parampart, TRUE);
2238 }
2239
2240
2241 GtkWidget*
2242 create_browser () {
2243     GUI *g = &uzbl.gui;
2244
2245     GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2246     //main_window_ref = g_object_ref(scrolled_window);
2247     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
2248
2249     g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2250     gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2251
2252     g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
2253     g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2254     g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2255     g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2256     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2257     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2258     g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2259     g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2260     g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2261     g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2262     g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2263
2264     return scrolled_window;
2265 }
2266
2267 GtkWidget*
2268 create_mainbar () {
2269     GUI *g = &uzbl.gui;
2270
2271     g->mainbar = gtk_hbox_new (FALSE, 0);
2272
2273     /* keep a reference to the bar so we can re-pack it at runtime*/
2274     //sbar_ref = g_object_ref(g->mainbar);
2275
2276     g->mainbar_label = gtk_label_new ("");
2277     gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2278     gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2279     gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2280     gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2281     gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2282     g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2283     return g->mainbar;
2284 }
2285
2286 GtkWidget*
2287 create_window () {
2288     GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2289     gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2290     gtk_widget_set_name (window, "Uzbl browser");
2291     g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2292     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2293
2294     return window;
2295 }
2296
2297 GtkPlug* 
2298 create_plug () {
2299     GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2300     g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2301     g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2302
2303     return plug;
2304 }
2305
2306
2307 gchar**
2308 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2309     /*
2310       If actname is one that calls an external command, this function will inject
2311       newargs in front of the user-provided args in that command line.  They will
2312       come become after the body of the script (in sh) or after the name of
2313       the command to execute (in spawn).
2314       i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2315       spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2316
2317       The return value consist of two strings: the action (sh, ...) and its args.
2318
2319       If act is not one that calls an external command, then the given action merely
2320       gets duplicated.
2321     */
2322     GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2323     /* Arrr! Here be memory leaks */
2324     gchar *actdup = g_strdup(actname);
2325     g_array_append_val(rets, actdup);
2326
2327     if ((g_strcmp0(actname, "spawn") == 0) ||
2328         (g_strcmp0(actname, "sh") == 0) ||
2329         (g_strcmp0(actname, "sync_spawn") == 0) ||
2330         (g_strcmp0(actname, "sync_sh") == 0)) {
2331         guint i;
2332         GString *a = g_string_new("");
2333         gchar **spawnparts = split_quoted(origargs, FALSE);
2334         g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2335         if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2336
2337         for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2338             if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2339
2340         g_array_append_val(rets, a->str);
2341         g_string_free(a, FALSE);
2342         g_strfreev(spawnparts);
2343     } else {
2344         gchar *origdup = g_strdup(origargs);
2345         g_array_append_val(rets, origdup);
2346     }
2347     return (gchar**)g_array_free(rets, FALSE);
2348 }
2349
2350 void
2351 run_handler (const gchar *act, const gchar *args) {
2352     /* Consider this code a temporary hack to make the handlers usable.
2353        In practice, all this splicing, injection, and reconstruction is
2354        inefficient, annoying and hard to manage.  Potential pitfalls arise
2355        when the handler specific args 1) are not quoted  (the handler
2356        callbacks should take care of this)  2) are quoted but interfere
2357        with the users' own quotation.  A more ideal solution is
2358        to refactor parse_command so that it doesn't just take a string
2359        and execute it; rather than that, we should have a function which
2360        returns the argument vector parsed from the string.  This vector
2361        could be modified (e.g. insert additional args into it) before
2362        passing it to the next function that actually executes it.  Though
2363        it still isn't perfect for chain actions..  will reconsider & re-
2364        factor when I have the time. -duc */
2365
2366     char **parts = g_strsplit(act, " ", 2);
2367     if (!parts) return;
2368     if (g_strcmp0(parts[0], "chain") == 0) {
2369         GString *newargs = g_string_new("");
2370         gchar **chainparts = split_quoted(parts[1], FALSE);
2371         
2372         /* for every argument in the chain, inject the handler args
2373            and make sure the new parts are wrapped in quotes */
2374         gchar **cp = chainparts;
2375         gchar quot = '\'';
2376         gchar *quotless = NULL;
2377         gchar **spliced_quotless = NULL; // sigh -_-;
2378         gchar **inpart = NULL;
2379         
2380         while (*cp) {
2381             if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2382                 quot = **cp;
2383                 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2384             } else quotless = g_strdup(*cp);
2385
2386             spliced_quotless = g_strsplit(quotless, " ", 2);
2387             inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2388             g_strfreev(spliced_quotless);
2389             
2390             g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2391             g_free(quotless);
2392             g_strfreev(inpart);
2393             cp++;
2394         }
2395
2396         parse_command(parts[0], &(newargs->str[1]), NULL);
2397         g_string_free(newargs, TRUE);
2398         g_strfreev(chainparts);
2399         
2400     } else {
2401         gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2402         parse_command(inparts[0], inparts[1], NULL);
2403         g_free(inparts[0]);
2404         g_free(inparts[1]);
2405     }
2406     g_strfreev(parts);
2407 }
2408
2409 void
2410 add_binding (const gchar *key, const gchar *act) {
2411     char **parts = g_strsplit(act, " ", 2);
2412     Action *action;
2413
2414     if (!parts)
2415         return;
2416
2417     //Debug:
2418     if (uzbl.state.verbose)
2419         printf ("Binding %-10s : %s\n", key, act);
2420     action = new_action(parts[0], parts[1]);
2421
2422     if (g_hash_table_remove (uzbl.bindings, key))
2423         g_warning ("Overwriting existing binding for \"%s\"", key);
2424     g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2425     g_strfreev(parts);
2426 }
2427
2428 gchar*
2429 get_xdg_var (XDG_Var xdg) {
2430     const gchar* actual_value = getenv (xdg.environmental);
2431     const gchar* home         = getenv ("HOME");
2432     gchar* return_value;
2433
2434     if (! actual_value || strcmp (actual_value, "") == 0) {
2435         if (xdg.default_value) {
2436             return_value = str_replace ("~", home, xdg.default_value);
2437         } else {
2438             return_value = NULL;
2439         }
2440     } else {
2441         return_value = str_replace("~", home, actual_value);
2442     }
2443
2444     return return_value;
2445 }
2446
2447 gchar*
2448 find_xdg_file (int xdg_type, char* filename) {
2449     /* xdg_type = 0 => config
2450        xdg_type = 1 => data
2451        xdg_type = 2 => cache*/
2452
2453     gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2454     gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2455     g_free (xdgv);
2456
2457     gchar* temporary_string;
2458     char*  saveptr;
2459     char*  buf;
2460
2461     if (! file_exists (temporary_file) && xdg_type != 2) {
2462         buf = get_xdg_var (XDG[3 + xdg_type]);
2463         temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2464         g_free(buf);
2465
2466         while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2467             g_free (temporary_file);
2468             temporary_file = g_strconcat (temporary_string, filename, NULL);
2469         }
2470     }
2471     
2472     //g_free (temporary_string); - segfaults.
2473
2474     if (file_exists (temporary_file)) {
2475         return temporary_file;
2476     } else {
2477         return NULL;
2478     }
2479 }
2480 void
2481 settings_init () {
2482     State *s = &uzbl.state;
2483     Network *n = &uzbl.net;
2484     int i;
2485     for (i = 0; default_config[i].command != NULL; i++) {
2486         parse_cmd_line(default_config[i].command, NULL);
2487     }
2488
2489     if (!s->config_file) {
2490         s->config_file = find_xdg_file (0, "/uzbl/config");
2491     }
2492
2493     if (s->config_file) {
2494         GArray* lines = read_file_by_line (s->config_file);
2495         int i = 0;
2496         gchar* line;
2497
2498         while ((line = g_array_index(lines, gchar*, i))) {
2499             parse_cmd_line (line, NULL);
2500             i ++;
2501             g_free (line);
2502         }
2503         g_array_free (lines, TRUE);
2504     } else {
2505         if (uzbl.state.verbose)
2506             printf ("No configuration file loaded.\n");
2507     }
2508
2509     g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2510 }
2511
2512 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2513     (void) session;
2514     (void) user_data;
2515     if (!uzbl.behave.cookie_handler)
2516          return;
2517
2518     soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2519     GString *s = g_string_new ("");
2520     SoupURI * soup_uri = soup_message_get_uri(msg);
2521     g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2522     run_handler(uzbl.behave.cookie_handler, s->str);
2523
2524     if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2525         char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2526         if ( p != NULL ) *p = '\0';
2527         soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2528     }
2529     if (uzbl.comm.sync_stdout)
2530         uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2531         
2532     g_string_free(s, TRUE);
2533 }
2534
2535 void
2536 save_cookies (SoupMessage *msg, gpointer user_data){
2537     (void) user_data;
2538     GSList *ck;
2539     char *cookie;
2540     for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2541         cookie = soup_cookie_to_set_cookie_header(ck->data);
2542         SoupURI * soup_uri = soup_message_get_uri(msg);
2543         GString *s = g_string_new ("");
2544         g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2545         run_handler(uzbl.behave.cookie_handler, s->str);
2546         g_free (cookie);
2547         g_string_free(s, TRUE);
2548     }
2549     g_slist_free(ck);
2550 }
2551
2552 /* --- WEBINSPECTOR --- */
2553 void
2554 hide_window_cb(GtkWidget *widget, gpointer data) {
2555     (void) data;
2556
2557     gtk_widget_hide(widget);
2558 }
2559
2560 WebKitWebView*
2561 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2562     (void) data;
2563     (void) page;
2564     (void) web_inspector;
2565     GtkWidget* scrolled_window;
2566     GtkWidget* new_web_view;
2567     GUI *g = &uzbl.gui;
2568
2569     g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2570     g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2571             G_CALLBACK(hide_window_cb), NULL);
2572
2573     gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2574     gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2575     gtk_widget_show(g->inspector_window);
2576
2577     scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2578     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2579             GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2580     gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2581     gtk_widget_show(scrolled_window);
2582
2583     new_web_view = webkit_web_view_new();
2584     gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2585
2586     return WEBKIT_WEB_VIEW(new_web_view);
2587 }
2588
2589 gboolean
2590 inspector_show_window_cb (WebKitWebInspector* inspector){
2591     (void) inspector;
2592     gtk_widget_show(uzbl.gui.inspector_window);
2593     return TRUE;
2594 }
2595
2596 /* TODO: Add variables and code to make use of these functions */
2597 gboolean
2598 inspector_close_window_cb (WebKitWebInspector* inspector){
2599     (void) inspector;
2600     return TRUE;
2601 }
2602
2603 gboolean
2604 inspector_attach_window_cb (WebKitWebInspector* inspector){
2605     (void) inspector;
2606     return FALSE;
2607 }
2608
2609 gboolean
2610 inspector_detach_window_cb (WebKitWebInspector* inspector){
2611     (void) inspector;
2612     return FALSE;
2613 }
2614
2615 gboolean
2616 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2617     (void) inspector;
2618     return FALSE;
2619 }
2620
2621 gboolean
2622 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2623     (void) inspector;
2624     return FALSE;
2625 }
2626
2627 void
2628 set_up_inspector() {
2629     GUI *g = &uzbl.gui;
2630     WebKitWebSettings *settings = view_settings();
2631     g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2632
2633     uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2634     g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2635     g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2636     g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2637     g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2638     g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2639     g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2640
2641     g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2642 }
2643
2644 void
2645 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2646     (void) ud;
2647     uzbl_cmdprop *c = v;
2648
2649     if(!c->dump)
2650         return;
2651
2652     if(c->type == TYPE_STR)
2653         printf("set %s = %s\n", (char *)k, *c->ptr ? (char *)*c->ptr : " ");
2654     else if(c->type == TYPE_INT)
2655         printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2656     else if(c->type == TYPE_FLOAT)
2657         printf("set %s = %f\n", (char *)k, *(float *)c->ptr);
2658 }
2659
2660 void
2661 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2662     (void) ud;
2663     Action *a = v;
2664
2665     printf("bind %s = %s %s\n", (char *)k ,
2666             (char *)a->name, a->param?(char *)a->param:"");
2667 }
2668
2669 void
2670 dump_config() {
2671     g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2672     g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2673 }
2674
2675 #ifndef UZBL_LIBRARY
2676 /** -- MAIN -- **/
2677 int
2678 main (int argc, char* argv[]) {
2679     gtk_init (&argc, &argv);
2680     if (!g_thread_supported ())
2681         g_thread_init (NULL);
2682     uzbl.state.executable_path = g_strdup(argv[0]);
2683     uzbl.state.selected_url = NULL;
2684     uzbl.state.searchtx = NULL;
2685
2686     GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2687     g_option_context_add_main_entries (context, entries, NULL);
2688     g_option_context_add_group (context, gtk_get_option_group (TRUE));
2689     g_option_context_parse (context, &argc, &argv, NULL);
2690     g_option_context_free(context);
2691     
2692     gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2693     gboolean verbose_override = uzbl.state.verbose;
2694
2695     /* initialize hash table */
2696     uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2697
2698     uzbl.net.soup_session = webkit_get_default_session();
2699     uzbl.state.keycmd = g_string_new("");
2700
2701     if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2702         fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2703     if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2704         fprintf(stderr, "uzbl: error hooking SIGINT\n");
2705     if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2706         fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2707
2708
2709     if(uname(&uzbl.state.unameinfo) == -1)
2710         g_printerr("Can't retrieve unameinfo.  Your useragent might appear wrong.\n");
2711
2712     uzbl.gui.sbar.progress_s = g_strdup("=");
2713     uzbl.gui.sbar.progress_u = g_strdup("·");
2714     uzbl.gui.sbar.progress_w = 10;
2715
2716     /* HTML mode defaults*/
2717     uzbl.behave.html_buffer = g_string_new("");
2718     uzbl.behave.html_endmarker = g_strdup(".");
2719     uzbl.behave.html_timeout = 60;
2720     uzbl.behave.base_url = g_strdup("http://invalid");
2721
2722     /* default mode indicators */
2723     uzbl.behave.insert_indicator = g_strdup("I");
2724     uzbl.behave.cmd_indicator    = g_strdup("C");
2725
2726     setup_scanner();
2727     commands_hash ();
2728     make_var_to_name_hash();
2729
2730     uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2731
2732     uzbl.gui.scrolled_win = create_browser();
2733     create_mainbar();
2734
2735     /* initial packing */
2736     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2737     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2738
2739     if (uzbl.state.socket_id) {
2740         uzbl.gui.plug = create_plug ();
2741         gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2742         gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2743     } else {
2744         uzbl.gui.main_window = create_window ();
2745         gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2746         gtk_widget_show_all (uzbl.gui.main_window);
2747         uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2748     }
2749
2750     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2751
2752     if (uzbl.state.verbose) {
2753         printf("Uzbl start location: %s\n", argv[0]);
2754         if (uzbl.state.socket_id)
2755             printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2756         else
2757             printf("window_id %i\n",(int) uzbl.xwin);
2758         printf("pid %i\n", getpid ());
2759         printf("name: %s\n", uzbl.state.instance_name);
2760     }
2761
2762     uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2763     uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2764     uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2765     uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2766     gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2767
2768     settings_init ();
2769
2770     if (!uzbl.behave.show_status)
2771         gtk_widget_hide(uzbl.gui.mainbar);
2772     else
2773         update_title();
2774
2775     /* WebInspector */
2776     set_up_inspector();
2777
2778     create_stdin();
2779
2780     if (verbose_override > uzbl.state.verbose)
2781         uzbl.state.verbose = verbose_override;
2782     
2783     if (uri_override) {
2784         set_var_value("uri", uri_override);
2785         g_free(uri_override);
2786     } else if (uzbl.state.uri)
2787         cmd_load_uri(uzbl.gui.web_view, NULL);
2788
2789     gtk_main ();
2790     clean_up();
2791
2792     return EXIT_SUCCESS;
2793 }
2794 #endif
2795
2796 /* vi: set et ts=4: */