cleanup more of barrucadus shit
[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     (void) condition;
1379     gchar *ctl_line = NULL;
1380     GIOStatus ret;
1381
1382     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1383     if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1384         return FALSE;
1385
1386     parse_cmd_line(ctl_line);
1387     g_free(ctl_line);
1388
1389     return TRUE;
1390 }
1391
1392 static void
1393 create_stdin () {
1394     GIOChannel *chan = NULL;
1395     GError *error = NULL;
1396
1397     chan = g_io_channel_unix_new(fileno(stdin));
1398     if (chan) {
1399         if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1400             g_error ("Stdin: could not add watch\n");
1401         } else {
1402             if (uzbl.state.verbose)
1403                 printf ("Stdin: watch added successfully\n");
1404         }
1405     } else {
1406         g_error ("Stdin: Error while opening: %s\n", error->message);
1407     }
1408     if (error) g_error_free (error);
1409 }
1410
1411 static gboolean
1412 control_socket(GIOChannel *chan) {
1413     struct sockaddr_un remote;
1414     char buffer[512], *ctl_line;
1415     char temp[128];
1416     int sock, clientsock, n, done;
1417     unsigned int t;
1418
1419     sock = g_io_channel_unix_get_fd(chan);
1420
1421     memset (buffer, 0, sizeof (buffer));
1422
1423     t          = sizeof (remote);
1424     clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1425
1426     done = 0;
1427     do {
1428         memset (temp, 0, sizeof (temp));
1429         n = recv (clientsock, temp, 128, 0);
1430         if (n == 0) {
1431             buffer[strlen (buffer)] = '\0';
1432             done = 1;
1433         }
1434         if (!done)
1435             strcat (buffer, temp);
1436     } while (!done);
1437
1438     if (strcmp (buffer, "\n") < 0) {
1439         buffer[strlen (buffer) - 1] = '\0';
1440     } else {
1441         buffer[strlen (buffer)] = '\0';
1442     }
1443     close (clientsock);
1444     ctl_line = g_strdup(buffer);
1445     parse_cmd_line (ctl_line);
1446
1447 /*
1448    TODO: we should be able to do it with this.  but glib errors out with "Invalid argument"
1449     GError *error = NULL;
1450     gsize len;
1451     GIOStatus ret;
1452     ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1453     if (ret == G_IO_STATUS_ERROR)
1454         g_error ("Error reading: %s\n", error->message);
1455
1456     printf("Got line %s (%u bytes) \n",ctl_line, len);
1457     if(ctl_line) {
1458        parse_line(ctl_line);
1459 */
1460
1461     g_free(ctl_line);
1462     return TRUE;
1463 }
1464
1465 static gchar*
1466 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1467     if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1468         if (unlink(uzbl.comm.socket_path) == -1)
1469             g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1470         g_free(uzbl.comm.socket_path);
1471         uzbl.comm.socket_path = NULL;
1472     }
1473
1474     if (*dir == ' ') {
1475         g_free(dir);
1476         return NULL;
1477     }
1478
1479     GIOChannel *chan = NULL;
1480     int sock, len;
1481     struct sockaddr_un local;
1482     gchar *path = build_stream_name(SOCKET, dir);
1483
1484     sock = socket (AF_UNIX, SOCK_STREAM, 0);
1485
1486     local.sun_family = AF_UNIX;
1487     strcpy (local.sun_path, path);
1488     unlink (local.sun_path);
1489
1490     len = strlen (local.sun_path) + sizeof (local.sun_family);
1491     if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1492         if (uzbl.state.verbose)
1493             printf ("init_socket: opened in %s\n", path);
1494         listen (sock, 5);
1495
1496         if( (chan = g_io_channel_unix_new(sock)) ) {
1497             g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1498             uzbl.comm.socket_path = path;
1499             return dir;
1500         }
1501     } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1502
1503     /* if we got this far, there was an error; cleanup */
1504     g_free(path);
1505     g_free(dir);
1506     return NULL;
1507 }
1508
1509 /*
1510  NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1511  it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1512 */
1513 // this function may be called very early when the templates are not set (yet), hence the checks
1514 static void
1515 update_title (void) {
1516     Behaviour *b = &uzbl.behave;
1517     gchar *parsed;
1518
1519     if (b->show_status) {
1520         if (b->title_format_short) {
1521             parsed = expand_template(b->title_format_short);
1522             gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1523             g_free(parsed);
1524         }
1525         if (b->status_format) {
1526             parsed = expand_template(b->status_format);
1527             gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1528             g_free(parsed);
1529         }
1530         if (b->status_background) {
1531             GdkColor color;
1532             gdk_color_parse (b->status_background, &color);
1533             //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)
1534             gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1535         }
1536     } else {
1537         if (b->title_format_long) {
1538             parsed = expand_template(b->title_format_long);
1539             gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1540             g_free(parsed);
1541         }
1542     }
1543 }
1544
1545 static gboolean
1546 key_press_cb (GtkWidget* window, GdkEventKey* event)
1547 {
1548     //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1549
1550     (void) window;
1551
1552     if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1553         || 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)
1554         return FALSE;
1555
1556     /* turn off insert mode (if always_insert_mode is not used) */
1557     if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1558         uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1559         update_title();
1560         return TRUE;
1561     }
1562
1563     if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1564         return FALSE;
1565
1566     if (event->keyval == GDK_Escape) {
1567         g_string_truncate(uzbl.state.keycmd, 0);
1568         update_title();
1569         return TRUE;
1570     }
1571
1572     //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1573     if (event->keyval == GDK_Insert) {
1574         gchar * str;
1575         if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1576             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1577         } else {
1578             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1579         }
1580         if (str) {
1581             g_string_append (uzbl.state.keycmd, str);
1582             update_title ();
1583             g_free (str);
1584         }
1585         return TRUE;
1586     }
1587
1588     if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1589         g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1590         update_title();
1591     }
1592
1593     gboolean key_ret = FALSE;
1594     if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1595         key_ret = TRUE;
1596     if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1597
1598     run_keycmd(key_ret);
1599     update_title();
1600     if (key_ret) return (!uzbl.behave.insert_mode);
1601     return TRUE;
1602 }
1603
1604 static void
1605 run_keycmd(const gboolean key_ret) {
1606     /* run the keycmd immediately if it isn't incremental and doesn't take args */
1607     Action *action;
1608     if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1609         g_string_truncate(uzbl.state.keycmd, 0);
1610         parse_command(action->name, action->param);
1611         return;
1612     }
1613
1614     /* try if it's an incremental keycmd or one that takes args, and run it */
1615     GString* short_keys = g_string_new ("");
1616     GString* short_keys_inc = g_string_new ("");
1617     unsigned int i;
1618     for (i=0; i<(uzbl.state.keycmd->len); i++) {
1619         g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1620         g_string_assign(short_keys_inc, short_keys->str);
1621         g_string_append_c(short_keys, '_');
1622         g_string_append_c(short_keys_inc, '*');
1623
1624         gboolean exec_now = FALSE;
1625         if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1626             if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1627         } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1628             if (key_ret) { /* just quit the incremental command on return */
1629                 g_string_truncate(uzbl.state.keycmd, 0);
1630                 break;
1631             } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1632         }
1633
1634         if (exec_now) {
1635             GString* parampart = g_string_new (uzbl.state.keycmd->str);
1636             GString* actionname = g_string_new ("");
1637             GString* actionparam = g_string_new ("");
1638             g_string_erase (parampart, 0, i+1);
1639             if (action->name)
1640                 g_string_printf (actionname, action->name, parampart->str);
1641             if (action->param)
1642                 g_string_printf (actionparam, action->param, parampart->str);
1643             parse_command(actionname->str, actionparam->str);
1644             g_string_free (actionname, TRUE);
1645             g_string_free (actionparam, TRUE);
1646             g_string_free (parampart, TRUE);
1647             if (key_ret)
1648                 g_string_truncate(uzbl.state.keycmd, 0);
1649             break;
1650         }
1651
1652         g_string_truncate(short_keys, short_keys->len - 1);
1653     }
1654     g_string_free (short_keys, TRUE);
1655     g_string_free (short_keys_inc, TRUE);
1656 }
1657
1658 static GtkWidget*
1659 create_browser () {
1660     GUI *g = &uzbl.gui;
1661
1662     GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1663     //main_window_ref = g_object_ref(scrolled_window);
1664     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
1665
1666     g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1667     gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1668
1669     g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1670     g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1671     g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1672     g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1673     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1674     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1675     g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1676     g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1677     g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1678     g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1679
1680     return scrolled_window;
1681 }
1682
1683 static GtkWidget*
1684 create_mainbar () {
1685     GUI *g = &uzbl.gui;
1686
1687     g->mainbar = gtk_hbox_new (FALSE, 0);
1688
1689     /* keep a reference to the bar so we can re-pack it at runtime*/
1690     //sbar_ref = g_object_ref(g->mainbar);
1691
1692     g->mainbar_label = gtk_label_new ("");
1693     gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1694     gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1695     gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1696     gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1697     gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1698     return g->mainbar;
1699 }
1700
1701 static
1702 GtkWidget* create_window () {
1703     GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1704     gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1705     gtk_widget_set_name (window, "Uzbl browser");
1706     g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1707     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
1708
1709     return window;
1710 }
1711
1712 static void
1713 run_handler (const gchar *act, const gchar *args) {
1714     char **parts = g_strsplit(act, " ", 2);
1715     if (!parts) return;
1716     else if ((g_strcmp0(parts[0], "spawn") == 0)
1717              || (g_strcmp0(parts[0], "sh") == 0)) {
1718         guint i;
1719         GString *a = g_string_new ("");
1720         char **spawnparts;
1721         spawnparts = split_quoted(parts[1], FALSE);
1722         g_string_append_printf(a, "%s", spawnparts[0]);
1723         if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1724         for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1725             g_string_append_printf(a, " %s", spawnparts[i]);
1726         parse_command(parts[0], a->str);
1727         g_string_free (a, TRUE);
1728         g_strfreev (spawnparts);
1729     } else
1730         parse_command(parts[0], parts[1]);
1731     g_strfreev (parts);
1732 }
1733
1734 static void
1735 add_binding (const gchar *key, const gchar *act) {
1736     char **parts = g_strsplit(act, " ", 2);
1737     Action *action;
1738
1739     if (!parts)
1740         return;
1741
1742     //Debug:
1743     if (uzbl.state.verbose)
1744         printf ("Binding %-10s : %s\n", key, act);
1745     action = new_action(parts[0], parts[1]);
1746
1747     g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
1748     g_strfreev(parts);
1749 }
1750
1751 static gchar*
1752 get_xdg_var (XDG_Var xdg) {
1753     const gchar* actual_value = getenv (xdg.environmental);
1754     const gchar* home         = getenv ("HOME");
1755
1756     gchar* return_value = str_replace ("~", home, actual_value);
1757
1758     if (! actual_value || strcmp (actual_value, "") == 0) {
1759         if (xdg.default_value) {
1760             return_value = str_replace ("~", home, xdg.default_value);
1761         } else {
1762             return_value = NULL;
1763         }
1764     }
1765     return return_value;
1766 }
1767
1768 static gchar*
1769 find_xdg_file (int xdg_type, char* filename) {
1770     /* xdg_type = 0 => config
1771        xdg_type = 1 => data
1772        xdg_type = 2 => cache*/
1773
1774     gchar* xdgv = get_xdg_var (XDG[xdg_type]);
1775     gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
1776     g_free (xdgv);
1777
1778     gchar* temporary_string;
1779     char*  saveptr;
1780     char*  buf;
1781
1782     if (! file_exists (temporary_file) && xdg_type != 2) {
1783         buf = get_xdg_var (XDG[3 + xdg_type]);
1784         temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1785         free(buf);
1786
1787         while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
1788             strcpy (temporary_file, temporary_string);
1789             strcat (temporary_file, filename);
1790         }
1791     }
1792     
1793     //g_free (temporary_string); - segfaults.
1794
1795     if (file_exists (temporary_file)) {
1796         return temporary_file;
1797     } else {
1798         return NULL;
1799     }
1800 }
1801 static void
1802 settings_init () {
1803     State *s = &uzbl.state;
1804     Network *n = &uzbl.net;
1805     uzbl.behave.reset_command_mode = 1;
1806
1807     if (!s->config_file) {
1808         s->config_file = find_xdg_file (0, "/uzbl/config");
1809     }
1810
1811     if (s->config_file) {
1812         GArray* lines = read_file_by_line (s->config_file);
1813         int i = 0;
1814         gchar* line;
1815
1816         while ((line = g_array_index(lines, gchar*, i))) {
1817             parse_cmd_line (line);
1818             i ++;
1819         }
1820         g_array_free (lines, TRUE);
1821     } else {
1822         if (uzbl.state.verbose)
1823             printf ("No configuration file loaded.\n");
1824     }
1825     if (!uzbl.behave.status_format)
1826         set_var_value("status_format", STATUS_DEFAULT);
1827     if (!uzbl.behave.title_format_long)
1828         set_var_value("title_format_long", TITLE_LONG_DEFAULT);
1829     if (!uzbl.behave.title_format_short)
1830         set_var_value("title_format_short", TITLE_SHORT_DEFAULT);
1831
1832
1833     g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1834 }
1835
1836 static gchar*
1837 set_useragent(gchar *val) {
1838     if (*val == ' ') {
1839         g_free(val);
1840         return NULL;
1841     }
1842     gchar *ua = expand_template(val);
1843     if (ua)
1844         g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1845     return ua;
1846 }
1847
1848 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1849     (void) session;
1850     (void) user_data;
1851     if (!uzbl.behave.cookie_handler) return;
1852
1853     gchar * stdout = NULL;
1854     soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1855     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1856     gchar *action = g_strdup ("GET");
1857     SoupURI * soup_uri = soup_message_get_uri(msg);
1858     sharg_append(a, action);
1859     sharg_append(a, soup_uri->host);
1860     sharg_append(a, soup_uri->path);
1861     run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, TRUE, &stdout); /* TODO: use handler */
1862     //run_handler(uzbl.behave.cookie_handler); /* TODO: global stdout pointer, spawn_sync */
1863     if(stdout) {
1864         soup_message_headers_replace (msg->request_headers, "Cookie", stdout);
1865     }
1866     g_free (action);
1867     g_array_free(a, TRUE);
1868 }
1869
1870 static void
1871 save_cookies (SoupMessage *msg, gpointer user_data){
1872     (void) user_data;
1873     GSList *ck;
1874     char *cookie;
1875     for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1876         cookie = soup_cookie_to_set_cookie_header(ck->data);
1877         GArray *a = g_array_new(TRUE, FALSE, sizeof(gchar*));
1878         SoupURI * soup_uri = soup_message_get_uri(msg);
1879         gchar *action = strdup("PUT");
1880         sharg_append(a, action);
1881         sharg_append(a, soup_uri->host);
1882         sharg_append(a, soup_uri->path);
1883         sharg_append(a, cookie);
1884         run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, FALSE, NULL);
1885         g_free (cookie);
1886         g_free (action);
1887         g_array_free(a, TRUE);
1888     }
1889     g_slist_free(ck);
1890 }
1891
1892 int
1893 main (int argc, char* argv[]) {
1894     gtk_init (&argc, &argv);
1895     if (!g_thread_supported ())
1896         g_thread_init (NULL);
1897     uzbl.state.executable_path = g_strdup(argv[0]);
1898     uzbl.state.selected_url = NULL;
1899     uzbl.state.searchtx = NULL;
1900
1901     GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1902     g_option_context_add_main_entries (context, entries, NULL);
1903     g_option_context_add_group (context, gtk_get_option_group (TRUE));
1904     g_option_context_parse (context, &argc, &argv, NULL);
1905     g_option_context_free(context);
1906     /* initialize hash table */
1907     uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1908
1909     uzbl.net.soup_session = webkit_get_default_session();
1910     uzbl.state.keycmd = g_string_new("");
1911
1912     if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1913         fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1914     if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1915         fprintf(stderr, "uzbl: error hooking SIGINT\n");
1916
1917     if(uname(&uzbl.state.unameinfo) == -1)
1918         g_printerr("Can't retrieve unameinfo.  Your useragent might appear wrong.\n");
1919
1920     setup_regex();
1921     setup_scanner();
1922     commands_hash ();
1923     make_var_to_name_hash();
1924
1925     uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1926
1927     uzbl.gui.scrolled_win = create_browser();
1928     create_mainbar();
1929
1930     /* initial packing */
1931     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1932     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1933
1934     uzbl.gui.main_window = create_window ();
1935     gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
1936
1937
1938     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1939     gtk_widget_show_all (uzbl.gui.main_window);
1940     uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1941
1942     if (uzbl.state.verbose) {
1943         printf("Uzbl start location: %s\n", argv[0]);
1944         printf("window_id %i\n",(int) uzbl.xwin);
1945         printf("pid %i\n", getpid ());
1946         printf("name: %s\n", uzbl.state.instance_name);
1947     }
1948
1949     uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1950     uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1951     uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
1952     uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
1953     gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
1954
1955     settings_init ();
1956
1957     if (!uzbl.behave.show_status)
1958         gtk_widget_hide(uzbl.gui.mainbar);
1959     else
1960         update_title();
1961
1962     create_stdin();
1963
1964     if(uzbl.state.uri) {
1965         GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1966         g_array_append_val(a, uzbl.state.uri);
1967         load_uri (uzbl.gui.web_view, a);
1968         g_array_free (a, TRUE);
1969     }
1970
1971     gtk_main ();
1972     clean_up();
1973
1974     return EXIT_SUCCESS;
1975 }
1976
1977 /* vi: set et ts=4: */