bugfix: initialise memory when mapping command
[vimprobable2.git] / main.c
blob20a99256e9a1be99e7c51faf7fc6529ae787aa2f
1 /*
2 (c) 2009 by Leon Winter
3 (c) 2009-2012 by Hannes Schueller
4 (c) 2009-2010 by Matto Fransen
5 (c) 2010-2011 by Hans-Peter Deifel
6 (c) 2010-2011 by Thomas Adam
7 (c) 2011 by Albert Kim
8 (c) 2011 by Daniel Carl
9 (c) 2012 by Matthew Carter
10 see LICENSE file
13 #include <X11/Xlib.h>
14 #include "includes.h"
15 #include "vimprobable.h"
16 #include "utilities.h"
17 #include "callbacks.h"
18 #include "javascript.h"
20 /* the CLEAN_MOD_*_MASK defines have all the bits set that will be stripped from the modifier bit field */
21 #define CLEAN_MOD_NUMLOCK_MASK (GDK_MOD2_MASK)
22 #define CLEAN_MOD_BUTTON_MASK (GDK_BUTTON1_MASK|GDK_BUTTON2_MASK|GDK_BUTTON3_MASK|GDK_BUTTON4_MASK|GDK_BUTTON5_MASK)
24 /* remove unused bits, numlock symbol and buttons from keymask */
25 #define CLEAN(mask) (mask & (GDK_MODIFIER_MASK) & ~(CLEAN_MOD_NUMLOCK_MASK) & ~(CLEAN_MOD_BUTTON_MASK))
27 #define IS_ESCAPE(event) (IS_ESCAPE_KEY(CLEAN(event->state), event->keyval))
28 #define IS_ESCAPE_KEY(s, k) ((s == 0 && k == GDK_Escape) || \
29 (s == GDK_CONTROL_MASK && k == GDK_bracketleft))
31 /* callbacks here */
32 static void inputbox_activate_cb(GtkEntry *entry, gpointer user_data);
33 static gboolean inputbox_keypress_cb(GtkEntry *entry, GdkEventKey *event);
34 static gboolean inputbox_keyrelease_cb(GtkEntry *entry, GdkEventKey *event);
35 static gboolean inputbox_changed_cb(GtkEditable *entry, gpointer user_data);
36 static WebKitWebView* inspector_inspect_web_view_cb(gpointer inspector, WebKitWebView* web_view);
37 static gboolean notify_event_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data);
38 static gboolean webview_console_cb(WebKitWebView *webview, char *message, int line, char *source, gpointer user_data);
39 static gboolean webview_download_cb(WebKitWebView *webview, WebKitDownload *download, gpointer user_data);
40 static void webview_hoverlink_cb(WebKitWebView *webview, char *title, char *link, gpointer data);
41 static gboolean webview_keypress_cb(WebKitWebView *webview, GdkEventKey *event);
42 static void webview_load_committed_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data);
43 static void webview_load_finished_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data);
44 static gboolean webview_mimetype_cb(WebKitWebView *webview, WebKitWebFrame *frame, WebKitNetworkRequest *request,
45 char *mime_type, WebKitWebPolicyDecision *decision, gpointer user_data);
46 static gboolean webview_new_window_cb(WebKitWebView *webview, WebKitWebFrame *frame, WebKitNetworkRequest *request,
47 WebKitWebNavigationAction *action, WebKitWebPolicyDecision *decision, gpointer user_data);
48 static gboolean webview_open_in_new_window_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data);
49 static void webview_progress_changed_cb(WebKitWebView *webview, int progress, gpointer user_data);
50 static void webview_title_changed_cb(WebKitWebView *webview, WebKitWebFrame *frame, char *title, gpointer user_data);
51 static void window_destroyed_cb(GtkWidget *window, gpointer func_data);
52 static gboolean blank_cb(void);
54 /* functions */
55 static gboolean bookmark(const Arg *arg);
56 static gboolean browser_settings(const Arg *arg);
57 static gboolean commandhistoryfetch(const Arg *arg);
58 static gboolean complete(const Arg *arg);
59 static gboolean descend(const Arg *arg);
60 gboolean echo(const Arg *arg);
61 static gboolean focus_input(const Arg *arg);
62 static gboolean input(const Arg *arg);
63 static gboolean navigate(const Arg *arg);
64 static gboolean number(const Arg *arg);
65 static gboolean open_arg(const Arg *arg);
66 static gboolean open_remembered(const Arg *arg);
67 static gboolean paste(const Arg *arg);
68 static gboolean quickmark(const Arg *arg);
69 static gboolean quit(const Arg *arg);
70 static gboolean revive(const Arg *arg);
71 static gboolean print_frame(const Arg *arg);
72 static gboolean search(const Arg *arg);
73 static gboolean set(const Arg *arg);
74 static gboolean script(const Arg *arg);
75 static gboolean scroll(const Arg *arg);
76 static gboolean search_tag(const Arg *arg);
77 static gboolean yank(const Arg *arg);
78 static gboolean view_source(const Arg * arg);
79 static gboolean zoom(const Arg *arg);
80 static gboolean fake_key_event(const Arg *arg);
82 static void update_url(const char *uri);
83 static void setup_modkeys(void);
84 static void setup_gui(void);
85 static void setup_settings(void);
86 static void setup_signals(void);
87 static void ascii_bar(int total, int state, char *string);
88 static gchar *jsapi_ref_to_string(JSContextRef context, JSValueRef ref);
89 static void jsapi_evaluate_script(const gchar *script, gchar **value, gchar **message);
90 static void download_progress(WebKitDownload *d, GParamSpec *pspec);
91 static void set_widget_font_and_color(GtkWidget *widget, const char *font_str,
92 const char *bg_color_str, const char *fg_color_str);
94 static gboolean history(void);
95 static gboolean process_set_line(char *line);
96 void save_command_history(char *line);
97 void toggle_proxy(gboolean onoff);
98 void toggle_scrollbars(gboolean onoff);
100 gboolean process_keypress(GdkEventKey *event);
101 void fill_suggline(char * suggline, const char * command, const char *fill_with);
102 GtkWidget * fill_eventbox(const char * completion_line);
103 static void mop_up(void);
105 #include "main.h"
107 /* variables */
108 static GtkWindow *window;
109 static GtkWidget *viewport;
110 static GtkBox *box;
111 static GtkScrollbar *scroll_h;
112 static GtkScrollbar *scroll_v;
113 static GtkAdjustment *adjust_h;
114 static GtkAdjustment *adjust_v;
115 static GtkWidget *inputbox;
116 static GtkWidget *eventbox;
117 static GtkBox *statusbar;
118 static GtkWidget *status_url;
119 static GtkWidget *status_state;
120 static WebKitWebView *webview;
121 static SoupSession *session;
122 static GtkClipboard *clipboards[2];
123 static GdkKeymap *keymap;
125 static char **args;
126 static unsigned int mode = ModeNormal;
127 static unsigned int count = 0;
128 static float zoomstep;
129 char *modkeys;
130 static char current_modkey;
131 static char *search_handle;
132 static gboolean search_direction;
133 static gboolean echo_active = TRUE;
134 WebKitWebInspector *inspector;
136 static GdkNativeWindow embed = 0;
137 static char *configfile = NULL;
138 static char *winid = NULL;
140 static char rememberedURI[1024] = "";
141 static char followTarget[8] = "";
142 char *error_msg = NULL;
143 char *config_base = NULL;
144 static gboolean manual_focus = FALSE;
146 GList *activeDownloads;
148 #include "config.h"
149 #include "keymap.h"
151 GList *commandhistory = NULL;
152 int commandpointer = 0;
154 KeyList *keylistroot = NULL;
156 /* Cookie support. */
157 #ifdef ENABLE_COOKIE_SUPPORT
158 static SoupCookieJar *session_cookie_jar = NULL;
159 static SoupCookieJar *file_cookie_jar = NULL;
160 static time_t cookie_timeout = 4800;
161 static char *cookie_store;
162 static void setup_cookies(void);
163 static char *get_cookies(SoupURI *soup_uri);
164 static void load_all_cookies(void);
165 static void new_generic_request(SoupSession *soup_ses, SoupMessage *soup_msg, gpointer unused);
166 static void update_cookie_jar(SoupCookieJar *jar, SoupCookie *old, SoupCookie *new);
167 static void handle_cookie_request(SoupMessage *soup_msg, gpointer unused);
168 #endif
169 /* callbacks */
170 void
171 window_destroyed_cb(GtkWidget *window, gpointer func_data) {
172 quit(NULL);
175 void
176 webview_title_changed_cb(WebKitWebView *webview, WebKitWebFrame *frame, char *title, gpointer user_data) {
177 gtk_window_set_title(window, title);
180 void
181 webview_progress_changed_cb(WebKitWebView *webview, int progress, gpointer user_data) {
182 #ifdef ENABLE_GTK_PROGRESS_BAR
183 gtk_entry_set_progress_fraction(GTK_ENTRY(inputbox), progress == 100 ? 0 : (double)progress/100);
184 #endif
185 update_state();
188 #ifdef ENABLE_WGET_PROGRESS_BAR
189 void
190 ascii_bar(int total, int state, char *string) {
191 int i;
193 for (i = 0; i < state; i++)
194 string[i] = progressbartickchar;
195 string[i++] = progressbarcurrent;
196 for (; i < total; i++)
197 string[i] = progressbarspacer;
198 string[i] = '\0';
200 #endif
202 void
203 webview_load_committed_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data) {
204 Arg a = { .i = Silent, .s = g_strdup(JS_SETUP_HINTS) };
205 const char *uri = webkit_web_view_get_uri(webview);
207 update_url(uri);
208 script(&a);
209 g_free(a.s);
211 if (mode == ModeInsert || mode == ModeHints) {
212 Arg a = { .i = ModeNormal };
213 set(&a);
215 manual_focus = FALSE;
218 void
219 webview_load_finished_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data) {
220 WebKitWebSettings *settings = webkit_web_view_get_settings(webview);
221 gboolean scripts;
223 g_object_get(settings, "enable-scripts", &scripts, NULL);
224 if (escape_input_on_load && scripts && !manual_focus && !gtk_widget_is_focus(inputbox)) {
225 Arg a = { .i = Silent, .s = g_strdup("hints.clearFocus();") };
226 script(&a);
227 g_free(a.s);
228 a.i = ModeNormal;
229 a.s = NULL;
230 set(&a);
232 if (HISTORY_MAX_ENTRIES > 0)
233 history();
234 update_state();
237 static gboolean
238 webview_open_in_new_window_cb(WebKitWebView *webview, WebKitWebFrame *frame, gpointer user_data) {
239 Arg a = { .i = TargetNew, .s = (char*)webkit_web_view_get_uri(webview) };
240 if (strlen(rememberedURI) > 0) {
241 a.s = rememberedURI;
243 open_arg(&a);
244 return FALSE;
247 gboolean
248 webview_new_window_cb(WebKitWebView *webview, WebKitWebFrame *frame, WebKitNetworkRequest *request,
249 WebKitWebNavigationAction *action, WebKitWebPolicyDecision *decision, gpointer user_data) {
250 Arg a = { .i = TargetNew, .s = (char*)webkit_network_request_get_uri(request) };
251 open_arg(&a);
252 webkit_web_policy_decision_ignore(decision);
253 return TRUE;
256 gboolean
257 webview_mimetype_cb(WebKitWebView *webview, WebKitWebFrame *frame, WebKitNetworkRequest *request,
258 char *mime_type, WebKitWebPolicyDecision *decision, gpointer user_data) {
259 if (webkit_web_view_can_show_mime_type(webview, mime_type) == FALSE) {
260 webkit_web_policy_decision_download(decision);
261 return TRUE;
262 } else {
263 return FALSE;
267 static WebKitWebView*
268 inspector_inspect_web_view_cb(gpointer inspector, WebKitWebView* web_view) {
269 gchar* inspector_title;
270 GtkWidget* inspector_window;
271 GtkWidget* inspector_view;
273 /* just enough code to show the inspector - no signal handling etc. */
274 inspector_title = g_strdup_printf("Inspect page - %s - Vimprobable2", webkit_web_view_get_uri(web_view));
275 if (embed) {
276 inspector_window = gtk_plug_new(embed);
277 } else {
278 inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
279 gtk_window_set_wmclass(window, "vimprobable2", "Vimprobable2");
281 gtk_window_set_title(GTK_WINDOW(inspector_window), inspector_title);
282 g_free(inspector_title);
283 inspector_view = webkit_web_view_new();
284 gtk_container_add(GTK_CONTAINER(inspector_window), inspector_view);
285 gtk_widget_show_all(inspector_window);
286 return WEBKIT_WEB_VIEW(inspector_view);
289 gboolean
290 webview_download_cb(WebKitWebView *webview, WebKitDownload *download, gpointer user_data) {
291 const gchar *filename;
292 gchar *uri, *path;
293 uint32_t size;
294 Arg a;
295 WebKitDownloadStatus status;
297 filename = webkit_download_get_suggested_filename(download);
298 if (filename == NULL || strlen(filename) == 0) {
299 filename = "vimprobable_download";
301 path = g_build_filename(g_strdup_printf(DOWNLOADS_PATH), filename, NULL);
302 uri = g_strconcat("file://", path, NULL);
303 webkit_download_set_destination_uri(download, uri);
304 g_free(uri);
305 size = (uint32_t)webkit_download_get_total_size(download);
306 a.i = Info;
307 if (size > 0)
308 a.s = g_strdup_printf("Download %s started (expected size: %u bytes)...", filename, size);
309 else
310 a.s = g_strdup_printf("Download %s started (unknown size)...", filename);
311 echo(&a);
312 g_free(a.s);
313 activeDownloads = g_list_prepend(activeDownloads, download);
314 g_signal_connect(download, "notify::progress", G_CALLBACK(download_progress), NULL);
315 g_signal_connect(download, "notify::status", G_CALLBACK(download_progress), NULL);
316 status = webkit_download_get_status(download);
317 if (status == WEBKIT_DOWNLOAD_STATUS_CREATED)
318 webkit_download_start(download);
319 update_state();
320 return TRUE;
323 gboolean
324 blank_cb(void) {
325 return TRUE;
328 void
329 download_progress(WebKitDownload *d, GParamSpec *pspec) {
330 Arg a;
331 WebKitDownloadStatus status = webkit_download_get_status(d);
333 if (status != WEBKIT_DOWNLOAD_STATUS_STARTED && status != WEBKIT_DOWNLOAD_STATUS_CREATED) {
334 if (status != WEBKIT_DOWNLOAD_STATUS_FINISHED) {
335 a.i = Error;
336 a.s = g_strdup_printf("Error while downloading %s", webkit_download_get_suggested_filename(d));
337 echo(&a);
338 } else {
339 a.i = Info;
340 a.s = g_strdup_printf("Download %s finished", webkit_download_get_suggested_filename(d));
341 echo(&a);
343 g_free(a.s);
344 activeDownloads = g_list_remove(activeDownloads, d);
346 update_state();
350 gboolean
351 process_keypress(GdkEventKey *event) {
352 KeyList *current;
353 guint keyval;
354 GdkModifierType irrelevant;
356 /* Get a mask of modifiers that shouldn't be considered for this event.
357 * E.g.: It shouldn't matter whether ';' is shifted or not. */
358 gdk_keymap_translate_keyboard_state(keymap, event->hardware_keycode,
359 event->state, event->group, &keyval, NULL, NULL, &irrelevant);
361 current = keylistroot;
363 while (current != NULL) {
364 if (current->Element.mask == (CLEAN(event->state) & ~irrelevant)
365 && (current->Element.modkey == current_modkey
366 || (!current->Element.modkey && !current_modkey)
367 || current->Element.modkey == GDK_VoidSymbol ) /* wildcard */
368 && current->Element.key == keyval
369 && current->Element.func)
370 if (current->Element.func(&current->Element.arg)) {
371 current_modkey = count = 0;
372 update_state();
373 return TRUE;
375 current = current->next;
377 return FALSE;
380 gboolean
381 webview_keypress_cb(WebKitWebView *webview, GdkEventKey *event) {
382 Arg a = { .i = ModeNormal, .s = NULL };
383 guint keyval;
384 GdkModifierType irrelevant;
386 /* Get a mask of modifiers that shouldn't be considered for this event.
387 * E.g.: It shouldn't matter whether ';' is shifted or not. */
388 gdk_keymap_translate_keyboard_state(keymap, event->hardware_keycode,
389 event->state, event->group, &keyval, NULL, NULL, &irrelevant);
391 switch (mode) {
392 case ModeNormal:
393 if ((CLEAN(event->state) & ~irrelevant) == 0) {
394 if (IS_ESCAPE(event)) {
395 a.i = Info;
396 a.s = g_strdup("");
397 echo(&a);
398 g_free(a.s);
399 } else if (current_modkey == 0 && ((event->keyval >= GDK_1 && event->keyval <= GDK_9)
400 || (event->keyval == GDK_0 && count))) {
401 count = (count ? count * 10 : 0) + (event->keyval - GDK_0);
402 update_state();
403 return TRUE;
404 } else if (strchr(modkeys, event->keyval) && current_modkey != event->keyval) {
405 current_modkey = event->keyval;
406 update_state();
407 return TRUE;
410 /* keybindings */
411 if (process_keypress(event) == TRUE) return TRUE;
413 break;
414 case ModeInsert:
415 if (IS_ESCAPE(event)) {
416 a.i = Silent;
417 a.s = g_strdup("hints.clearFocus();");
418 script(&a);
419 g_free(a.s);
420 a.i = ModeNormal;
421 return set(&a);
423 case ModePassThrough:
424 if (IS_ESCAPE(event)) {
425 echo(&a);
426 set(&a);
427 return TRUE;
429 break;
430 case ModeSendKey:
431 echo(&a);
432 set(&a);
433 break;
435 return FALSE;
438 void
439 set_widget_font_and_color(GtkWidget *widget, const char *font_str, const char *bg_color_str,
440 const char *fg_color_str) {
441 GdkColor fg_color;
442 GdkColor bg_color;
443 PangoFontDescription *font;
445 font = pango_font_description_from_string(font_str);
446 gtk_widget_modify_font(widget, font);
447 pango_font_description_free(font);
449 if (fg_color_str)
450 gdk_color_parse(fg_color_str, &fg_color);
451 if (bg_color_str)
452 gdk_color_parse(bg_color_str, &bg_color);
454 gtk_widget_modify_text(widget, GTK_STATE_NORMAL, fg_color_str ? &fg_color : NULL);
455 gtk_widget_modify_base(widget, GTK_STATE_NORMAL, bg_color_str ? &bg_color : NULL);
457 return;
460 void
461 webview_hoverlink_cb(WebKitWebView *webview, char *title, char *link, gpointer data) {
462 const char *uri = webkit_web_view_get_uri(webview);
463 char *markup;
465 memset(rememberedURI, 0, 1024);
466 if (link) {
467 markup = g_markup_printf_escaped("<span font=\"%s\">Link: %s</span>", statusfont, link);
468 gtk_label_set_markup(GTK_LABEL(status_url), markup);
469 strncpy(rememberedURI, link, 1024);
470 g_free(markup);
471 } else
472 update_url(uri);
475 gboolean
476 webview_console_cb(WebKitWebView *webview, char *message, int line, char *source, gpointer user_data) {
477 Arg a;
479 /* Don't change internal mode if the browser doesn't have focus to prevent inconsistent states */
480 if (gtk_window_has_toplevel_focus(window)) {
481 if (!strcmp(message, "hintmode_off") || !strcmp(message, "insertmode_off")) {
482 a.i = ModeNormal;
483 return set(&a);
484 } else if (!strcmp(message, "insertmode_on")) {
485 a.i = ModeInsert;
486 return set(&a);
489 return FALSE;
492 void
493 inputbox_activate_cb(GtkEntry *entry, gpointer user_data) {
494 char *text;
495 guint16 length = gtk_entry_get_text_length(entry);
496 Arg a;
497 gboolean success = FALSE, forward = FALSE;
499 a.i = HideCompletion;
500 complete(&a);
501 if (length == 0)
502 return;
503 text = (char*)gtk_entry_get_text(entry);
504 if (length > 1 && text[0] == ':') {
505 success = process_line((text + 1));
506 } else if (length > 1 && ((forward = text[0] == '/') || text[0] == '?')) {
507 webkit_web_view_unmark_text_matches(webview);
508 #ifdef ENABLE_MATCH_HIGHLITING
509 webkit_web_view_mark_text_matches(webview, &text[1], FALSE, 0);
510 webkit_web_view_set_highlight_text_matches(webview, TRUE);
511 #endif
512 count = 0;
513 #ifndef ENABLE_INCREMENTAL_SEARCH
514 a.s =& text[1];
515 a.i = searchoptions | (forward ? DirectionForward : DirectionBackwards);
516 search(&a);
517 #else
518 search_direction = forward;
519 search_handle = g_strdup(&text[1]);
520 #endif
521 } else if (text[0] == '.' || text[0] == ',' || text[0] == ';') {
522 a.i = Silent;
523 a.s = g_strdup_printf("hints.fire();");
524 script(&a);
525 g_free(a.s);
526 update_state();
527 } else
528 return;
529 if (!echo_active)
530 gtk_entry_set_text(entry, "");
531 gtk_widget_grab_focus(GTK_WIDGET(webview));
534 gboolean
535 inputbox_keypress_cb(GtkEntry *entry, GdkEventKey *event) {
536 Arg a;
537 int numval;
539 if (mode == ModeHints) {
540 if (event->keyval == GDK_Tab) {
541 a.i = Silent;
542 a.s = g_strdup_printf("hints.focusNextHint();");
543 script(&a);
544 g_free(a.s);
545 update_state();
546 return TRUE;
548 if (event->keyval == GDK_ISO_Left_Tab) {
549 a.i = Silent;
550 a.s = g_strdup_printf("hints.focusPreviousHint();");
551 script(&a);
552 g_free(a.s);
553 update_state();
554 return TRUE;
556 if (event->keyval == GDK_Return) {
557 a.i = Silent;
558 a.s = g_strdup_printf("hints.fire();");
559 script(&a);
560 g_free(a.s);
561 update_state();
562 return TRUE;
565 switch (event->keyval) {
566 case GDK_bracketleft:
567 case GDK_Escape:
568 if (!IS_ESCAPE(event)) break;
569 a.i = HideCompletion;
570 complete(&a);
571 a.i = ModeNormal;
572 commandpointer = 0;
573 return set(&a);
574 break;
575 case GDK_Tab:
576 a.i = DirectionNext;
577 return complete(&a);
578 break;
579 case GDK_Up:
580 a.i = DirectionPrev;
581 return commandhistoryfetch(&a);
582 break;
583 case GDK_Down:
584 a.i = DirectionNext;
585 return commandhistoryfetch(&a);
586 break;
587 case GDK_ISO_Left_Tab:
588 a.i = DirectionPrev;
589 return complete(&a);
590 break;
593 if (mode == ModeHints) {
594 if ((CLEAN(event->state) & GDK_SHIFT_MASK) &&
595 (CLEAN(event->state) & GDK_CONTROL_MASK) &&
596 (event->keyval == GDK_BackSpace)) {
597 count /= 10;
598 a.i = Silent;
599 a.s = g_strdup_printf("hints.updateHints(%d);", count);
600 script(&a);
601 g_free(a.s);
602 update_state();
603 return TRUE;
606 numval = g_unichar_digit_value((gunichar) gdk_keyval_to_unicode(event->keyval));
607 if ((numval >= 1 && numval <= 9) || (numval == 0 && count)) {
608 /* allow a zero as non-first number */
609 count = (count ? count * 10 : 0) + numval;
610 a.i = Silent;
611 a.s = g_strdup_printf("hints.updateHints(%d);", count);
612 script(&a);
613 g_free(a.s);
614 update_state();
615 return TRUE;
619 return FALSE;
622 gboolean
623 notify_event_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) {
624 int i;
625 WebKitHitTestResult *result;
626 WebKitHitTestResultContext context;
627 if (mode == ModeNormal && event->type == GDK_BUTTON_RELEASE) {
628 /* handle mouse click events */
629 for (i = 0; i < LENGTH(mouse); i++) {
630 if (mouse[i].mask == CLEAN(event->button.state)
631 && (mouse[i].modkey == current_modkey
632 || (!mouse[i].modkey && !current_modkey)
633 || mouse[i].modkey == GDK_VoidSymbol) /* wildcard */
634 && mouse[i].button == event->button.button
635 && mouse[i].func) {
636 if (mouse[i].func(&mouse[i].arg)) {
637 current_modkey = count = 0;
638 update_state();
639 return TRUE;
643 result = webkit_web_view_get_hit_test_result(WEBKIT_WEB_VIEW(widget), (GdkEventButton*)event);
644 g_object_get(result, "context", &context, NULL);
645 if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE) {
646 Arg a = { .i = ModeInsert };
647 set(&a);
648 manual_focus = TRUE;
650 } else if (mode == ModeInsert && event->type == GDK_BUTTON_RELEASE) {
651 result = webkit_web_view_get_hit_test_result(WEBKIT_WEB_VIEW(widget), (GdkEventButton*)event);
652 g_object_get(result, "context", &context, NULL);
653 if (!(context & WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE)) {
654 Arg a = { .i = ModeNormal };
655 set(&a);
657 } else {
658 gchar *value = NULL, *message = NULL;
659 jsapi_evaluate_script("window.getSelection().focusNode", &value, &message);
660 if (value && !strcmp(value, "[object HTMLFormElement]")) {
661 Arg a = { .i = ModeInsert, .s = NULL };
662 set(&a);
663 manual_focus = TRUE;
665 g_free(value);
666 g_free(message);
668 return FALSE;
671 static gboolean inputbox_keyrelease_cb(GtkEntry *entry, GdkEventKey *event) {
672 Arg a;
673 guint16 length = gtk_entry_get_text_length(entry);
675 if (!length) {
676 a.i = HideCompletion;
677 complete(&a);
678 a.i = ModeNormal;
679 return set(&a);
681 return FALSE;
684 static gboolean inputbox_changed_cb(GtkEditable *entry, gpointer user_data) {
685 Arg a;
686 char *text = (char*)gtk_entry_get_text(GTK_ENTRY(entry));
687 guint16 length = gtk_entry_get_text_length(GTK_ENTRY(entry));
688 gboolean forward = FALSE;
690 /* Update incremental search if the user changes the search text.
692 * Note: gtk_widget_is_focus() is a poor way to check if the change comes
693 * from the user. But if the entry is focused and the text is set
694 * through gtk_entry_set_text() in some asyncrounous operation,
695 * I would consider that a bug.
698 if (gtk_widget_is_focus(GTK_WIDGET(entry)) && length > 1 && ((forward = text[0] == '/') || text[0] == '?')) {
699 webkit_web_view_unmark_text_matches(webview);
700 webkit_web_view_search_text(webview, &text[1], searchoptions & CaseSensitive, forward, searchoptions & Wrapping);
701 return TRUE;
702 } else if (gtk_widget_is_focus(GTK_WIDGET(entry)) && length >= 1 &&
703 (text[0] == '.' || text[0] == ',' || text[0] == ';')) {
704 a.i = Silent;
705 switch (text[0]) {
706 case '.':
707 a.s = g_strconcat("hints.createHints('", text + 1, "', 'f');", NULL);
708 break;
710 case ',':
711 a.s = g_strconcat("hints.createHints('", text + 1, "', 'F');", NULL);
712 break;
714 case ';':
715 a.s = NULL;
716 switch (text[1]) {
717 case 's':
718 a.s = g_strconcat("hints.createHints('", text + 2, "', 's');", NULL);
719 break;
720 case 'y':
721 a.s = g_strconcat("hints.createHints('", text + 2, "', 'y');", NULL);
722 break;
723 case 'o':
724 a.s = g_strconcat("hints.createHints('", text + 2, "', 'f');", NULL);
725 break;
726 case 't': case 'w':
727 a.s = g_strconcat("hints.createHints('", text + 2, "', 'F');", NULL);
728 break;
729 case 'O': case 'T': case 'W':
730 a.s = g_strconcat("hints.createHints('", text + 2, "', 'O');", NULL);
731 break;
732 case 'i':
733 a.s = g_strconcat("hints.createHints('", text + 2, "', 'i');", NULL);
734 break;
735 case 'I':
736 a.s = g_strconcat("hints.createHints('", text + 2, "', 'I');", NULL);
737 break;
739 break;
741 count = 0;
742 if (a.s) {
743 script(&a);
744 g_free(a.s);
747 return TRUE;
748 } else if (length == 0 && followTarget[0]) {
749 mode = ModeNormal;
750 a.i = Silent;
751 a.s = g_strdup("hints.clearHints();");
752 script(&a);
753 g_free(a.s);
754 count = 0;
755 update_state();
758 return FALSE;
761 /* funcs here */
763 void fill_suggline(char * suggline, const char * command, const char *fill_with) {
764 memset(suggline, 0, 512);
765 strncpy(suggline, command, 512);
766 strncat(suggline, " ", 1);
767 strncat(suggline, fill_with, 512 - strlen(suggline) - 1);
770 GtkWidget * fill_eventbox(const char * completion_line) {
771 GtkBox * row;
772 GtkWidget *row_eventbox, *el;
773 GdkColor color;
774 char *markup, *markup_tmp;
776 row = GTK_BOX(gtk_hbox_new(FALSE, 0));
777 row_eventbox = gtk_event_box_new();
778 gdk_color_parse(completionbgcolor[0], &color);
779 gtk_widget_modify_bg(row_eventbox, GTK_STATE_NORMAL, &color);
780 el = gtk_label_new(NULL);
781 markup_tmp = g_markup_escape_text(completion_line, strlen(completion_line));
782 markup = g_strconcat("<span font=\"", completionfont[0], "\" color=\"", completioncolor[0], "\">",
783 markup_tmp, "</span>", NULL);
784 gtk_label_set_markup(GTK_LABEL(el), markup);
785 g_free(markup_tmp);
786 g_free(markup);
787 gtk_misc_set_alignment(GTK_MISC(el), 0, 0);
788 gtk_box_pack_start(row, el, TRUE, TRUE, 2);
789 gtk_container_add(GTK_CONTAINER(row_eventbox), GTK_WIDGET(row));
790 return row_eventbox;
793 gboolean
794 complete(const Arg *arg) {
795 char *str, *p, *s, *markup, *entry, *searchfor, command[32] = "", suggline[512] = "", **suggurls;
796 size_t listlen, len, cmdlen;
797 int i, spacepos;
798 Listelement *elementlist = NULL, *elementpointer;
799 gboolean highlight = FALSE;
800 GtkBox *row;
801 GtkWidget *row_eventbox, *el;
802 GtkBox *_table;
803 GdkColor color;
804 static GtkWidget *table, *top_border;
805 static char *prefix;
806 static char **suggestions;
807 static GtkWidget **widgets;
808 static int n = 0, m, current = -1;
810 str = (char*)gtk_entry_get_text(GTK_ENTRY(inputbox));
811 len = strlen(str);
813 /* Get the length of the list of commands for completion. We need this to
814 * malloc/realloc correctly.
816 listlen = LENGTH(commands);
818 if ((len == 0 || str[0] != ':') && arg->i != HideCompletion)
819 return TRUE;
820 if (prefix) {
821 if (arg->i != HideCompletion && widgets && current != -1 && !strcmp(&str[1], suggestions[current])) {
822 gdk_color_parse(completionbgcolor[0], &color);
823 gtk_widget_modify_bg(widgets[current], GTK_STATE_NORMAL, &color);
824 current = (n + current + (arg->i == DirectionPrev ? -1 : 1)) % n;
825 if ((arg->i == DirectionNext && current == 0)
826 || (arg->i == DirectionPrev && current == n - 1))
827 current = -1;
828 } else {
829 free(widgets);
830 free(suggestions);
831 free(prefix);
832 gtk_widget_destroy(GTK_WIDGET(table));
833 gtk_widget_destroy(GTK_WIDGET(top_border));
834 table = NULL;
835 widgets = NULL;
836 suggestions = NULL;
837 prefix = NULL;
838 n = 0;
839 current = -1;
840 if (arg->i == HideCompletion)
841 return TRUE;
843 } else if (arg->i == HideCompletion)
844 return TRUE;
845 if (!widgets) {
846 prefix = g_strdup(str);
847 widgets = malloc(sizeof(GtkWidget*) * listlen);
848 suggestions = malloc(sizeof(char*) * listlen);
849 top_border = gtk_event_box_new();
850 gtk_widget_set_size_request(GTK_WIDGET(top_border), 0, 1);
851 gdk_color_parse(completioncolor[2], &color);
852 gtk_widget_modify_bg(top_border, GTK_STATE_NORMAL, &color);
853 table = gtk_event_box_new();
854 gdk_color_parse(completionbgcolor[0], &color);
855 _table = GTK_BOX(gtk_vbox_new(FALSE, 0));
856 highlight = len > 1;
857 if (strchr(str, ' ') == NULL) {
858 /* command completion */
859 listlen = LENGTH(commands);
860 for (i = 0; i < listlen; i++) {
861 if (commands[i].cmd == NULL)
862 break;
863 cmdlen = strlen(commands[i].cmd);
864 if (!highlight || (n < MAX_LIST_SIZE && len - 1 <= cmdlen && !strncmp(&str[1], commands[i].cmd, len - 1))) {
865 p = s = malloc(sizeof(char*) * (highlight ? sizeof(COMPLETION_TAG_OPEN) + sizeof(COMPLETION_TAG_CLOSE) - 1 : 1) + cmdlen);
866 if (highlight) {
867 memcpy(p, COMPLETION_TAG_OPEN, sizeof(COMPLETION_TAG_OPEN) - 1);
868 memcpy((p += sizeof(COMPLETION_TAG_OPEN) - 1), &str[1], len - 1);
869 memcpy((p += len - 1), COMPLETION_TAG_CLOSE, sizeof(COMPLETION_TAG_CLOSE) - 1);
870 p += sizeof(COMPLETION_TAG_CLOSE) - 1;
872 memcpy(p, &commands[i].cmd[len - 1], cmdlen - len + 2);
873 row = GTK_BOX(gtk_hbox_new(FALSE, 0));
874 row_eventbox = gtk_event_box_new();
875 gtk_widget_modify_bg(row_eventbox, GTK_STATE_NORMAL, &color);
876 el = gtk_label_new(NULL);
877 markup = g_strconcat("<span font=\"", completionfont[0], "\" color=\"", completioncolor[0], "\">", s, "</span>", NULL);
878 free(s);
879 gtk_label_set_markup(GTK_LABEL(el), markup);
880 g_free(markup);
881 gtk_misc_set_alignment(GTK_MISC(el), 0, 0);
882 gtk_box_pack_start(row, el, TRUE, TRUE, 2);
883 gtk_container_add(GTK_CONTAINER(row_eventbox), GTK_WIDGET(row));
884 gtk_box_pack_start(_table, GTK_WIDGET(row_eventbox), FALSE, FALSE, 0);
885 suggestions[n] = commands[i].cmd;
886 widgets[n++] = row_eventbox;
889 } else {
890 entry = (char *)malloc(512 * sizeof(char));
891 if (entry == NULL) {
892 return FALSE;
894 memset(entry, 0, 512);
895 suggurls = malloc(sizeof(char*) * listlen);
896 if (suggurls == NULL) {
897 return FALSE;
899 spacepos = strcspn(str, " ");
900 searchfor = (str + spacepos + 1);
901 strncpy(command, (str + 1), spacepos - 1);
902 if (strlen(command) == 3 && strncmp(command, "set", 3) == 0) {
903 /* browser settings */
904 listlen = LENGTH(browsersettings);
905 for (i = 0; i < listlen; i++) {
906 if (n < MAX_LIST_SIZE && strstr(browsersettings[i].name, searchfor) != NULL) {
907 /* match */
908 fill_suggline(suggline, command, browsersettings[i].name);
909 /* FIXME(HP): This memory is never freed */
910 suggurls[n] = (char *)malloc(sizeof(char) * 512 + 1);
911 strncpy(suggurls[n], suggline, 512);
912 suggestions[n] = suggurls[n];
913 row_eventbox = fill_eventbox(suggline);
914 gtk_box_pack_start(_table, GTK_WIDGET(row_eventbox), FALSE, FALSE, 0);
915 widgets[n++] = row_eventbox;
919 } else if (strlen(command) == 2 && strncmp(command, "qt", 2) == 0) {
920 /* completion on tags */
921 spacepos = strcspn(str, " ");
922 searchfor = (str + spacepos + 1);
923 elementlist = complete_list(searchfor, 1, elementlist);
924 } else {
925 /* URL completion: bookmarks */
926 elementlist = complete_list(searchfor, 0, elementlist);
927 m = count_list(elementlist);
928 if (m < MAX_LIST_SIZE) {
929 /* URL completion: history */
930 elementlist = complete_list(searchfor, 2, elementlist);
933 elementpointer = elementlist;
934 while (elementpointer != NULL) {
935 fill_suggline(suggline, command, elementpointer->element);
936 /* FIXME(HP): This memory is never freed */
937 suggurls[n] = (char *)malloc(sizeof(char) * 512 + 1);
938 strncpy(suggurls[n], suggline, 512);
939 suggestions[n] = suggurls[n];
940 row_eventbox = fill_eventbox(suggline);
941 gtk_box_pack_start(_table, GTK_WIDGET(row_eventbox), FALSE, FALSE, 0);
942 widgets[n++] = row_eventbox;
943 elementpointer = elementpointer->next;
944 if (n >= MAX_LIST_SIZE)
945 break;
947 free_list(elementlist);
948 if (suggurls != NULL) {
949 free(suggurls);
950 suggurls = NULL;
952 if (entry != NULL) {
953 free(entry);
954 entry = NULL;
957 /* TA: FIXME - this needs rethinking entirely. */
959 GtkWidget **widgets_temp = realloc(widgets, sizeof(*widgets) * n);
960 if (widgets_temp == NULL && widgets == NULL) {
961 fprintf(stderr, "Couldn't realloc() widgets\n");
962 exit(1);
964 widgets = widgets_temp;
965 char **suggestions_temp = realloc(suggestions, sizeof(*suggestions) * n);
966 if (suggestions_temp == NULL && suggestions == NULL) {
967 fprintf(stderr, "Couldn't realloc() suggestions\n");
968 exit(1);
970 suggestions = suggestions_temp;
972 if (!n) {
973 gdk_color_parse(completionbgcolor[1], &color);
974 gtk_widget_modify_bg(table, GTK_STATE_NORMAL, &color);
975 el = gtk_label_new(NULL);
976 gtk_misc_set_alignment(GTK_MISC(el), 0, 0);
977 markup = g_strconcat("<span font=\"", completionfont[1], "\" color=\"", completioncolor[1], "\">No Completions</span>", NULL);
978 gtk_label_set_markup(GTK_LABEL(el), markup);
979 g_free(markup);
980 gtk_box_pack_start(_table, GTK_WIDGET(el), FALSE, FALSE, 0);
982 gtk_box_pack_start(box, GTK_WIDGET(top_border), FALSE, FALSE, 0);
983 gtk_container_add(GTK_CONTAINER(table), GTK_WIDGET(_table));
984 gtk_box_pack_start(box, GTK_WIDGET(table), FALSE, FALSE, 0);
985 gtk_widget_show_all(GTK_WIDGET(window));
986 if (!n)
987 return TRUE;
988 current = arg->i == DirectionPrev ? n - 1 : 0;
990 if (current != -1) {
991 gdk_color_parse(completionbgcolor[2], &color);
992 gtk_widget_modify_bg(GTK_WIDGET(widgets[current]), GTK_STATE_NORMAL, &color);
993 s = g_strconcat(":", suggestions[current], NULL);
994 gtk_entry_set_text(GTK_ENTRY(inputbox), s);
995 g_free(s);
996 } else
997 gtk_entry_set_text(GTK_ENTRY(inputbox), prefix);
998 gtk_editable_set_position(GTK_EDITABLE(inputbox), -1);
999 return TRUE;
1002 gboolean
1003 descend(const Arg *arg) {
1004 char *source = (char*)webkit_web_view_get_uri(webview), *p = &source[0], *new;
1005 int i, len;
1006 count = count ? count : 1;
1008 if (!source)
1009 return TRUE;
1010 if (arg->i == Rootdir) {
1011 for (i = 0; i < 3; i++) /* get to the third slash */
1012 if (!(p = strchr(++p, '/')))
1013 return TRUE; /* if we cannot find it quit */
1014 } else {
1015 len = strlen(source);
1016 if (!len) /* if string is empty quit */
1017 return TRUE;
1018 p = source + len; /* start at the end */
1019 if (*(p - 1) == '/') /* /\/$/ is not an additional level */
1020 ++count;
1021 for (i = 0; i < count; i++)
1022 while(*(p--) != '/' || *p == '/') /* count /\/+/ as one slash */
1023 if (p == source) /* if we reach the first char pointer quit */
1024 return TRUE;
1025 ++p; /* since we do p-- in the while, we are pointing at
1026 the char before the slash, so +1 */
1028 len = p - source + 1; /* new length = end - start + 1 */
1029 new = malloc(len + 1);
1030 memcpy(new, source, len);
1031 new[len] = '\0';
1032 webkit_web_view_load_uri(webview, new);
1033 free(new);
1034 return TRUE;
1037 gboolean
1038 echo(const Arg *arg) {
1039 int index = !arg->s ? 0 : arg->i & (~NoAutoHide);
1041 if (index < Info || index > Error)
1042 return TRUE;
1044 if (!gtk_widget_is_focus(GTK_WIDGET(inputbox))) {
1045 set_widget_font_and_color(inputbox, urlboxfont[index], urlboxbgcolor[index], urlboxcolor[index]);
1046 gtk_entry_set_text(GTK_ENTRY(inputbox), !arg->s ? "" : arg->s);
1049 return TRUE;
1052 gboolean
1053 input(const Arg *arg) {
1054 int pos = 0;
1055 count = 0;
1056 const char *url;
1057 int index = Info;
1058 Arg a;
1060 /* if inputbox hidden, show it again */
1061 if (!gtk_widget_get_visible(inputbox))
1062 gtk_widget_set_visible(inputbox, TRUE);
1064 update_state();
1066 /* Set the colour and font back to the default, so that we don't still
1067 * maintain a red colour from a warning from an end of search indicator,
1068 * etc.
1070 set_widget_font_and_color(inputbox, urlboxfont[index], urlboxbgcolor[index], urlboxcolor[index]);
1072 /* to avoid things like :open URL :open URL2 or :open :open URL */
1073 gtk_entry_set_text(GTK_ENTRY(inputbox), "");
1074 gtk_editable_insert_text(GTK_EDITABLE(inputbox), arg->s, -1, &pos);
1075 if (arg->i & InsertCurrentURL && (url = webkit_web_view_get_uri(webview)))
1076 gtk_editable_insert_text(GTK_EDITABLE(inputbox), url, -1, &pos);
1078 gtk_widget_grab_focus(inputbox);
1079 gtk_editable_set_position(GTK_EDITABLE(inputbox), -1);
1081 if (arg->s[0] == '.' || arg->s[0] == ',' || arg->s[0] == ';') {
1082 mode = ModeHints;
1083 memset(followTarget, 0, 8);
1084 strncpy(followTarget, "current", 8);
1085 a.i = Silent;
1086 switch (arg->s[0]) {
1087 case '.':
1088 a.s = g_strdup("hints.createHints('', 'f');");
1089 break;
1091 case ',':
1092 a.s = g_strdup("hints.createHints('', 'F');");
1093 break;
1095 case ';':
1096 a.s = NULL;
1097 if (arg->s[1]) {
1098 switch (arg->s[1]) {
1099 case 's':
1100 a.s = g_strdup("hints.createHints('', 's');");
1101 break;
1102 case 'y':
1103 a.s = g_strdup("hints.createHints('', 'y');");
1104 break;
1105 case 'o':
1106 a.s = g_strdup("hints.createHints('', 'f');");
1107 break;
1108 case 't': case 'w':
1109 a.s = g_strdup("hints.createHints('', 'F');");
1110 break;
1111 case 'O': case 'T': case 'W':
1112 a.s = g_strdup("hints.createHints('', 'O');");
1113 break;
1114 case 'i':
1115 a.s = g_strdup("hints.createHints('', 'i');");
1116 break;
1117 case 'I':
1118 a.s = g_strdup("hints.createHints('', 'I');");
1119 break;
1122 break;
1124 count = 0;
1125 if (a.s) {
1126 script(&a);
1127 g_free(a.s);
1131 return TRUE;
1134 gboolean
1135 navigate(const Arg *arg) {
1136 if (arg->i & NavigationForwardBack)
1137 webkit_web_view_go_back_or_forward(webview, (arg->i == NavigationBack ? -1 : 1) * (count ? count : 1));
1138 else if (arg->i & NavigationReloadActions)
1139 (arg->i == NavigationReload ? webkit_web_view_reload : webkit_web_view_reload_bypass_cache)(webview);
1140 else
1141 webkit_web_view_stop_loading(webview);
1142 return TRUE;
1145 gboolean
1146 number(const Arg *arg) {
1147 const char *source = webkit_web_view_get_uri(webview);
1148 char *uri, *p, *new;
1149 int number, diff = (count ? count : 1) * (arg->i == Increment ? 1 : -1);
1151 if (!source)
1152 return TRUE;
1153 uri = g_strdup(source); /* copy string */
1154 p =& uri[0];
1155 while(*p != '\0') /* goto the end of the string */
1156 ++p;
1157 --p;
1158 while(*p >= '0' && *p <= '9') /* go back until non number char is reached */
1159 --p;
1160 if (*(++p) == '\0') { /* if no numbers were found abort */
1161 free(uri);
1162 return TRUE;
1164 number = atoi(p) + diff; /* apply diff on number */
1165 *p = '\0';
1166 new = g_strdup_printf("%s%d", uri, number); /* create new uri */
1167 webkit_web_view_load_uri(webview, new);
1168 g_free(new);
1169 free(uri);
1170 return TRUE;
1173 gboolean
1174 open_arg(const Arg *arg) {
1175 char *argv[64];
1176 char *s = arg->s, *p = NULL, *new;
1177 Arg a = { .i = NavigationReload };
1178 int len;
1179 char *search_uri, *search_term;
1181 if (embed) {
1182 argv[0] = *args;
1183 argv[1] = "-e";
1184 argv[2] = winid;
1185 argv[3] = arg->s;
1186 argv[4] = NULL;
1187 } else {
1188 argv[0] = *args;
1189 argv[1] = arg->s;
1190 argv[2] = NULL;
1193 if (!arg->s)
1194 navigate(&a);
1195 else if (arg->i == TargetCurrent) {
1196 while(*s == ' ') /* strip leading whitespace */
1197 ++s;
1198 p = (s + strlen(s) - 1);
1199 while(*p == ' ') /* strip trailing whitespace */
1200 --p;
1201 *(p + 1) = '\0';
1202 len = strlen(s);
1203 new = NULL;
1204 /* check for external handlers */
1205 if (open_handler(s))
1206 return TRUE;
1207 /* check for search engines */
1208 p = strchr(s, ' ');
1209 if (p) { /* check for search engines */
1210 *p = '\0';
1211 search_uri = find_uri_for_searchengine(s);
1212 if (search_uri != NULL) {
1213 search_term = soup_uri_encode(p+1, "&");
1214 new = g_strdup_printf(search_uri, search_term);
1215 g_free(search_term);
1217 *p = ' ';
1219 if (!new) {
1220 if (len > 3 && strstr(s, "://")) { /* valid url? */
1221 p = new = g_malloc(len + 1);
1222 while(*s != '\0') { /* strip whitespaces */
1223 if (*s != ' ')
1224 *(p++) = *s;
1225 ++s;
1227 *p = '\0';
1228 } else if (strcspn(s, "/") == 0 || strcspn(s, "./") == 0) { /* prepend "file://" */
1229 new = g_malloc(sizeof("file://") + len);
1230 strcpy(new, "file://");
1231 memcpy(&new[sizeof("file://") - 1], s, len + 1);
1232 } else if (p || !strchr(s, '.')) { /* whitespaces or no dot? */
1233 search_uri = find_uri_for_searchengine(defaultsearch);
1234 if (search_uri != NULL) {
1235 search_term = soup_uri_encode(s, "&");
1236 new = g_strdup_printf(search_uri, search_term);
1237 g_free(search_term);
1239 } else { /* prepend "http://" */
1240 new = g_malloc(sizeof("http://") + len);
1241 strcpy(new, "http://");
1242 memcpy(&new[sizeof("http://") - 1], s, len + 1);
1245 webkit_web_view_load_uri(webview, new);
1246 g_free(new);
1247 } else
1248 g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
1249 return TRUE;
1252 gboolean
1253 open_remembered(const Arg *arg)
1255 Arg a = {arg->i, rememberedURI};
1257 if (strcmp(rememberedURI, "")) {
1258 open_arg(&a);
1260 return TRUE;
1263 gboolean
1264 yank(const Arg *arg) {
1265 const char *url, *feedback, *content;
1267 if (arg->i & SourceSelection) {
1268 webkit_web_view_copy_clipboard(webview);
1269 if (arg->i & ClipboardPrimary)
1270 content = gtk_clipboard_wait_for_text(clipboards[0]);
1271 if (!content && arg->i & ClipboardGTK)
1272 content = gtk_clipboard_wait_for_text(clipboards[1]);
1273 if (content) {
1274 feedback = g_strconcat("Yanked ", content, NULL);
1275 g_free((gpointer *)content);
1276 give_feedback(feedback);
1277 g_free((gpointer *)feedback);
1279 } else {
1280 if (arg->i & SourceURL) {
1281 url = webkit_web_view_get_uri(webview);
1282 } else {
1283 url = arg->s;
1285 if (!url)
1286 return TRUE;
1287 feedback = g_strconcat("Yanked ", url, NULL);
1288 give_feedback(feedback);
1289 if (arg->i & ClipboardPrimary)
1290 gtk_clipboard_set_text(clipboards[0], url, -1);
1291 if (arg->i & ClipboardGTK)
1292 gtk_clipboard_set_text(clipboards[1], url, -1);
1294 return TRUE;
1297 gboolean
1298 paste(const Arg *arg) {
1299 Arg a = { .i = arg->i & TargetNew, .s = NULL };
1301 /* If we're over a link, open it in a new target. */
1302 if (strlen(rememberedURI) > 0) {
1303 Arg new_target = { .i = TargetNew, .s = arg->s };
1304 open_arg(&new_target);
1305 return TRUE;
1308 if (arg->i & ClipboardPrimary)
1309 a.s = gtk_clipboard_wait_for_text(clipboards[0]);
1310 if (!a.s && arg->i & ClipboardGTK)
1311 a.s = gtk_clipboard_wait_for_text(clipboards[1]);
1312 if (a.s) {
1313 open_arg(&a);
1314 g_free(a.s);
1316 return TRUE;
1319 gboolean
1320 quit(const Arg *arg) {
1321 FILE *f;
1322 const char *filename;
1323 const char *uri = webkit_web_view_get_uri(webview);
1324 if (uri != NULL) {
1325 /* write last URL into status file for recreation with "u" */
1326 filename = g_strdup_printf(CLOSED_URL_FILENAME);
1327 f = fopen(filename, "w");
1328 g_free((gpointer *)filename);
1329 if (f != NULL) {
1330 fprintf(f, "%s", uri);
1331 fclose(f);
1334 gtk_main_quit();
1335 return TRUE;
1338 gboolean
1339 revive(const Arg *arg) {
1340 FILE *f;
1341 const char *filename;
1342 char buffer[512] = "";
1343 Arg a = { .i = TargetNew, .s = NULL };
1344 /* get the URL of the window which has been closed last */
1345 filename = g_strdup_printf(CLOSED_URL_FILENAME);
1346 f = fopen(filename, "r");
1347 g_free((gpointer *)filename);
1348 if (f != NULL) {
1349 fgets(buffer, 512, f);
1350 fclose(f);
1352 if (strlen(buffer) > 0) {
1353 a.s = buffer;
1354 open_arg(&a);
1355 return TRUE;
1357 return FALSE;
1360 static
1361 gboolean print_frame(const Arg *arg)
1363 WebKitWebFrame *frame = webkit_web_view_get_main_frame(webview);
1364 webkit_web_frame_print (frame);
1365 return TRUE;
1368 gboolean
1369 search(const Arg *arg) {
1370 count = count ? count : 1;
1371 gboolean success, direction = arg->i & DirectionPrev;
1372 Arg a;
1374 if (arg->s) {
1375 free(search_handle);
1376 search_handle = g_strdup(arg->s);
1378 if (!search_handle)
1379 return TRUE;
1380 if (arg->i & DirectionAbsolute)
1381 search_direction = direction;
1382 else
1383 direction ^= search_direction;
1384 do {
1385 success = webkit_web_view_search_text(webview, search_handle, arg->i & CaseSensitive, direction, FALSE);
1386 if (!success) {
1387 if (arg->i & Wrapping) {
1388 success = webkit_web_view_search_text(webview, search_handle, arg->i & CaseSensitive, direction, TRUE);
1389 if (success) {
1390 a.i = Warning;
1391 a.s = g_strdup_printf("search hit %s, continuing at %s",
1392 direction ? "BOTTOM" : "TOP",
1393 direction ? "TOP" : "BOTTOM");
1394 echo(&a);
1395 g_free(a.s);
1396 } else
1397 break;
1398 } else
1399 break;
1401 } while(--count);
1402 if (!success) {
1403 a.i = Error;
1404 a.s = g_strdup_printf("Pattern not found: %s", search_handle);
1405 echo(&a);
1406 g_free(a.s);
1408 return TRUE;
1411 gboolean
1412 set(const Arg *arg) {
1413 Arg a = { .i = Info | NoAutoHide };
1415 switch (arg->i) {
1416 case ModeNormal:
1417 if (search_handle) {
1418 search_handle = NULL;
1419 webkit_web_view_unmark_text_matches(webview);
1421 gtk_entry_set_text(GTK_ENTRY(inputbox), "");
1422 gtk_widget_grab_focus(GTK_WIDGET(webview));
1423 break;
1424 case ModePassThrough:
1425 a.s = g_strdup("-- PASS THROUGH --");
1426 echo(&a);
1427 g_free(a.s);
1428 break;
1429 case ModeSendKey:
1430 a.s = g_strdup("-- PASS TROUGH (next) --");
1431 echo(&a);
1432 g_free(a.s);
1433 break;
1434 case ModeInsert: /* should not be called manually but automatically */
1435 a.s = g_strdup("-- INSERT --");
1436 echo(&a);
1437 g_free(a.s);
1438 break;
1439 default:
1440 return TRUE;
1442 mode = arg->i;
1443 return TRUE;
1446 gchar*
1447 jsapi_ref_to_string(JSContextRef context, JSValueRef ref) {
1448 JSStringRef string_ref;
1449 gchar *string;
1450 size_t length;
1452 string_ref = JSValueToStringCopy(context, ref, NULL);
1453 length = JSStringGetMaximumUTF8CStringSize(string_ref);
1454 string = g_new(gchar, length);
1455 JSStringGetUTF8CString(string_ref, string, length);
1456 JSStringRelease(string_ref);
1457 return string;
1460 void
1461 jsapi_evaluate_script(const gchar *script, gchar **value, gchar **message) {
1462 WebKitWebFrame *frame = webkit_web_view_get_main_frame(webview);
1463 JSGlobalContextRef context = webkit_web_frame_get_global_context(frame);
1464 JSStringRef str;
1465 JSValueRef val, exception;
1467 str = JSStringCreateWithUTF8CString(script);
1468 val = JSEvaluateScript(context, str, JSContextGetGlobalObject(context), NULL, 0, &exception);
1469 JSStringRelease(str);
1470 if (!val)
1471 *message = jsapi_ref_to_string(context, exception);
1472 else
1473 *value = jsapi_ref_to_string(context, val);
1476 gboolean
1477 quickmark(const Arg *a) {
1478 int i, b;
1479 b = atoi(a->s);
1480 char *fn = g_strdup_printf(QUICKMARK_FILE);
1481 FILE *fp;
1482 fp = fopen(fn, "r");
1483 g_free(fn);
1484 fn = NULL;
1485 char buf[100];
1487 if (fp != NULL && b < 10) {
1488 for( i=0; i < b; ++i ) {
1489 if (feof(fp)) {
1490 break;
1492 fgets(buf, 100, fp);
1494 char *ptr = strrchr(buf, '\n');
1495 *ptr = '\0';
1496 Arg x = { .s = buf };
1497 if (strlen(buf))
1498 return open_arg(&x);
1499 else {
1500 x.i = Error;
1501 x.s = g_strdup_printf("Quickmark %d not defined", b);
1502 echo(&x);
1503 g_free(x.s);
1504 return false;
1506 } else { return false; }
1509 gboolean
1510 script(const Arg *arg) {
1511 gchar *value = NULL, *message = NULL;
1512 char text[1024] = "";
1513 Arg a;
1514 WebKitNetworkRequest *request;
1515 WebKitDownload *download;
1517 if (!arg->s) {
1518 set_error("Missing argument.");
1519 return FALSE;
1521 jsapi_evaluate_script(arg->s, &value, &message);
1522 if (message) {
1523 set_error(message);
1524 g_free(message);
1525 return FALSE;
1527 g_free(message);
1528 if (arg->i != Silent && value) {
1529 a.i = arg->i;
1530 a.s = g_strdup(value);
1531 echo(&a);
1532 g_free(a.s);
1534 /* switch mode according to scripts return value */
1535 if (value) {
1536 if (strncmp(value, "done;", 5) == 0) {
1537 a.i = ModeNormal;
1538 set(&a);
1539 } else if (strncmp(value, "insert;", 7) == 0) {
1540 a.i = ModeInsert;
1541 set(&a);
1542 manual_focus = TRUE;
1543 } else if (strncmp(value, "save;", 5) == 0) {
1544 /* forced download */
1545 a.i = ModeNormal;
1546 set(&a);
1547 request = webkit_network_request_new((value + 5));
1548 download = webkit_download_new(request);
1549 webview_download_cb(webview, download, (gpointer *)NULL);
1550 } else if (strncmp(value, "yank;", 5) == 0) {
1551 /* yank link URL to clipboard */
1552 a.i = ModeNormal;
1553 set(&a);
1554 a.i = ClipboardPrimary | ClipboardGTK;
1555 a.s = (value + 5);
1556 yank(&a);
1557 } else if (strncmp(value, "colon;", 6) == 0) {
1558 /* use link URL for colon command */
1559 strncpy(text, (char *)gtk_entry_get_text(GTK_ENTRY(inputbox)), 1023);
1560 a.i = ModeNormal;
1561 set(&a);
1562 switch (text[1]) {
1563 case 'O':
1564 a.s = g_strconcat(":open ", (value + 6), NULL);
1565 break;
1566 case 'T': case 'W':
1567 a.s = g_strconcat(":tabopen ", (value + 6), NULL);
1568 break;
1570 if (a.s) {
1571 input(&a);
1572 g_free(a.s);
1574 } else if (strncmp(value, "error;", 6) == 0) {
1575 a.i = Error;
1576 set(&a);
1579 g_free(value);
1580 return TRUE;
1583 gboolean
1584 scroll(const Arg *arg) {
1585 GtkAdjustment *adjust = (arg->i & OrientationHoriz) ? adjust_h : adjust_v;
1586 int max = gtk_adjustment_get_upper(adjust) - gtk_adjustment_get_page_size(adjust);
1587 float val = gtk_adjustment_get_value(adjust) / max * 100;
1588 int direction = (arg->i & (1 << 2)) ? 1 : -1;
1590 if ((direction == 1 && val < 100) || (direction == -1 && val > 0)) {
1591 if (arg->i & ScrollMove)
1592 gtk_adjustment_set_value(adjust, gtk_adjustment_get_value(adjust) +
1593 direction * /* direction */
1594 ((arg->i & UnitLine || (arg->i & UnitBuffer && count)) ? (scrollstep * (count ? count : 1)) : (
1595 arg->i & UnitBuffer ? gtk_adjustment_get_page_size(adjust) / 2 :
1596 (count ? count : 1) * (gtk_adjustment_get_page_size(adjust) -
1597 (gtk_adjustment_get_page_size(adjust) > pagingkeep ? pagingkeep : 0)))));
1598 else
1599 gtk_adjustment_set_value(adjust,
1600 ((direction == 1) ? gtk_adjustment_get_upper : gtk_adjustment_get_lower)(adjust));
1601 update_state();
1603 return TRUE;
1606 gboolean
1607 zoom(const Arg *arg) {
1608 webkit_web_view_set_full_content_zoom(webview, (arg->i & ZoomFullContent) > 0);
1609 webkit_web_view_set_zoom_level(webview, (arg->i & ZoomOut) ?
1610 webkit_web_view_get_zoom_level(webview) +
1611 (((float)(count ? count : 1)) * (arg->i & (1 << 1) ? 1.0 : -1.0) * zoomstep) :
1612 (count ? (float)count / 100.0 : 1.0));
1613 return TRUE;
1616 gboolean
1617 fake_key_event(const Arg *a) {
1618 if(!embed) {
1619 return FALSE;
1621 Arg err;
1622 err.i = Error;
1623 Display *xdpy;
1624 if ( (xdpy = XOpenDisplay(NULL)) == NULL ) {
1625 err.s = g_strdup("Couldn't find the XDisplay.");
1626 echo(&err);
1627 g_free(err.s);
1628 return FALSE;
1631 XKeyEvent xk;
1632 xk.display = xdpy;
1633 xk.subwindow = None;
1634 xk.time = CurrentTime;
1635 xk.same_screen = True;
1636 xk.x = xk.y = xk.x_root = xk.y_root = 1;
1637 xk.window = embed;
1638 xk.state = a->i;
1640 if( ! a->s ) {
1641 err.s = g_strdup("Zero pointer as argument! Check your config.h");
1642 echo(&err);
1643 g_free(err.s);
1644 return FALSE;
1647 KeySym keysym;
1648 if( (keysym = XStringToKeysym(a->s)) == NoSymbol ) {
1649 err.s = g_strdup_printf("Couldn't translate %s to keysym", a->s );
1650 echo(&err);
1651 g_free(err.s);
1652 return FALSE;
1655 if( (xk.keycode = XKeysymToKeycode(xdpy, keysym)) == NoSymbol ) {
1656 err.s = g_strdup("Couldn't translate keysym to keycode");
1657 echo(&err);
1658 g_free(err.s);
1659 return FALSE;
1662 xk.type = KeyPress;
1663 if( !XSendEvent(xdpy, embed, True, KeyPressMask, (XEvent *)&xk) ) {
1664 err.s = g_strdup("XSendEvent failed");
1665 echo(&err);
1666 g_free(err.s);
1667 return FALSE;
1669 XFlush(xdpy);
1671 return TRUE;
1675 gboolean
1676 commandhistoryfetch(const Arg *arg) {
1677 const int length = g_list_length(commandhistory);
1679 if (length > 0) {
1680 if (arg->i == DirectionPrev) {
1681 commandpointer = (length + commandpointer - 1) % length;
1682 } else {
1683 commandpointer = (length + commandpointer + 1) % length;
1686 const char* command = (char *)g_list_nth_data(commandhistory, commandpointer);
1687 gtk_entry_set_text(GTK_ENTRY(inputbox), g_strconcat(":", command, NULL));
1688 gtk_editable_set_position(GTK_EDITABLE(inputbox), -1);
1689 return TRUE;
1692 return FALSE;
1695 gboolean
1696 bookmark(const Arg *arg) {
1697 FILE *f;
1698 const char *filename;
1699 const char *uri = webkit_web_view_get_uri(webview);
1700 const char *title = webkit_web_view_get_title(webview);
1701 filename = g_strdup_printf(BOOKMARKS_STORAGE_FILENAME);
1702 f = fopen(filename, "a");
1703 g_free((gpointer *)filename);
1704 if (uri == NULL || strlen(uri) == 0) {
1705 set_error("No URI found to bookmark.");
1706 return FALSE;
1708 if (f != NULL) {
1709 fprintf(f, "%s", uri);
1710 if (title != NULL) {
1711 fprintf(f, "%s", " ");
1712 fprintf(f, "%s", title);
1714 if (arg->s && strlen(arg->s)) {
1715 build_taglist(arg, f);
1717 fprintf(f, "%s", "\n");
1718 fclose(f);
1719 give_feedback( "Bookmark saved" );
1720 return TRUE;
1721 } else {
1722 set_error("Bookmarks file not found.");
1723 return FALSE;
1727 gboolean
1728 history() {
1729 FILE *f;
1730 const char *filename;
1731 const char *uri = webkit_web_view_get_uri(webview);
1732 const char *title = webkit_web_view_get_title(webview);
1733 char *entry, buffer[512], *new;
1734 int n, i = 0;
1735 gboolean finished = FALSE;
1736 if (uri != NULL) {
1737 if (title != NULL) {
1738 entry = malloc((strlen(uri) + strlen(title) + 2) * sizeof(char));
1739 memset(entry, 0, strlen(uri) + strlen(title) + 2);
1740 } else {
1741 entry = malloc((strlen(uri) + 1) * sizeof(char));
1742 memset(entry, 0, strlen(uri) + 1);
1744 if (entry != NULL) {
1745 strncpy(entry, uri, strlen(uri));
1746 if (title != NULL) {
1747 strncat(entry, " ", 1);
1748 strncat(entry, title, strlen(title));
1750 n = strlen(entry);
1751 filename = g_strdup_printf(HISTORY_STORAGE_FILENAME);
1752 f = fopen(filename, "r");
1753 if (f != NULL) {
1754 new = (char *)malloc(HISTORY_MAX_ENTRIES * 512 * sizeof(char) + 1);
1755 if (new != NULL) {
1756 memset(new, 0, HISTORY_MAX_ENTRIES * 512 * sizeof(char) + 1);
1757 /* newest entries go on top */
1758 strncpy(new, entry, strlen(entry));
1759 strncat(new, "\n", 1);
1760 /* retain at most HISTORY_MAX_ENTIRES - 1 old entries */
1761 while (finished != TRUE) {
1762 if ((char *)NULL == fgets(buffer, 512, f)) {
1763 /* check if end of file was reached / error occured */
1764 if (!feof(f)) {
1765 break;
1767 /* end of file reached */
1768 finished = TRUE;
1769 continue;
1771 /* compare line (-1 because of newline character) */
1772 if (n != strlen(buffer) - 1 || strncmp(entry, buffer, n) != 0) {
1773 /* if the URI is already in history; we put it on top and skip it here */
1774 strncat(new, buffer, 512);
1775 i++;
1777 if ((i + 1) >= HISTORY_MAX_ENTRIES) {
1778 break;
1781 fclose(f);
1783 f = fopen(filename, "w");
1784 g_free((gpointer *)filename);
1785 if (f != NULL) {
1786 fprintf(f, "%s", new);
1787 fclose(f);
1789 if (new != NULL) {
1790 free(new);
1791 new = NULL;
1795 if (entry != NULL) {
1796 free(entry);
1797 entry = NULL;
1800 return TRUE;
1803 static gboolean
1804 view_source(const Arg * arg) {
1805 gboolean current_mode = webkit_web_view_get_view_source_mode(webview);
1806 webkit_web_view_set_view_source_mode(webview, !current_mode);
1807 webkit_web_view_reload(webview);
1808 return TRUE;
1811 static gboolean
1812 focus_input(const Arg *arg) {
1813 static Arg a;
1815 a.s = g_strdup("hints.focusInput();");
1816 a.i = Silent;
1817 script(&a);
1818 g_free(a.s);
1819 update_state();
1820 manual_focus = TRUE;
1821 return TRUE;
1824 static gboolean
1825 browser_settings(const Arg *arg) {
1826 char line[255];
1827 if (!arg->s) {
1828 set_error("Missing argument.");
1829 return FALSE;
1831 strncpy(line, arg->s, 254);
1832 if (process_set_line(line))
1833 return TRUE;
1834 else {
1835 set_error("Invalid setting.");
1836 return FALSE;
1840 char *
1841 search_word(int whichword) {
1842 int k = 0;
1843 static char word[240];
1844 char *c = my_pair.line;
1846 while (isspace(*c) && *c)
1847 c++;
1849 switch (whichword) {
1850 case 0:
1851 while (*c && !isspace (*c) && *c != '=' && k < 240) {
1852 word[k++] = *c;
1853 c++;
1855 word[k] = '\0';
1856 strncpy(my_pair.what, word, 20);
1857 break;
1858 case 1:
1859 while (*c && k < 240) {
1860 word[k++] = *c;
1861 c++;
1863 word[k] = '\0';
1864 strncpy(my_pair.value, word, 240);
1865 break;
1868 return c;
1871 static gboolean
1872 process_set_line(char *line) {
1873 char *c;
1874 int listlen, i;
1875 gboolean boolval;
1876 WebKitWebSettings *settings;
1878 settings = webkit_web_view_get_settings(webview);
1879 my_pair.line = line;
1880 c = search_word(0);
1881 if (!strlen(my_pair.what))
1882 return FALSE;
1884 while (isspace(*c) && *c)
1885 c++;
1887 if (*c == ':' || *c == '=')
1888 c++;
1890 my_pair.line = c;
1891 c = search_word(1);
1893 listlen = LENGTH(browsersettings);
1894 for (i = 0; i < listlen; i++) {
1895 if (strlen(browsersettings[i].name) == strlen(my_pair.what) && strncmp(browsersettings[i].name, my_pair.what, strlen(my_pair.what)) == 0) {
1896 /* mandatory argument not provided */
1897 if (strlen(my_pair.value) == 0)
1898 return FALSE;
1899 /* process qmark? */
1900 if (strlen(my_pair.what) == 5 && strncmp("qmark", my_pair.what, 5) == 0) {
1901 return (process_save_qmark(my_pair.value, webview));
1903 /* interpret boolean values */
1904 if (browsersettings[i].boolval) {
1905 if (strncmp(my_pair.value, "on", 2) == 0 || strncmp(my_pair.value, "true", 4) == 0 || strncmp(my_pair.value, "ON", 2) == 0 || strncmp(my_pair.value, "TRUE", 4) == 0) {
1906 boolval = TRUE;
1907 } else if (strncmp(my_pair.value, "off", 3) == 0 || strncmp(my_pair.value, "false", 5) == 0 || strncmp(my_pair.value, "OFF", 3) == 0 || strncmp(my_pair.value, "FALSE", 5) == 0) {
1908 boolval = FALSE;
1909 } else {
1910 return FALSE;
1912 } else if (browsersettings[i].colourval) {
1913 /* interpret as hexadecimal colour */
1914 if (!parse_colour(my_pair.value)) {
1915 return FALSE;
1918 if (browsersettings[i].var != NULL) {
1919 strncpy(browsersettings[i].var, my_pair.value, MAX_SETTING_SIZE);
1920 if (strlen(my_pair.value) > MAX_SETTING_SIZE - 1) {
1921 /* in this case, \0 will not have been copied */
1922 browsersettings[i].var[MAX_SETTING_SIZE - 1] = '\0';
1923 /* in case this string is also used for a webkit setting, make sure it's consistent */
1924 my_pair.value[MAX_SETTING_SIZE - 1] = '\0';
1925 give_feedback("String too long; automatically truncated!");
1928 if (strlen(browsersettings[i].webkit) > 0) {
1929 /* activate appropriate webkit setting */
1930 if (browsersettings[i].boolval) {
1931 g_object_set((GObject*)settings, browsersettings[i].webkit, boolval, NULL);
1932 } else if (browsersettings[i].intval) {
1933 g_object_set((GObject*)settings, browsersettings[i].webkit, atoi(my_pair.value), NULL);
1934 } else {
1935 g_object_set((GObject*)settings, browsersettings[i].webkit, my_pair.value, NULL);
1937 webkit_web_view_set_settings(webview, settings);
1940 if (strlen(my_pair.what) == 14) {
1941 if (strncmp("acceptlanguage", my_pair.what, 14) == 0) {
1942 g_object_set(G_OBJECT(session), "accept-language", acceptlanguage, NULL);
1943 } else if (strncmp("completioncase", my_pair.what, 14) == 0) {
1944 complete_case_sensitive = boolval;
1946 } else if (strlen(my_pair.what) == 5 && strncmp("proxy", my_pair.what, 5) == 0) {
1947 toggle_proxy(boolval);
1948 } else if (strlen(my_pair.what) == 10 && strncmp("scrollbars", my_pair.what, 10) == 0) {
1949 toggle_scrollbars(boolval);
1950 } else if (strlen(my_pair.what) == 9 && strncmp("statusbar", my_pair.what, 9) == 0) {
1951 gtk_widget_set_visible(GTK_WIDGET(statusbar), boolval);
1952 } else if (strlen(my_pair.what) == 8 && strncmp("inputbox", my_pair.what, 8) == 0) {
1953 gtk_widget_set_visible(inputbox, boolval);
1954 } else if (strlen(my_pair.what) == 11 && strncmp("escapeinput", my_pair.what, 11) == 0) {
1955 escape_input_on_load = boolval;
1958 /* SSL certificate checking */
1959 if (strlen(my_pair.what) == 9 && strncmp("strictssl", my_pair.what, 9) == 0) {
1960 if (boolval) {
1961 strict_ssl = TRUE;
1962 g_object_set(G_OBJECT(session), "ssl-strict", TRUE, NULL);
1963 } else {
1964 strict_ssl = FALSE;
1965 g_object_set(G_OBJECT(session), "ssl-strict", FALSE, NULL);
1968 if (strlen(my_pair.what) == 8 && strncmp("cabundle", my_pair.what, 8) == 0) {
1969 g_object_set(G_OBJECT(session), SOUP_SESSION_SSL_CA_FILE, ca_bundle, NULL);
1972 /* reload page? */
1973 if (browsersettings[i].reload)
1974 webkit_web_view_reload(webview);
1975 return TRUE;
1978 return FALSE;
1981 gboolean
1982 process_line(char *line) {
1983 char *c = line, *command_hist;
1984 int i;
1985 size_t len, length = strlen(line);
1986 gboolean found = FALSE, success = FALSE;
1987 Arg a;
1989 while (isspace(*c))
1990 c++;
1991 /* Ignore blank lines. */
1992 if (c[0] == '\0')
1993 return TRUE;
1995 command_hist = g_strdup(c);
1996 for (i = 0; i < LENGTH(commands); i++) {
1997 if (commands[i].cmd == NULL)
1998 break;
1999 len = strlen(commands[i].cmd);
2000 if (length >= len && !strncmp(c, commands[i].cmd, len) && (c[len] == ' ' || !c[len])) {
2001 found = TRUE;
2002 a.i = commands[i].arg.i;
2003 a.s = g_strdup(length > len + 1 ? &c[len + 1] : commands[i].arg.s);
2004 success = commands[i].func(&a);
2005 g_free(a.s);
2006 break;
2010 save_command_history(command_hist);
2011 g_free(command_hist);
2013 if (!found) {
2014 a.i = Error;
2015 a.s = g_strdup_printf("Not a browser command: %s", c);
2016 echo(&a);
2017 g_free(a.s);
2018 } else if (!success) {
2019 a.i = Error;
2020 if (error_msg != NULL) {
2021 a.s = g_strdup_printf("%s", error_msg);
2022 g_free(error_msg);
2023 error_msg = NULL;
2024 } else {
2025 a.s = g_strdup_printf("Unknown error. Please file a bug report!");
2027 echo(&a);
2028 g_free(a.s);
2030 return success;
2033 static gboolean
2034 search_tag(const Arg * a) {
2035 FILE *f;
2036 const char *filename;
2037 const char *tag = a->s;
2038 char s[BUFFERSIZE], foundtag[40], url[BUFFERSIZE];
2039 int t, i, intag, k;
2041 if (!tag) {
2042 /* The user must give us something to load up. */
2043 set_error("Bookmark tag required with this option.");
2044 return FALSE;
2047 if (strlen(tag) > MAXTAGSIZE) {
2048 set_error("Tag too long.");
2049 return FALSE;
2052 filename = g_strdup_printf(BOOKMARKS_STORAGE_FILENAME);
2053 f = fopen(filename, "r");
2054 g_free((gpointer *)filename);
2055 if (f == NULL) {
2056 set_error("Couldn't open bookmarks file.");
2057 return FALSE;
2059 while (fgets(s, BUFFERSIZE-1, f)) {
2060 intag = 0;
2061 t = strlen(s) - 1;
2062 while (isspace(s[t]))
2063 t--;
2064 if (s[t] != ']') continue;
2065 while (t > 0) {
2066 if (s[t] == ']') {
2067 if (!intag)
2068 intag = t;
2069 else
2070 intag = 0;
2071 } else {
2072 if (s[t] == '[') {
2073 if (intag) {
2074 i = 0;
2075 k = t + 1;
2076 while (k < intag)
2077 foundtag[i++] = s[k++];
2078 foundtag[i] = '\0';
2079 /* foundtag now contains the tag */
2080 if (strlen(foundtag) < MAXTAGSIZE && strcmp(tag, foundtag) == 0) {
2081 i = 0;
2082 while (isspace(s[i])) i++;
2083 k = 0;
2084 while (s[i] && !isspace(s[i])) url[k++] = s[i++];
2085 url[k] = '\0';
2086 Arg x = { .i = TargetNew, .s = url };
2087 open_arg(&x);
2090 intag = 0;
2093 t--;
2096 return TRUE;
2099 void
2100 toggle_proxy(gboolean onoff) {
2101 SoupURI *proxy_uri;
2102 char *filename, *new;
2104 if (onoff == FALSE) {
2105 g_object_set(session, "proxy-uri", NULL, NULL);
2106 } else {
2107 filename = (char *)g_getenv("http_proxy");
2109 /* Fallthrough to checking HTTP_PROXY as well, since this can also be
2110 * defined.
2112 if (filename == NULL)
2113 filename = (char *)g_getenv("HTTP_PROXY");
2115 if (filename != NULL && 0 < strlen(filename)) {
2116 new = g_strrstr(filename, "http://") ? g_strdup(filename) : g_strdup_printf("http://%s", filename);
2117 proxy_uri = soup_uri_new(new);
2119 g_object_set(session, "proxy-uri", proxy_uri, NULL);
2121 soup_uri_free(proxy_uri);
2122 g_free(new);
2127 void
2128 toggle_scrollbars(gboolean onoff) {
2129 if (onoff == TRUE) {
2130 adjust_h = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(viewport));
2131 adjust_v = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(viewport));
2132 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(viewport), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2133 } else {
2134 adjust_v = gtk_range_get_adjustment(GTK_RANGE(scroll_v));
2135 adjust_h = gtk_range_get_adjustment(GTK_RANGE(scroll_h));
2136 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(viewport), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
2138 gtk_widget_set_scroll_adjustments (GTK_WIDGET(webview), adjust_h, adjust_v);
2140 return;
2143 void
2144 update_url(const char *uri) {
2145 gboolean ssl = g_str_has_prefix(uri, "https://");
2146 GdkColor color;
2147 WebKitWebFrame *frame;
2148 WebKitWebDataSource *src;
2149 WebKitNetworkRequest *request;
2150 SoupMessage *msg;
2151 gboolean ssl_ok;
2152 char *sslactivecolor;
2153 gchar *markup;
2154 #ifdef ENABLE_HISTORY_INDICATOR
2155 char before[] = " [";
2156 char after[] = "]";
2157 gboolean back = webkit_web_view_can_go_back(webview);
2158 gboolean fwd = webkit_web_view_can_go_forward(webview);
2160 if (!back && !fwd)
2161 before[0] = after[0] = '\0';
2162 #endif
2163 markup = g_markup_printf_escaped(
2164 #ifdef ENABLE_HISTORY_INDICATOR
2165 "<span font=\"%s\">%s%s%s%s%s</span>", statusfont, uri,
2166 before, back ? "+" : "", fwd ? "-" : "", after
2167 #else
2168 "<span font=\"%s\">%s</span>", statusfont, uri
2169 #endif
2171 gtk_label_set_markup(GTK_LABEL(status_url), markup);
2172 g_free(markup);
2173 if (ssl) {
2174 frame = webkit_web_view_get_main_frame(webview);
2175 src = webkit_web_frame_get_data_source(frame);
2176 request = webkit_web_data_source_get_request(src);
2177 msg = webkit_network_request_get_message(request);
2178 ssl_ok = soup_message_get_flags(msg) & SOUP_MESSAGE_CERTIFICATE_TRUSTED;
2179 if (ssl_ok)
2180 sslactivecolor = sslbgcolor;
2181 else
2182 sslactivecolor = sslinvalidbgcolor;
2184 gdk_color_parse(ssl ? sslactivecolor : statusbgcolor, &color);
2185 gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, &color);
2186 gdk_color_parse(ssl ? sslcolor : statuscolor, &color);
2187 gtk_widget_modify_fg(GTK_WIDGET(status_url), GTK_STATE_NORMAL, &color);
2188 gtk_widget_modify_fg(GTK_WIDGET(status_state), GTK_STATE_NORMAL, &color);
2191 void
2192 update_state() {
2193 char *markup;
2194 int download_count = g_list_length(activeDownloads);
2195 GString *status = g_string_new("");
2197 /* construct the status line */
2199 /* count, modkey and input buffer */
2200 g_string_append_printf(status, "%.0d", count);
2201 if (current_modkey) g_string_append_c(status, current_modkey);
2203 /* the number of active downloads */
2204 if (activeDownloads) {
2205 g_string_append_printf(status, " %d active %s", download_count,
2206 (download_count == 1) ? "download" : "downloads");
2209 #ifdef ENABLE_WGET_PROGRESS_BAR
2210 /* the progressbar */
2212 int progress = -1;
2213 char progressbar[progressbartick + 1];
2215 if (activeDownloads) {
2216 progress = 0;
2217 GList *ptr;
2219 for (ptr = activeDownloads; ptr; ptr = g_list_next(ptr)) {
2220 progress += 100 * webkit_download_get_progress(ptr->data);
2223 progress /= download_count;
2225 } else if (webkit_web_view_get_load_status(webview) != WEBKIT_LOAD_FINISHED
2226 && webkit_web_view_get_load_status(webview) != WEBKIT_LOAD_FAILED) {
2228 progress = webkit_web_view_get_progress(webview) * 100;
2231 if (progress >= 0) {
2232 ascii_bar(progressbartick, progress * progressbartick / 100, progressbar);
2233 g_string_append_printf(status, " %c%s%c",
2234 progressborderleft, progressbar, progressborderright);
2237 #endif
2239 /* and the current scroll position */
2241 int max = gtk_adjustment_get_upper(adjust_v) - gtk_adjustment_get_page_size(adjust_v);
2242 int val = (int)(gtk_adjustment_get_value(adjust_v) / max * 100);
2244 if (max == 0)
2245 g_string_append(status, " All");
2246 else if (val == 0)
2247 g_string_append(status, " Top");
2248 else if (val == 100)
2249 g_string_append(status, " Bot");
2250 else
2251 g_string_append_printf(status, " %d%%", val);
2255 markup = g_markup_printf_escaped("<span font=\"%s\">%s</span>", statusfont, status->str);
2256 gtk_label_set_markup(GTK_LABEL(status_state), markup);
2257 g_free(markup);
2259 g_string_free(status, TRUE);
2262 void
2263 setup_modkeys() {
2264 unsigned int i;
2265 modkeys = calloc(LENGTH(keys) + 1, sizeof(char));
2266 char *ptr = modkeys;
2268 for (i = 0; i < LENGTH(keys); i++)
2269 if (keys[i].modkey && !strchr(modkeys, keys[i].modkey))
2270 *(ptr++) = keys[i].modkey;
2271 modkeys = realloc(modkeys, &ptr[0] - &modkeys[0] + 1);
2274 void
2275 setup_gui() {
2276 scroll_h = GTK_SCROLLBAR(gtk_hscrollbar_new(NULL));
2277 scroll_v = GTK_SCROLLBAR(gtk_vscrollbar_new(NULL));
2278 adjust_h = gtk_range_get_adjustment(GTK_RANGE(scroll_h));
2279 adjust_v = gtk_range_get_adjustment(GTK_RANGE(scroll_v));
2280 if (embed) {
2281 window = GTK_WINDOW(gtk_plug_new(embed));
2282 } else {
2283 window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
2284 gtk_window_set_wmclass(GTK_WINDOW(window), "vimprobable2", "Vimprobable2");
2286 gtk_window_set_default_size(GTK_WINDOW(window), 640, 480);
2287 box = GTK_BOX(gtk_vbox_new(FALSE, 0));
2288 inputbox = gtk_entry_new();
2289 webview = (WebKitWebView*)webkit_web_view_new();
2290 statusbar = GTK_BOX(gtk_hbox_new(FALSE, 0));
2291 eventbox = gtk_event_box_new();
2292 status_url = gtk_label_new(NULL);
2293 status_state = gtk_label_new(NULL);
2294 GdkColor bg;
2295 PangoFontDescription *font;
2296 GdkGeometry hints = { 1, 1 };
2297 inspector = webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(webview));
2299 clipboards[0] = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
2300 clipboards[1] = gtk_clipboard_get(GDK_NONE);
2301 setup_settings();
2302 gdk_color_parse(statusbgcolor, &bg);
2303 gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, &bg);
2304 gtk_widget_set_name(GTK_WIDGET(window), "Vimprobable2");
2305 gtk_window_set_geometry_hints(window, NULL, &hints, GDK_HINT_MIN_SIZE);
2307 keymap = gdk_keymap_get_default();
2309 #ifdef DISABLE_SCROLLBAR
2310 viewport = gtk_scrolled_window_new(NULL, NULL);
2311 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(viewport), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
2312 #else
2313 /* Ensure we still see scrollbars. */
2314 GtkWidget *viewport = gtk_scrolled_window_new(adjust_h, adjust_v);
2315 #endif
2317 setup_signals();
2318 gtk_container_add(GTK_CONTAINER(viewport), GTK_WIDGET(webview));
2320 /* Ensure we set the scroll adjustments now, so that if we're not drawing
2321 * titlebars, we can still scroll.
2323 gtk_widget_set_scroll_adjustments(GTK_WIDGET(webview), adjust_h, adjust_v);
2325 font = pango_font_description_from_string(urlboxfont[0]);
2326 gtk_widget_modify_font(GTK_WIDGET(inputbox), font);
2327 pango_font_description_free(font);
2328 gtk_entry_set_inner_border(GTK_ENTRY(inputbox), NULL);
2329 gtk_misc_set_alignment(GTK_MISC(status_url), 0.0, 0.0);
2330 gtk_misc_set_alignment(GTK_MISC(status_state), 1.0, 0.0);
2331 gtk_box_pack_start(statusbar, status_url, TRUE, TRUE, 2);
2332 gtk_box_pack_start(statusbar, status_state, FALSE, FALSE, 2);
2333 gtk_container_add(GTK_CONTAINER(eventbox), GTK_WIDGET(statusbar));
2334 gtk_box_pack_start(box, viewport, TRUE, TRUE, 0);
2335 gtk_box_pack_start(box, eventbox, FALSE, FALSE, 0);
2336 gtk_entry_set_has_frame(GTK_ENTRY(inputbox), FALSE);
2337 gtk_box_pack_end(box, inputbox, FALSE, FALSE, 0);
2338 gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(box));
2339 gtk_widget_grab_focus(GTK_WIDGET(webview));
2340 gtk_widget_show_all(GTK_WIDGET(window));
2341 set_widget_font_and_color(inputbox, urlboxfont[0], urlboxbgcolor[0], urlboxcolor[0]);
2342 g_object_set(gtk_widget_get_settings(inputbox), "gtk-entry-select-on-focus", FALSE, NULL);
2345 void
2346 setup_settings() {
2347 WebKitWebSettings *settings = (WebKitWebSettings*)webkit_web_settings_new();
2348 char *filename, *file_url;
2350 session = webkit_get_default_session();
2351 g_object_set(G_OBJECT(session), "ssl-ca-file", ca_bundle, NULL);
2352 g_object_set(G_OBJECT(session), "ssl-strict", strict_ssl, NULL);
2353 g_object_set(G_OBJECT(settings), "default-font-size", DEFAULT_FONT_SIZE, NULL);
2354 g_object_set(G_OBJECT(settings), "enable-scripts", enablePlugins, NULL);
2355 g_object_set(G_OBJECT(settings), "enable-plugins", enablePlugins, NULL);
2356 g_object_set(G_OBJECT(settings), "enable-java-applet", enableJava, NULL);
2357 g_object_set(G_OBJECT(settings), "enable-page-cache", enablePagecache, NULL);
2358 filename = g_strdup_printf(USER_STYLESHEET);
2359 file_url = g_strdup_printf("file://%s", filename);
2360 g_object_set(G_OBJECT(settings), "user-stylesheet-uri", file_url, NULL);
2361 g_free(file_url);
2362 g_free(filename);
2363 g_object_set(G_OBJECT(settings), "user-agent", useragent, NULL);
2364 g_object_get(G_OBJECT(settings), "zoom-step", &zoomstep, NULL);
2365 webkit_web_view_set_settings(webview, settings);
2367 /* proxy */
2368 toggle_proxy(use_proxy);
2371 void
2372 setup_signals() {
2373 WebKitWebFrame *frame = webkit_web_view_get_main_frame(webview);
2374 #ifdef ENABLE_COOKIE_SUPPORT
2375 /* Headers. */
2376 g_signal_connect_after(G_OBJECT(session), "request-started", G_CALLBACK(new_generic_request), NULL);
2377 #endif
2378 /* Accept-language header */
2379 g_object_set(G_OBJECT(session), "accept-language", acceptlanguage, NULL);
2380 /* window */
2381 g_object_connect(G_OBJECT(window),
2382 "signal::destroy", G_CALLBACK(window_destroyed_cb), NULL,
2383 NULL);
2384 /* frame */
2385 g_signal_connect(G_OBJECT(frame),
2386 "scrollbars-policy-changed", G_CALLBACK(blank_cb), NULL);
2387 /* webview */
2388 g_object_connect(G_OBJECT(webview),
2389 "signal::title-changed", G_CALLBACK(webview_title_changed_cb), NULL,
2390 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb), NULL,
2391 "signal::load-committed", G_CALLBACK(webview_load_committed_cb), NULL,
2392 "signal::load-finished", G_CALLBACK(webview_load_finished_cb), NULL,
2393 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_navigation_cb), NULL,
2394 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_new_window_cb), NULL,
2395 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb), NULL,
2396 "signal::download-requested", G_CALLBACK(webview_download_cb), NULL,
2397 "signal::key-press-event", G_CALLBACK(webview_keypress_cb), NULL,
2398 "signal::hovering-over-link", G_CALLBACK(webview_hoverlink_cb), NULL,
2399 "signal::console-message", G_CALLBACK(webview_console_cb), NULL,
2400 "signal::create-web-view", G_CALLBACK(webview_open_in_new_window_cb), NULL,
2401 "signal::event", G_CALLBACK(notify_event_cb), NULL,
2402 NULL);
2403 /* webview adjustment */
2404 g_object_connect(G_OBJECT(adjust_v),
2405 "signal::value-changed", G_CALLBACK(webview_scroll_cb), NULL,
2406 NULL);
2407 /* inputbox */
2408 g_object_connect(G_OBJECT(inputbox),
2409 "signal::activate", G_CALLBACK(inputbox_activate_cb), NULL,
2410 "signal::key-press-event", G_CALLBACK(inputbox_keypress_cb), NULL,
2411 "signal::key-release-event", G_CALLBACK(inputbox_keyrelease_cb), NULL,
2412 #ifdef ENABLE_INCREMENTAL_SEARCH
2413 "signal::changed", G_CALLBACK(inputbox_changed_cb), NULL,
2414 #endif
2415 NULL);
2416 /* inspector */
2417 g_signal_connect(G_OBJECT(inspector),
2418 "inspect-web-view", G_CALLBACK(inspector_inspect_web_view_cb), NULL);
2421 #ifdef ENABLE_COOKIE_SUPPORT
2422 void
2423 setup_cookies()
2425 if (file_cookie_jar)
2426 g_object_unref(file_cookie_jar);
2428 if (session_cookie_jar)
2429 g_object_unref(session_cookie_jar);
2431 session_cookie_jar = soup_cookie_jar_new();
2433 cookie_store = g_strdup_printf(COOKIES_STORAGE_FILENAME);
2435 load_all_cookies();
2437 g_signal_connect(G_OBJECT(file_cookie_jar), "changed",
2438 G_CALLBACK(update_cookie_jar), NULL);
2440 return;
2443 /* TA: XXX - we should be using this callback for any header-requests we
2444 * receive (hence the name "new_generic_request" -- but for now, its use
2445 * is limited to handling cookies.
2447 void
2448 new_generic_request(SoupSession *session, SoupMessage *soup_msg, gpointer unused)
2450 SoupMessageHeaders *soup_msg_h;
2451 SoupURI *uri;
2452 char *cookie_str;
2454 soup_msg_h = soup_msg->request_headers;
2455 soup_message_headers_remove(soup_msg_h, "Cookie");
2456 uri = soup_message_get_uri(soup_msg);
2457 if ((cookie_str = get_cookies(uri))) {
2458 soup_message_headers_append(soup_msg_h, "Cookie", cookie_str);
2459 g_free(cookie_str);
2462 g_signal_connect_after(G_OBJECT(soup_msg), "got-headers", G_CALLBACK(handle_cookie_request), NULL);
2464 return;
2467 char *
2468 get_cookies(SoupURI *soup_uri) {
2469 char *cookie_str;
2471 cookie_str = soup_cookie_jar_get_cookies(file_cookie_jar, soup_uri, TRUE);
2473 return cookie_str;
2476 void
2477 handle_cookie_request(SoupMessage *soup_msg, gpointer unused)
2479 GSList *resp_cookie = NULL, *cookie_list;
2480 SoupCookie *cookie;
2482 cookie_list = soup_cookies_from_response(soup_msg);
2483 for(resp_cookie = cookie_list; resp_cookie; resp_cookie = g_slist_next(resp_cookie))
2485 SoupDate *soup_date;
2486 cookie = soup_cookie_copy((SoupCookie *)resp_cookie->data);
2488 if (cookie_timeout && cookie->expires == NULL) {
2489 soup_date = soup_date_new_from_time_t(time(NULL) + cookie_timeout * 10);
2490 soup_cookie_set_expires(cookie, soup_date);
2491 soup_date_free(soup_date);
2493 soup_cookie_jar_add_cookie(file_cookie_jar, cookie);
2496 soup_cookies_free(cookie_list);
2498 return;
2501 void
2502 update_cookie_jar(SoupCookieJar *jar, SoupCookie *old, SoupCookie *new)
2504 if (!new) {
2505 /* Nothing to do. */
2506 return;
2509 SoupCookie *copy;
2510 copy = soup_cookie_copy(new);
2512 soup_cookie_jar_add_cookie(session_cookie_jar, copy);
2514 return;
2517 void
2518 load_all_cookies(void)
2520 GSList *cookie_list;
2521 file_cookie_jar = soup_cookie_jar_text_new(cookie_store, COOKIES_STORAGE_READONLY);
2523 /* Put them back in the session store. */
2524 GSList *cookies_from_file = soup_cookie_jar_all_cookies(file_cookie_jar);
2525 cookie_list = cookies_from_file;
2527 for (; cookies_from_file;
2528 cookies_from_file = cookies_from_file->next)
2530 soup_cookie_jar_add_cookie(session_cookie_jar, cookies_from_file->data);
2533 soup_cookies_free(cookies_from_file);
2534 g_slist_free(cookie_list);
2536 return;
2539 #endif
2541 void
2542 mop_up(void) {
2543 /* Free up any nasty globals before exiting. */
2544 #ifdef ENABLE_COOKIE_SUPPORT
2545 if (cookie_store)
2546 g_free(cookie_store);
2547 #endif
2548 return;
2552 main(int argc, char *argv[]) {
2553 static Arg a;
2554 static char url[256] = "";
2555 static gboolean ver = false;
2556 static gboolean configfile_exists = FALSE;
2557 static const char *cfile = NULL;
2558 static GOptionEntry opts[] = {
2559 { "version", 'v', 0, G_OPTION_ARG_NONE, &ver, "print version", NULL },
2560 { "embed", 'e', 0, G_OPTION_ARG_STRING, &winid, "embedded", NULL },
2561 { "configfile", 'c', 0, G_OPTION_ARG_STRING, &cfile, "config file", NULL },
2562 { NULL }
2564 static GError *err;
2565 args = argv;
2567 /* command line argument: version */
2568 if (!gtk_init_with_args(&argc, &argv, "[<uri>]", opts, NULL, &err)) {
2569 g_printerr("can't init gtk: %s\n", err->message);
2570 g_error_free(err);
2571 return EXIT_FAILURE;
2574 if (ver) {
2575 printf("%s\n", INTERNAL_VERSION);
2576 return EXIT_SUCCESS;
2579 if( getenv("XDG_CONFIG_HOME") )
2580 config_base = g_strdup_printf("%s", getenv("XDG_CONFIG_HOME"));
2581 else
2582 config_base = g_strdup_printf("%s/.config/", getenv("HOME"));
2584 if (cfile)
2585 configfile = g_strdup(cfile);
2586 else
2587 configfile = g_strdup_printf(RCFILE);
2589 if (!g_thread_supported())
2590 g_thread_init(NULL);
2592 if (winid)
2593 embed = atoi(winid);
2595 setup_modkeys();
2596 make_keyslist();
2597 setup_gui();
2598 #ifdef ENABLE_COOKIE_SUPPORT
2599 setup_cookies();
2600 #endif
2602 make_searchengines_list(searchengines, LENGTH(searchengines));
2603 make_uri_handlers_list(uri_handlers, LENGTH(uri_handlers));
2605 /* Check if the specified file exists. */
2606 /* And only warn the user, if they explicitly asked for a config on the
2607 * command line.
2609 if (!(access(configfile, F_OK) == 0) && cfile) {
2610 char *feedback_str;
2612 feedback_str = g_strdup_printf("Config file '%s' doesn't exist", cfile);
2613 give_feedback(feedback_str);
2614 g_free(feedback_str);
2615 } else if ((access(configfile, F_OK) == 0))
2616 configfile_exists = true;
2618 /* read config file */
2619 /* But only report errors if we failed, and the file existed. */
2620 if ((SUCCESS != read_rcfile(configfile)) && configfile_exists) {
2621 a.i = Error;
2622 a.s = g_strdup_printf("Error in config file '%s'", configfile);
2623 echo(&a);
2624 g_free(a.s);
2625 g_free(configfile);
2628 /* command line argument: URL */
2629 if (argc > 1) {
2630 strncpy(url, argv[argc - 1], 255);
2631 } else {
2632 strncpy(url, startpage, 255);
2635 a.i = TargetCurrent;
2636 a.s = url;
2637 open_arg(&a);
2638 gtk_main();
2640 mop_up();
2642 return EXIT_SUCCESS;