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