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