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