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