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