2 (c) 2009 by Leon Winter
3 (c) 2009-2013 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
8 (c) 2011-2013 by Daniel Carl
9 (c) 2012 by Matthew Carter
15 #include <sys/types.h>
20 #include "vimprobable.h"
21 #include "utilities.h"
22 #include "callbacks.h"
23 #include "javascript.h"
25 /* the CLEAN_MOD_*_MASK defines have all the bits set that will be stripped from the modifier bit field */
26 #define CLEAN_MOD_NUMLOCK_MASK (GDK_MOD2_MASK)
27 #define CLEAN_MOD_BUTTON_MASK (GDK_BUTTON1_MASK|GDK_BUTTON2_MASK|GDK_BUTTON3_MASK|GDK_BUTTON4_MASK|GDK_BUTTON5_MASK)
29 /* remove unused bits, numlock symbol and buttons from keymask */
30 #define CLEAN(mask) (mask & (GDK_MODIFIER_MASK) & ~(CLEAN_MOD_NUMLOCK_MASK) & ~(CLEAN_MOD_BUTTON_MASK))
32 #define IS_ESCAPE(event) (IS_ESCAPE_KEY(CLEAN(event->state), event->keyval))
33 #define IS_ESCAPE_KEY(s, k) ((s == 0 && k == GDK_Escape) || \
34 (s == GDK_CONTROL_MASK && k == GDK_bracketleft))
37 static void inputbox_activate_cb(GtkEntry
*entry
, gpointer user_data
);
38 static gboolean
inputbox_keypress_cb(GtkEntry
*entry
, GdkEventKey
*event
);
39 static gboolean
inputbox_keyrelease_cb(GtkEntry
*entry
, GdkEventKey
*event
);
40 static gboolean
inputbox_changed_cb(GtkEditable
*entry
, gpointer user_data
);
41 static WebKitWebView
* inspector_new_cb(WebKitWebInspector
* inspector
, WebKitWebView
* web_view
);
42 static gboolean
inspector_show_cb(WebKitWebInspector
*inspector
);
43 static gboolean
inspector_close_cb(WebKitWebInspector
*inspector
);
44 static void inspector_finished_cb(WebKitWebInspector
*inspector
);
45 static gboolean
notify_event_cb(GtkWidget
*widget
, GdkEvent
*event
, gpointer user_data
);
46 static gboolean
webview_console_cb(WebKitWebView
*webview
, char *message
, int line
, char *source
, gpointer user_data
);
47 static gboolean
webview_download_cb(WebKitWebView
*webview
, WebKitDownload
*download
, gpointer user_data
);
48 static void webview_hoverlink_cb(WebKitWebView
*webview
, char *title
, char *link
, gpointer data
);
49 static gboolean
webview_keypress_cb(WebKitWebView
*webview
, GdkEventKey
*event
);
50 static void webview_load_committed_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
);
51 static void webview_load_finished_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
);
52 static gboolean
webview_mimetype_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
,
53 char *mime_type
, WebKitWebPolicyDecision
*decision
, gpointer user_data
);
54 static void webview_open_js_window_cb(WebKitWebView
* temp_view
, GParamSpec param_spec
);
55 static gboolean
webview_new_window_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
,
56 WebKitWebNavigationAction
*action
, WebKitWebPolicyDecision
*decision
, gpointer user_data
);
57 static WebKitWebView
* webview_open_in_new_window_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
);
58 static void webview_progress_changed_cb(WebKitWebView
*webview
, int progress
, gpointer user_data
);
59 static void webview_title_changed_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, char *title
, gpointer user_data
);
60 static void window_destroyed_cb(GtkWidget
*window
, gpointer func_data
);
61 static gboolean
blank_cb(void);
64 static gboolean
bookmark(const Arg
*arg
);
65 static gboolean
browser_settings(const Arg
*arg
);
66 static gboolean
commandhistoryfetch(const Arg
*arg
);
67 static gboolean
complete(const Arg
*arg
);
68 static gboolean
descend(const Arg
*arg
);
69 gboolean
echo(const Arg
*arg
);
70 static gboolean
focus_input(const Arg
*arg
);
71 static gboolean
open_editor(const Arg
*arg
);
72 void _resume_from_editor(GPid child_pid
, int status
, gpointer data
);
73 static gboolean
input(const Arg
*arg
);
74 static gboolean
open_inspector(const Arg
* arg
);
75 static gboolean
navigate(const Arg
*arg
);
76 static gboolean
number(const Arg
*arg
);
77 static gboolean
open_arg(const Arg
*arg
);
78 static gboolean
open_remembered(const Arg
*arg
);
79 static gboolean
paste(const Arg
*arg
);
80 static gboolean
quickmark(const Arg
*arg
);
81 static gboolean
quit(const Arg
*arg
);
82 static gboolean
revive(const Arg
*arg
);
83 static gboolean
print_frame(const Arg
*arg
);
84 static gboolean
search(const Arg
*arg
);
85 static gboolean
set(const Arg
*arg
);
86 static gboolean
script(const Arg
*arg
);
87 static gboolean
scroll(const Arg
*arg
);
88 static gboolean
search_tag(const Arg
*arg
);
89 static gboolean
yank(const Arg
*arg
);
90 static gboolean
view_source(const Arg
* arg
);
91 static gboolean
zoom(const Arg
*arg
);
92 static gboolean
fake_key_event(const Arg
*arg
);
94 static void clear_focus(void);
95 static void update_url(const char *uri
);
96 static void setup_client(void);
97 static void setup_modkeys(void);
98 static void setup_gui(void);
99 static void setup_settings(void);
100 static void setup_signals(void);
101 static void ascii_bar(int total
, int state
, char *string
);
102 static gchar
*jsapi_ref_to_string(JSContextRef context
, JSValueRef ref
);
103 static void jsapi_evaluate_script(const gchar
*script
, gchar
**value
, gchar
**message
);
104 static void download_progress(WebKitDownload
*d
, GParamSpec
*pspec
);
105 static void set_widget_font_and_color(GtkWidget
*widget
, const char *font_str
,
106 const char *bg_color_str
, const char *fg_color_str
);
107 static void scripts_run_user_file(void);
109 static gboolean
history(void);
110 static gboolean
process_set_line(char *line
);
111 void save_command_history(char *line
);
112 void toggle_proxy(gboolean onoff
);
113 void toggle_scrollbars(gboolean onoff
);
114 void set_default_winsize(const char * const size
);
116 gboolean
process_keypress(GdkEventKey
*event
);
117 void fill_suggline(char * suggline
, const char * command
, const char *fill_with
);
118 GtkWidget
* fill_eventbox(const char * completion_line
);
119 static void mop_up(void);
129 /* Cookie support. */
130 #ifdef ENABLE_COOKIE_SUPPORT
131 static void setup_cookies(void);
132 static char *get_cookies(SoupURI
*soup_uri
);
133 static void load_all_cookies(void);
134 static void new_generic_request(SoupSession
*soup_ses
, SoupMessage
*soup_msg
, gpointer unused
);
135 static void update_cookie_jar(SoupCookieJar
*jar
, SoupCookie
*old
, SoupCookie
*new);
136 static void handle_cookie_request(SoupMessage
*soup_msg
, gpointer unused
);
143 window_destroyed_cb(GtkWidget
*window
, gpointer func_data
) {
148 webview_title_changed_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, char *title
, gpointer user_data
) {
149 gtk_window_set_title(client
.gui
.window
, title
);
153 webview_progress_changed_cb(WebKitWebView
*webview
, int progress
, gpointer user_data
) {
154 #ifdef ENABLE_GTK_PROGRESS_BAR
155 gtk_entry_set_progress_fraction(GTK_ENTRY(client
.gui
.inputbox
), progress
== 100 ? 0 : (double)progress
/100);
160 #ifdef ENABLE_WGET_PROGRESS_BAR
162 ascii_bar(int total
, int state
, char *string
) {
165 for (i
= 0; i
< state
; i
++)
166 string
[i
] = progressbartickchar
;
167 string
[i
++] = progressbarcurrent
;
168 for (; i
< total
; i
++)
169 string
[i
] = progressbarspacer
;
175 webview_load_committed_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
) {
176 Arg a
= { .i
= Silent
, .s
= g_strdup(JS_SETUP_HINTS
) };
177 const char *uri
= webkit_web_view_get_uri(webview
);
182 scripts_run_user_file();
184 if (client
.state
.mode
== ModeInsert
|| client
.state
.mode
== ModeHints
) {
185 Arg a
= { .i
= ModeNormal
};
188 client
.state
.manual_focus
= FALSE
;
192 webview_load_finished_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
) {
193 WebKitWebSettings
*settings
= webkit_web_view_get_settings(webview
);
196 g_object_get(settings
, "enable-scripts", &scripts
, NULL
);
197 if (escape_input_on_load
&& scripts
&& !client
.state
.manual_focus
&& !gtk_widget_is_focus(client
.gui
.inputbox
)) {
200 if (HISTORY_MAX_ENTRIES
> 0)
206 webview_open_js_window_cb(WebKitWebView
* temp_view
, GParamSpec param_spec
) {
207 /* retrieve the URI of the temporary webview */
208 Arg a
= { .i
= TargetNew
, .s
= (char*)webkit_web_view_get_uri(temp_view
) };
210 webkit_web_view_stop_loading(temp_view
);
211 gtk_widget_destroy(GTK_WIDGET(temp_view
));
212 /* open the requested window */
216 static WebKitWebView
*
217 webview_open_in_new_window_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
) {
218 if (client
.state
.rememberedURI
!= NULL
&& strlen(client
.state
.rememberedURI
) > 0) {
219 if (strncmp(client
.state
.rememberedURI
, "javascript:", 11) != 0) {
220 Arg a
= { .i
= TargetNew
, .s
= client
.state
.rememberedURI
};
225 /* create a temporary webview to execute the script in */
226 WebKitWebView
*temp_view
= WEBKIT_WEB_VIEW(webkit_web_view_new());
227 /* wait until the new webview receives its new URI */
228 g_object_connect(temp_view
, "signal::notify::uri", G_CALLBACK(webview_open_js_window_cb
), NULL
, NULL
);
233 webview_new_window_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
,
234 WebKitWebNavigationAction
*action
, WebKitWebPolicyDecision
*decision
, gpointer user_data
) {
235 Arg a
= { .i
= TargetNew
, .s
= (char*)webkit_network_request_get_uri(request
) };
237 webkit_web_policy_decision_ignore(decision
);
242 webview_mimetype_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
,
243 char *mime_type
, WebKitWebPolicyDecision
*decision
, gpointer user_data
) {
244 if (webkit_web_view_can_show_mime_type(webview
, mime_type
) == FALSE
) {
245 webkit_web_policy_decision_download(decision
);
252 static WebKitWebView
*
253 inspector_new_cb(WebKitWebInspector
*inspector
, WebKitWebView
* web_view
) {
254 return WEBKIT_WEB_VIEW(webkit_web_view_new());
258 inspector_show_cb(WebKitWebInspector
*inspector
) {
259 WebKitWebView
*webview
;
260 State
*state
= &client
.state
;
262 if (state
->is_inspecting
) {
266 webview
= webkit_web_inspector_get_web_view(inspector
);
267 gtk_paned_pack2(GTK_PANED(client
.gui
.pane
), GTK_WIDGET(webview
), TRUE
, TRUE
);
268 gtk_widget_show(GTK_WIDGET(webview
));
270 state
->is_inspecting
= TRUE
;
276 inspector_close_cb(WebKitWebInspector
*inspector
) {
278 State
*state
= &client
.state
;
280 if (!state
->is_inspecting
) {
283 widget
= GTK_WIDGET(webkit_web_inspector_get_web_view(inspector
));
284 gtk_widget_hide(widget
);
285 gtk_widget_destroy(widget
);
287 state
->is_inspecting
= FALSE
;
293 inspector_finished_cb(WebKitWebInspector
*inspector
) {
298 webview_download_cb(WebKitWebView
*webview
, WebKitDownload
*download
, gpointer user_data
) {
299 const gchar
*filename
;
302 WebKitDownloadStatus status
;
304 filename
= webkit_download_get_suggested_filename(download
);
305 if (filename
== NULL
|| strlen(filename
) == 0) {
306 filename
= "vimprobable_download";
308 path
= g_build_filename(g_strdup_printf(DOWNLOADS_PATH
), filename
, NULL
);
309 uri
= g_strconcat("file://", path
, NULL
);
310 webkit_download_set_destination_uri(download
, uri
);
313 size
= (uint32_t)webkit_download_get_total_size(download
);
315 echo_message(Info
, "Download %s started (expected size: %u bytes)...", filename
, size
);
317 echo_message(Info
, "Download %s started (unknown size)...", filename
);
318 client
.state
.activeDownloads
= g_list_prepend(client
.state
.activeDownloads
, download
);
319 g_signal_connect(download
, "notify::progress", G_CALLBACK(download_progress
), NULL
);
320 g_signal_connect(download
, "notify::status", G_CALLBACK(download_progress
), NULL
);
321 status
= webkit_download_get_status(download
);
322 if (status
== WEBKIT_DOWNLOAD_STATUS_CREATED
)
323 webkit_download_start(download
);
334 download_progress(WebKitDownload
*d
, GParamSpec
*pspec
) {
335 WebKitDownloadStatus status
= webkit_download_get_status(d
);
337 if (status
!= WEBKIT_DOWNLOAD_STATUS_STARTED
&& status
!= WEBKIT_DOWNLOAD_STATUS_CREATED
) {
338 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
) {
339 echo_message(Error
, "Error while downloading %s", webkit_download_get_suggested_filename(d
));
341 echo_message(Info
, "Download %s finished", webkit_download_get_suggested_filename(d
));
343 client
.state
.activeDownloads
= g_list_remove(client
.state
.activeDownloads
, d
);
350 process_keypress(GdkEventKey
*event
) {
351 State
*state
= &client
.state
;
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(state
->keymap
, event
->hardware_keycode
,
359 event
->state
, event
->group
, &keyval
, NULL
, NULL
, &irrelevant
);
361 current
= client
.config
.keylistroot
;
363 while (current
!= NULL
) {
364 if (current
->Element
.mask
== (CLEAN(event
->state
) & ~irrelevant
)
365 && (current
->Element
.modkey
== state
->current_modkey
366 || (!current
->Element
.modkey
&& !state
->current_modkey
)
367 || current
->Element
.modkey
== GDK_VoidSymbol
) /* wildcard */
368 && current
->Element
.key
== keyval
369 && current
->Element
.func
)
370 if (current
->Element
.func(¤t
->Element
.arg
)) {
371 state
->current_modkey
= state
->count
= 0;
375 current
= current
->next
;
381 webview_keypress_cb(WebKitWebView
*webview
, GdkEventKey
*event
) {
382 State
*state
= &client
.state
;
383 Arg a
= { .i
= ModeNormal
, .s
= NULL
};
385 GdkModifierType irrelevant
;
387 /* Get a mask of modifiers that shouldn't be considered for this event.
388 * E.g.: It shouldn't matter whether ';' is shifted or not. */
389 gdk_keymap_translate_keyboard_state(state
->keymap
, event
->hardware_keycode
,
390 event
->state
, event
->group
, &keyval
, NULL
, NULL
, &irrelevant
);
392 switch (state
->mode
) {
394 if ((CLEAN(event
->state
) & ~irrelevant
) == 0) {
395 if (IS_ESCAPE(event
)) {
396 echo_message(Info
, "");
398 } else if (state
->current_modkey
== 0 && ((event
->keyval
>= GDK_1
&& event
->keyval
<= GDK_9
)
399 || (event
->keyval
== GDK_0
&& state
->count
))) {
400 state
->count
= (state
->count
? state
->count
* 10 : 0) + (event
->keyval
- GDK_0
);
403 } else if (strchr(client
.config
.modkeys
, event
->keyval
) && state
->current_modkey
!= event
->keyval
) {
404 state
->current_modkey
= event
->keyval
;
410 if (process_keypress(event
) == TRUE
) return TRUE
;
414 if (IS_ESCAPE(event
)) {
416 a
.s
= g_strdup("hints.clearFocus();");
421 } else if (CLEAN(event
->state
) & GDK_CONTROL_MASK
) {
422 /* keybindings of non-printable characters */
423 if (process_keypress(event
) == TRUE
) return TRUE
;
425 case ModePassThrough
:
426 if (IS_ESCAPE(event
)) {
427 echo_message(Info
, "");
433 echo_message(Info
, "");
441 set_widget_font_and_color(GtkWidget
*widget
, const char *font_str
, const char *bg_color_str
,
442 const char *fg_color_str
) {
445 PangoFontDescription
*font
;
447 font
= pango_font_description_from_string(font_str
);
448 gtk_widget_modify_font(widget
, font
);
449 pango_font_description_free(font
);
452 gdk_color_parse(fg_color_str
, &fg_color
);
454 gdk_color_parse(bg_color_str
, &bg_color
);
456 gtk_widget_modify_text(widget
, GTK_STATE_NORMAL
, fg_color_str
? &fg_color
: NULL
);
457 gtk_widget_modify_base(widget
, GTK_STATE_NORMAL
, bg_color_str
? &bg_color
: NULL
);
463 webview_hoverlink_cb(WebKitWebView
*webview
, char *title
, char *link
, gpointer data
) {
464 const char *uri
= webkit_web_view_get_uri(webview
);
467 memset(client
.state
.rememberedURI
, 0, BUF_SIZE
);
469 markup
= g_markup_printf_escaped("<span font=\"%s\">Link: %s</span>", statusfont
, link
);
470 gtk_label_set_markup(GTK_LABEL(client
.gui
.status_url
), markup
);
471 strncpy(client
.state
.rememberedURI
, link
, BUF_SIZE
);
478 webview_console_cb(WebKitWebView
*webview
, char *message
, int line
, char *source
, gpointer user_data
) {
481 /* Don't change internal mode if the browser doesn't have focus to prevent inconsistent states */
482 if (gtk_window_has_toplevel_focus(client
.gui
.window
)) {
483 if (!strcmp(message
, "hintmode_off") || !strcmp(message
, "insertmode_off")) {
486 } else if (!strcmp(message
, "insertmode_on")) {
495 inputbox_activate_cb(GtkEntry
*entry
, gpointer user_data
) {
496 Gui
*gui
= &client
.gui
;
497 State
*state
= &client
.state
;
499 guint16 length
= gtk_entry_get_text_length(entry
);
501 gboolean forward
= FALSE
;
503 a
.i
= HideCompletion
;
507 text
= (char*)gtk_entry_get_text(entry
);
509 /* move focus from inputbox to print potential messages that could not be
510 * printed as long as the inputbox is focused */
511 gtk_widget_grab_focus(GTK_WIDGET(gui
->webview
));
513 if (length
> 1 && text
[0] == ':') {
514 process_line((text
+ 1));
515 } else if (length
> 1 && ((forward
= text
[0] == '/') || text
[0] == '?')) {
516 webkit_web_view_unmark_text_matches(gui
->webview
);
517 #ifdef ENABLE_MATCH_HIGHLITING
518 webkit_web_view_mark_text_matches(gui
->webview
, &text
[1], FALSE
, 0);
519 webkit_web_view_set_highlight_text_matches(gui
->webview
, TRUE
);
522 #ifndef ENABLE_INCREMENTAL_SEARCH
524 a
.i
= searchoptions
| (forward
? DirectionForward
: DirectionBackwards
);
527 state
->search_direction
= forward
;
528 if (state
->search_handle
) {
529 g_free(state
->search_handle
);
531 state
->search_handle
= g_strdup(&text
[1]);
533 } else if (text
[0] == '.' || text
[0] == ',' || text
[0] == ';') {
535 a
.s
= g_strdup_printf("hints.fire();");
541 gtk_widget_grab_focus(GTK_WIDGET(gui
->webview
));
545 inputbox_keypress_cb(GtkEntry
*entry
, GdkEventKey
*event
) {
548 State
*state
= &client
.state
;
550 if (state
->mode
== ModeHints
) {
551 if (event
->keyval
== GDK_Tab
) {
553 a
.s
= g_strdup_printf("hints.focusNextHint();");
559 if (event
->keyval
== GDK_ISO_Left_Tab
) {
561 a
.s
= g_strdup_printf("hints.focusPreviousHint();");
567 if (event
->keyval
== GDK_Return
) {
569 a
.s
= g_strdup_printf("hints.fire();");
576 switch (event
->keyval
) {
577 case GDK_bracketleft
:
579 if (!IS_ESCAPE(event
)) break;
580 a
.i
= HideCompletion
;
583 state
->commandpointer
= 0;
592 return commandhistoryfetch(&a
);
596 return commandhistoryfetch(&a
);
598 case GDK_ISO_Left_Tab
:
604 if (state
->mode
== ModeHints
) {
605 if ((CLEAN(event
->state
) & GDK_SHIFT_MASK
) &&
606 (CLEAN(event
->state
) & GDK_CONTROL_MASK
) &&
607 (event
->keyval
== GDK_BackSpace
)) {
610 a
.s
= g_strdup_printf("hints.updateHints(%d);", state
->count
);
617 numval
= g_unichar_digit_value((gunichar
) gdk_keyval_to_unicode(event
->keyval
));
618 if ((numval
>= 1 && numval
<= 9) || (numval
== 0 && state
->count
)) {
619 /* allow a zero as non-first number */
620 state
->count
= (state
->count
? state
->count
* 10 : 0) + numval
;
622 a
.s
= g_strdup_printf("hints.updateHints(%d);", state
->count
);
634 notify_event_cb(GtkWidget
*widget
, GdkEvent
*event
, gpointer user_data
) {
636 WebKitHitTestResult
*result
;
637 WebKitHitTestResultContext context
;
638 State
*state
= &client
.state
;
639 if (state
->mode
== ModeNormal
&& event
->type
== GDK_BUTTON_RELEASE
) {
640 /* handle mouse click events */
641 for (i
= 0; i
< LENGTH(mouse
); i
++) {
642 if (mouse
[i
].mask
== CLEAN(event
->button
.state
)
643 && (mouse
[i
].modkey
== state
->current_modkey
644 || (!mouse
[i
].modkey
&& !state
->current_modkey
)
645 || mouse
[i
].modkey
== GDK_VoidSymbol
) /* wildcard */
646 && mouse
[i
].button
== event
->button
.button
648 if (mouse
[i
].func(&mouse
[i
].arg
)) {
649 state
->current_modkey
= state
->count
= 0;
655 result
= webkit_web_view_get_hit_test_result(WEBKIT_WEB_VIEW(widget
), (GdkEventButton
*)event
);
656 g_object_get(result
, "context", &context
, NULL
);
657 if (context
& WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE
) {
658 Arg a
= { .i
= ModeInsert
};
660 state
->manual_focus
= TRUE
;
662 } else if (state
->mode
== ModeInsert
&& event
->type
== GDK_BUTTON_RELEASE
) {
663 result
= webkit_web_view_get_hit_test_result(WEBKIT_WEB_VIEW(widget
), (GdkEventButton
*)event
);
664 g_object_get(result
, "context", &context
, NULL
);
665 if (!(context
& WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE
)) {
666 Arg a
= { .i
= ModeNormal
};
670 gchar
*value
= NULL
, *message
= NULL
;
671 jsapi_evaluate_script("window.getSelection().focusNode", &value
, &message
);
672 if (value
&& !strcmp(value
, "[object HTMLFormElement]")) {
673 Arg a
= { .i
= ModeInsert
, .s
= NULL
};
675 state
->manual_focus
= TRUE
;
683 static gboolean
inputbox_keyrelease_cb(GtkEntry
*entry
, GdkEventKey
*event
) {
685 guint16 length
= gtk_entry_get_text_length(entry
);
688 a
.i
= HideCompletion
;
696 static gboolean
inputbox_changed_cb(GtkEditable
*entry
, gpointer user_data
) {
698 char *text
= (char*)gtk_entry_get_text(GTK_ENTRY(entry
));
699 guint16 length
= gtk_entry_get_text_length(GTK_ENTRY(entry
));
700 gboolean forward
= FALSE
;
702 /* Update incremental search if the user changes the search text.
704 * Note: gtk_widget_is_focus() is a poor way to check if the change comes
705 * from the user. But if the entry is focused and the text is set
706 * through gtk_entry_set_text() in some asyncrounous operation,
707 * I would consider that a bug.
710 if (gtk_widget_is_focus(GTK_WIDGET(entry
)) && length
> 1 && ((forward
= text
[0] == '/') || text
[0] == '?')) {
711 webkit_web_view_unmark_text_matches(client
.gui
.webview
);
712 webkit_web_view_search_text(client
.gui
.webview
, &text
[1], searchoptions
& CaseSensitive
, forward
, searchoptions
& Wrapping
);
714 } else if (gtk_widget_is_focus(GTK_WIDGET(entry
)) && length
>= 1 &&
715 (text
[0] == '.' || text
[0] == ',' || text
[0] == ';')) {
719 a
.s
= g_strconcat("hints.createHints('", text
+ 1, "', 'f');", NULL
);
723 a
.s
= g_strconcat("hints.createHints('", text
+ 1, "', 'F');", NULL
);
730 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 's');", NULL
);
733 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'y');", NULL
);
736 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'f');", NULL
);
739 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'F');", NULL
);
741 case 'O': case 'T': case 'W':
742 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'O');", NULL
);
745 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'i');", NULL
);
748 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'I');", NULL
);
753 client
.state
.count
= 0;
760 } else if (length
== 0) {
761 client
.state
.mode
= ModeNormal
;
763 a
.s
= g_strdup("hints.clearHints();");
766 client
.state
.count
= 0;
775 void fill_suggline(char * suggline
, const char * command
, const char *fill_with
) {
776 memset(suggline
, 0, 512);
777 strncpy(suggline
, command
, 512);
778 strncat(suggline
, " ", 1);
779 strncat(suggline
, fill_with
, 512 - strlen(suggline
) - 1);
782 GtkWidget
* fill_eventbox(const char * completion_line
) {
784 GtkWidget
*row_eventbox
, *el
;
786 char *markup
, *markup_tmp
;
788 row
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
789 row_eventbox
= gtk_event_box_new();
790 gdk_color_parse(completionbgcolor
[0], &color
);
791 gtk_widget_modify_bg(row_eventbox
, GTK_STATE_NORMAL
, &color
);
792 el
= gtk_label_new(NULL
);
793 markup_tmp
= g_markup_escape_text(completion_line
, strlen(completion_line
));
794 markup
= g_strconcat("<span font=\"", completionfont
[0], "\" color=\"", completioncolor
[0], "\">",
795 markup_tmp
, "</span>", NULL
);
796 gtk_label_set_markup(GTK_LABEL(el
), markup
);
799 gtk_misc_set_alignment(GTK_MISC(el
), 0, 0);
800 gtk_box_pack_start(row
, el
, TRUE
, TRUE
, 2);
801 gtk_container_add(GTK_CONTAINER(row_eventbox
), GTK_WIDGET(row
));
806 complete(const Arg
*arg
) {
807 char *str
, *p
, *s
, *markup
, *entry
, *searchfor
, command
[32] = "", suggline
[512] = "", **suggurls
;
808 size_t listlen
, len
, cmdlen
;
810 Listelement
*elementlist
= NULL
, *elementpointer
;
811 gboolean highlight
= FALSE
;
813 GtkWidget
*row_eventbox
, *el
;
816 static GtkWidget
*table
, *top_border
;
818 static char **suggestions
;
819 static GtkWidget
**widgets
;
820 static int n
= 0, m
, current
= -1;
821 Gui
*gui
= &client
.gui
;
823 str
= (char*)gtk_entry_get_text(GTK_ENTRY(gui
->inputbox
));
826 /* Get the length of the list of commands for completion. We need this to
827 * malloc/realloc correctly.
829 listlen
= LENGTH(commands
);
831 if ((len
== 0 || str
[0] != ':') && arg
->i
!= HideCompletion
)
834 if (arg
->i
!= HideCompletion
&& widgets
&& current
!= -1 && !strcmp(&str
[1], suggestions
[current
])) {
835 gdk_color_parse(completionbgcolor
[0], &color
);
836 gtk_widget_modify_bg(widgets
[current
], GTK_STATE_NORMAL
, &color
);
837 current
= (n
+ current
+ (arg
->i
== DirectionPrev
? -1 : 1)) % n
;
838 if ((arg
->i
== DirectionNext
&& current
== 0)
839 || (arg
->i
== DirectionPrev
&& current
== n
- 1))
845 gtk_widget_destroy(GTK_WIDGET(table
));
846 gtk_widget_destroy(GTK_WIDGET(top_border
));
853 if (arg
->i
== HideCompletion
)
856 } else if (arg
->i
== HideCompletion
)
859 prefix
= g_strdup(str
);
860 widgets
= malloc(sizeof(GtkWidget
*) * listlen
);
861 suggestions
= malloc(sizeof(char*) * listlen
);
862 top_border
= gtk_event_box_new();
863 gtk_widget_set_size_request(GTK_WIDGET(top_border
), 0, 1);
864 gdk_color_parse(completioncolor
[2], &color
);
865 gtk_widget_modify_bg(top_border
, GTK_STATE_NORMAL
, &color
);
866 table
= gtk_event_box_new();
867 gdk_color_parse(completionbgcolor
[0], &color
);
868 _table
= GTK_BOX(gtk_vbox_new(FALSE
, 0));
870 if (strchr(str
, ' ') == NULL
) {
871 /* command completion */
872 listlen
= LENGTH(commands
);
873 for (i
= 0; i
< listlen
; i
++) {
874 if (commands
[i
].cmd
== NULL
)
876 cmdlen
= strlen(commands
[i
].cmd
);
877 if (!highlight
|| (n
< MAX_LIST_SIZE
&& len
- 1 <= cmdlen
&& !strncmp(&str
[1], commands
[i
].cmd
, len
- 1))) {
878 p
= s
= malloc(sizeof(char*) * (highlight
? sizeof(COMPLETION_TAG_OPEN
) + sizeof(COMPLETION_TAG_CLOSE
) - 1 : 1) + cmdlen
);
880 memcpy(p
, COMPLETION_TAG_OPEN
, sizeof(COMPLETION_TAG_OPEN
) - 1);
881 memcpy((p
+= sizeof(COMPLETION_TAG_OPEN
) - 1), &str
[1], len
- 1);
882 memcpy((p
+= len
- 1), COMPLETION_TAG_CLOSE
, sizeof(COMPLETION_TAG_CLOSE
) - 1);
883 p
+= sizeof(COMPLETION_TAG_CLOSE
) - 1;
885 memcpy(p
, &commands
[i
].cmd
[len
- 1], cmdlen
- len
+ 2);
886 row
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
887 row_eventbox
= gtk_event_box_new();
888 gtk_widget_modify_bg(row_eventbox
, GTK_STATE_NORMAL
, &color
);
889 el
= gtk_label_new(NULL
);
890 markup
= g_strconcat("<span font=\"", completionfont
[0], "\" color=\"", completioncolor
[0], "\">", s
, "</span>", NULL
);
892 gtk_label_set_markup(GTK_LABEL(el
), markup
);
894 gtk_misc_set_alignment(GTK_MISC(el
), 0, 0);
895 gtk_box_pack_start(row
, el
, TRUE
, TRUE
, 2);
896 gtk_container_add(GTK_CONTAINER(row_eventbox
), GTK_WIDGET(row
));
897 gtk_box_pack_start(_table
, GTK_WIDGET(row_eventbox
), FALSE
, FALSE
, 0);
898 suggestions
[n
] = commands
[i
].cmd
;
899 widgets
[n
++] = row_eventbox
;
903 entry
= (char *)malloc(512 * sizeof(char));
907 memset(entry
, 0, 512);
908 suggurls
= malloc(sizeof(char*) * listlen
);
909 if (suggurls
== NULL
) {
912 spacepos
= strcspn(str
, " ");
913 searchfor
= (str
+ spacepos
+ 1);
914 strncpy(command
, (str
+ 1), spacepos
- 1);
915 if (strlen(command
) == 3 && strncmp(command
, "set", 3) == 0) {
916 /* browser settings */
917 listlen
= LENGTH(browsersettings
);
918 for (i
= 0; i
< listlen
; i
++) {
919 if (n
< MAX_LIST_SIZE
&& strstr(browsersettings
[i
].name
, searchfor
) != NULL
) {
921 fill_suggline(suggline
, command
, browsersettings
[i
].name
);
922 /* FIXME(HP): This memory is never freed */
923 suggurls
[n
] = (char *)malloc(sizeof(char) * 512 + 1);
924 strncpy(suggurls
[n
], suggline
, 512);
925 suggestions
[n
] = suggurls
[n
];
926 row_eventbox
= fill_eventbox(suggline
);
927 gtk_box_pack_start(_table
, GTK_WIDGET(row_eventbox
), FALSE
, FALSE
, 0);
928 widgets
[n
++] = row_eventbox
;
932 } else if (strlen(command
) == 2 && strncmp(command
, "qt", 2) == 0) {
933 /* completion on tags */
934 spacepos
= strcspn(str
, " ");
935 searchfor
= (str
+ spacepos
+ 1);
936 elementlist
= complete_list(searchfor
, 1, elementlist
);
938 /* URL completion: bookmarks */
939 elementlist
= complete_list(searchfor
, 0, elementlist
);
940 m
= count_list(elementlist
);
941 if (m
< MAX_LIST_SIZE
) {
942 /* URL completion: history */
943 elementlist
= complete_list(searchfor
, 2, elementlist
);
946 elementpointer
= elementlist
;
947 while (elementpointer
!= NULL
) {
948 fill_suggline(suggline
, command
, elementpointer
->element
);
949 /* FIXME(HP): This memory is never freed */
950 suggurls
[n
] = (char *)malloc(sizeof(char) * 512 + 1);
951 strncpy(suggurls
[n
], suggline
, 512);
952 suggestions
[n
] = suggurls
[n
];
953 row_eventbox
= fill_eventbox(suggline
);
954 gtk_box_pack_start(_table
, GTK_WIDGET(row_eventbox
), FALSE
, FALSE
, 0);
955 widgets
[n
++] = row_eventbox
;
956 elementpointer
= elementpointer
->next
;
957 if (n
>= MAX_LIST_SIZE
)
960 free_list(elementlist
);
961 if (suggurls
!= NULL
) {
970 /* TA: FIXME - this needs rethinking entirely. */
972 GtkWidget
**widgets_temp
= realloc(widgets
, sizeof(*widgets
) * n
);
973 if (widgets_temp
== NULL
&& widgets
== NULL
) {
974 fprintf(stderr
, "Couldn't realloc() widgets\n");
977 widgets
= widgets_temp
;
978 char **suggestions_temp
= realloc(suggestions
, sizeof(*suggestions
) * n
);
979 if (suggestions_temp
== NULL
&& suggestions
== NULL
) {
980 fprintf(stderr
, "Couldn't realloc() suggestions\n");
983 suggestions
= suggestions_temp
;
986 gdk_color_parse(completionbgcolor
[1], &color
);
987 gtk_widget_modify_bg(table
, GTK_STATE_NORMAL
, &color
);
988 el
= gtk_label_new(NULL
);
989 gtk_misc_set_alignment(GTK_MISC(el
), 0, 0);
990 markup
= g_strconcat("<span font=\"", completionfont
[1], "\" color=\"", completioncolor
[1], "\">No Completions</span>", NULL
);
991 gtk_label_set_markup(GTK_LABEL(el
), markup
);
993 gtk_box_pack_start(_table
, GTK_WIDGET(el
), FALSE
, FALSE
, 0);
995 gtk_box_pack_start(gui
->box
, GTK_WIDGET(top_border
), FALSE
, FALSE
, 0);
996 gtk_container_add(GTK_CONTAINER(table
), GTK_WIDGET(_table
));
997 gtk_box_pack_start(gui
->box
, GTK_WIDGET(table
), FALSE
, FALSE
, 0);
998 gtk_widget_show_all(GTK_WIDGET(gui
->window
));
1001 current
= arg
->i
== DirectionPrev
? n
- 1 : 0;
1003 if (current
!= -1) {
1004 gdk_color_parse(completionbgcolor
[2], &color
);
1005 gtk_widget_modify_bg(GTK_WIDGET(widgets
[current
]), GTK_STATE_NORMAL
, &color
);
1006 s
= g_strconcat(":", suggestions
[current
], NULL
);
1007 gtk_entry_set_text(GTK_ENTRY(gui
->inputbox
), s
);
1010 gtk_entry_set_text(GTK_ENTRY(gui
->inputbox
), prefix
);
1011 gtk_editable_set_position(GTK_EDITABLE(gui
->inputbox
), -1);
1016 descend(const Arg
*arg
) {
1017 char *source
= (char*)webkit_web_view_get_uri(client
.gui
.webview
), *p
= &source
[0], *new;
1019 client
.state
.count
= client
.state
.count
? client
.state
.count
: 1;
1023 if (arg
->i
== Rootdir
) {
1024 for (i
= 0; i
< 3; i
++) /* get to the third slash */
1025 if (!(p
= strchr(++p
, '/')))
1026 return TRUE
; /* if we cannot find it quit */
1028 len
= strlen(source
);
1029 if (!len
) /* if string is empty quit */
1031 p
= source
+ len
; /* start at the end */
1032 if (*(p
- 1) == '/') /* /\/$/ is not an additional level */
1033 ++client
.state
.count
;
1034 for (i
= 0; i
< client
.state
.count
; i
++)
1035 while(*(p
--) != '/' || *p
== '/') /* count /\/+/ as one slash */
1036 if (p
== source
) /* if we reach the first char pointer quit */
1038 ++p
; /* since we do p-- in the while, we are pointing at
1039 the char before the slash, so +1 */
1041 len
= p
- source
+ 1; /* new length = end - start + 1 */
1042 new = malloc(len
+ 1);
1043 memcpy(new, source
, len
);
1045 webkit_web_view_load_uri(client
.gui
.webview
, new);
1051 echo(const Arg
*arg
) {
1052 int index
= !arg
->s
? 0 : arg
->i
& (~NoAutoHide
);
1054 if (index
< Info
|| index
> Error
)
1057 if (!gtk_widget_is_focus(GTK_WIDGET(client
.gui
.inputbox
))) {
1058 set_widget_font_and_color(client
.gui
.inputbox
, urlboxfont
[index
], urlboxbgcolor
[index
], urlboxcolor
[index
]);
1059 gtk_entry_set_text(GTK_ENTRY(client
.gui
.inputbox
), !arg
->s
? "" : arg
->s
);
1066 open_inspector(const Arg
* arg
) {
1067 gboolean inspect_enabled
;
1068 WebKitWebSettings
*settings
;
1069 State
*state
= &client
.state
;
1071 settings
= webkit_web_view_get_settings(client
.gui
.webview
);
1072 g_object_get(G_OBJECT(settings
), "enable-developer-extras", &inspect_enabled
, NULL
);
1073 if (inspect_enabled
) {
1074 if (state
->is_inspecting
) {
1075 webkit_web_inspector_close(client
.gui
.inspector
);
1077 webkit_web_inspector_show(client
.gui
.inspector
);
1081 echo_message(Error
, "Webinspector is not enabled");
1087 input(const Arg
*arg
) {
1089 client
.state
.count
= 0;
1093 GtkWidget
*inputbox
= client
.gui
.inputbox
;
1095 /* if inputbox hidden, show it again */
1096 if (!gtk_widget_get_visible(inputbox
))
1097 gtk_widget_set_visible(inputbox
, TRUE
);
1101 /* Set the colour and font back to the default, so that we don't still
1102 * maintain a red colour from a warning from an end of search indicator,
1105 set_widget_font_and_color(inputbox
, urlboxfont
[index
], urlboxbgcolor
[index
], urlboxcolor
[index
]);
1107 /* to avoid things like :open URL :open URL2 or :open :open URL */
1108 gtk_entry_set_text(GTK_ENTRY(inputbox
), "");
1109 gtk_editable_insert_text(GTK_EDITABLE(inputbox
), arg
->s
, -1, &pos
);
1110 if (arg
->i
& InsertCurrentURL
&& (url
= webkit_web_view_get_uri(client
.gui
.webview
)))
1111 gtk_editable_insert_text(GTK_EDITABLE(inputbox
), url
, -1, &pos
);
1113 gtk_widget_grab_focus(inputbox
);
1114 gtk_editable_set_position(GTK_EDITABLE(inputbox
), -1);
1116 if (arg
->s
[0] == '.' || arg
->s
[0] == ',' || arg
->s
[0] == ';') {
1117 client
.state
.mode
= ModeHints
;
1119 switch (arg
->s
[0]) {
1121 a
.s
= g_strdup("hints.createHints('', 'f');");
1125 a
.s
= g_strdup("hints.createHints('', 'F');");
1131 switch (arg
->s
[1]) {
1133 a
.s
= g_strdup("hints.createHints('', 's');");
1136 a
.s
= g_strdup("hints.createHints('', 'y');");
1139 a
.s
= g_strdup("hints.createHints('', 'f');");
1142 a
.s
= g_strdup("hints.createHints('', 'F');");
1144 case 'O': case 'T': case 'W':
1145 a
.s
= g_strdup("hints.createHints('', 'O');");
1148 a
.s
= g_strdup("hints.createHints('', 'i');");
1151 a
.s
= g_strdup("hints.createHints('', 'I');");
1157 client
.state
.count
= 0;
1168 navigate(const Arg
*arg
) {
1169 if (arg
->i
& NavigationForwardBack
)
1170 webkit_web_view_go_back_or_forward(client
.gui
.webview
, (arg
->i
== NavigationBack
? -1 : 1) * (client
.state
.count
? client
.state
.count
: 1));
1171 else if (arg
->i
& NavigationReloadActions
)
1172 (arg
->i
== NavigationReload
? webkit_web_view_reload
: webkit_web_view_reload_bypass_cache
)(client
.gui
.webview
);
1174 webkit_web_view_stop_loading(client
.gui
.webview
);
1179 number(const Arg
*arg
) {
1180 const char *source
= webkit_web_view_get_uri(client
.gui
.webview
);
1181 char *uri
, *p
, *new;
1182 int number
, diff
= (client
.state
.count
? client
.state
.count
: 1) * (arg
->i
== Increment
? 1 : -1);
1186 uri
= g_strdup(source
); /* copy string */
1188 while(*p
!= '\0') /* goto the end of the string */
1191 while(*p
>= '0' && *p
<= '9') /* go back until non number char is reached */
1193 if (*(++p
) == '\0') { /* if no numbers were found abort */
1197 number
= atoi(p
) + diff
; /* apply diff on number */
1199 new = g_strdup_printf("%s%d", uri
, number
); /* create new uri */
1200 webkit_web_view_load_uri(client
.gui
.webview
, new);
1207 open_arg(const Arg
*arg
) {
1209 char *s
= arg
->s
, *p
= NULL
, *new;
1210 Arg a
= { .i
= NavigationReload
};
1212 char *search_uri
, *search_term
;
1213 struct stat statbuf
;
1215 if (client
.state
.embed
) {
1217 snprintf(winid
, LENGTH(winid
), "%u", (gint
)client
.state
.embed
);
1231 else if (arg
->i
== TargetCurrent
) {
1232 while(*s
== ' ') /* strip leading whitespace */
1234 p
= (s
+ strlen(s
) - 1);
1235 while(*p
== ' ') /* strip trailing whitespace */
1240 /* check for external handlers */
1241 if (open_handler(s
))
1243 /* check for search engines */
1245 if (p
) { /* check for search engines */
1247 search_uri
= find_uri_for_searchengine(s
);
1248 if (search_uri
!= NULL
) {
1249 search_term
= soup_uri_encode(p
+1, "&");
1250 new = g_strdup_printf(search_uri
, search_term
);
1251 g_free(search_term
);
1256 if (len
> 3 && strstr(s
, "://")) { /* valid url? */
1257 p
= new = g_malloc(len
+ 1);
1258 while(*s
!= '\0') { /* strip whitespaces */
1264 } else if (!stat(s
, &statbuf
)) { /* prepend "file://" */
1265 char *rpath
= realpath(s
, NULL
);
1266 if (rpath
!= NULL
) {
1267 len
= strlen(rpath
);
1268 new = g_malloc(sizeof("file://") + len
);
1269 sprintf(new, "file://%s", rpath
);
1272 new = g_malloc(sizeof("file://") + len
);
1273 sprintf(new, "file://%s", s
);
1275 } else if (p
|| !strchr(s
, '.')) { /* whitespaces or no dot? */
1276 search_uri
= find_uri_for_searchengine(defaultsearch
);
1277 if (search_uri
!= NULL
) {
1278 search_term
= soup_uri_encode(s
, "&");
1279 new = g_strdup_printf(search_uri
, search_term
);
1280 g_free(search_term
);
1282 } else { /* prepend "http://" */
1283 new = g_malloc(sizeof("http://") + len
);
1284 strcpy(new, "http://");
1285 memcpy(&new[sizeof("http://") - 1], s
, len
+ 1);
1288 webkit_web_view_load_uri(client
.gui
.webview
, new);
1291 g_spawn_async(NULL
, argv
, NULL
, G_SPAWN_SEARCH_PATH
, NULL
, NULL
, NULL
, NULL
);
1296 open_remembered(const Arg
*arg
)
1298 Arg a
= {arg
->i
, client
.state
.rememberedURI
};
1300 if (strcmp(client
.state
.rememberedURI
, "")) {
1307 yank(const Arg
*arg
) {
1308 const char *url
, *content
;
1310 if (arg
->i
& SourceSelection
) {
1311 webkit_web_view_copy_clipboard(client
.gui
.webview
);
1312 if (arg
->i
& ClipboardPrimary
)
1313 content
= gtk_clipboard_wait_for_text(client
.state
.clipboards
[0]);
1314 if (!content
&& arg
->i
& ClipboardGTK
)
1315 content
= gtk_clipboard_wait_for_text(client
.state
.clipboards
[1]);
1317 echo_message(Info
, "Yanked %s", content
);
1318 g_free((gpointer
*)content
);
1321 if (arg
->i
& SourceURL
) {
1322 url
= webkit_web_view_get_uri(client
.gui
.webview
);
1329 echo_message(Info
, "Yanked %s", url
);
1330 if (arg
->i
& ClipboardPrimary
)
1331 gtk_clipboard_set_text(client
.state
.clipboards
[0], url
, -1);
1332 if (arg
->i
& ClipboardGTK
)
1333 gtk_clipboard_set_text(client
.state
.clipboards
[1], url
, -1);
1339 paste(const Arg
*arg
) {
1340 Arg a
= { .i
= arg
->i
& TargetNew
, .s
= NULL
};
1342 /* If we're over a link, open it in a new target. */
1343 if (strlen(client
.state
.rememberedURI
) > 0) {
1344 Arg new_target
= { .i
= TargetNew
, .s
= arg
->s
};
1345 open_arg(&new_target
);
1349 if (arg
->i
& ClipboardPrimary
)
1350 a
.s
= gtk_clipboard_wait_for_text(client
.state
.clipboards
[0]);
1351 if (!a
.s
&& arg
->i
& ClipboardGTK
)
1352 a
.s
= gtk_clipboard_wait_for_text(client
.state
.clipboards
[1]);
1361 quit(const Arg
*arg
) {
1363 const char *filename
;
1364 const char *uri
= webkit_web_view_get_uri(client
.gui
.webview
);
1366 /* write last URL into status file for recreation with "u" */
1367 filename
= g_strdup_printf("%s", client
.config
.config_base
);
1368 filename
= g_strdup_printf(CLOSED_URL_FILENAME
);
1369 f
= fopen(filename
, "w");
1370 g_free((gpointer
*)filename
);
1372 fprintf(f
, "%s", uri
);
1381 revive(const Arg
*arg
) {
1383 const char *filename
;
1384 char buffer
[512] = "";
1385 Arg a
= { .i
= TargetNew
, .s
= NULL
};
1386 /* get the URL of the window which has been closed last */
1387 filename
= g_strdup_printf(CLOSED_URL_FILENAME
);
1388 f
= fopen(filename
, "r");
1389 g_free((gpointer
*)filename
);
1391 fgets(buffer
, 512, f
);
1394 if (strlen(buffer
) > 0) {
1403 gboolean
print_frame(const Arg
*arg
)
1405 WebKitWebFrame
*frame
= webkit_web_view_get_main_frame(client
.gui
.webview
);
1406 webkit_web_frame_print (frame
);
1411 search(const Arg
*arg
) {
1412 State
*state
= &client
.state
;
1413 state
->count
= state
->count
? state
->count
: 1;
1414 gboolean success
, direction
= arg
->i
& DirectionPrev
;
1417 if (state
->search_handle
) {
1418 g_free(state
->search_handle
);
1420 state
->search_handle
= g_strdup(arg
->s
);
1422 if (!state
->search_handle
)
1424 if (arg
->i
& DirectionAbsolute
)
1425 state
->search_direction
= direction
;
1427 direction
^= state
->search_direction
;
1429 success
= webkit_web_view_search_text(client
.gui
.webview
, state
->search_handle
, arg
->i
& CaseSensitive
, direction
, FALSE
);
1431 if (arg
->i
& Wrapping
) {
1432 success
= webkit_web_view_search_text(client
.gui
.webview
, state
->search_handle
, arg
->i
& CaseSensitive
, direction
, TRUE
);
1434 echo_message(Warning
, "search hit %s, continuing at %s",
1435 direction
? "BOTTOM" : "TOP",
1436 direction
? "TOP" : "BOTTOM");
1442 } while(--state
->count
);
1444 echo_message(Error
, "Pattern not found: %s", state
->search_handle
);
1450 set(const Arg
*arg
) {
1453 if (client
.state
.search_handle
) {
1454 g_free(client
.state
.search_handle
);
1455 client
.state
.search_handle
= NULL
;
1456 webkit_web_view_unmark_text_matches(client
.gui
.webview
);
1458 gtk_entry_set_text(GTK_ENTRY(client
.gui
.inputbox
), "");
1459 gtk_widget_grab_focus(GTK_WIDGET(client
.gui
.webview
));
1461 case ModePassThrough
:
1462 echo_message(Info
| NoAutoHide
, "-- PASS THROUGH --");
1465 echo_message(Info
| NoAutoHide
, "-- PASS TROUGH (next) --");
1467 case ModeInsert
: /* should not be called manually but automatically */
1468 echo_message(Info
| NoAutoHide
, "-- INSERT --");
1473 client
.state
.mode
= arg
->i
;
1478 jsapi_ref_to_string(JSContextRef context
, JSValueRef ref
) {
1479 JSStringRef string_ref
;
1483 string_ref
= JSValueToStringCopy(context
, ref
, NULL
);
1484 length
= JSStringGetMaximumUTF8CStringSize(string_ref
);
1485 string
= g_new(gchar
, length
);
1486 JSStringGetUTF8CString(string_ref
, string
, length
);
1487 JSStringRelease(string_ref
);
1492 jsapi_evaluate_script(const gchar
*script
, gchar
**value
, gchar
**message
) {
1493 WebKitWebFrame
*frame
= webkit_web_view_get_main_frame(client
.gui
.webview
);
1494 JSGlobalContextRef context
= webkit_web_frame_get_global_context(frame
);
1496 JSValueRef val
, exception
;
1498 str
= JSStringCreateWithUTF8CString(script
);
1499 val
= JSEvaluateScript(context
, str
, JSContextGetGlobalObject(context
), NULL
, 0, &exception
);
1500 JSStringRelease(str
);
1502 *message
= jsapi_ref_to_string(context
, exception
);
1504 *value
= jsapi_ref_to_string(context
, val
);
1508 quickmark(const Arg
*a
) {
1511 char *fn
= g_strdup_printf(QUICKMARK_FILE
);
1513 fp
= fopen(fn
, "r");
1518 if (fp
!= NULL
&& b
< 10) {
1519 for( i
=0; i
< b
; ++i
) {
1523 fgets(buf
, 100, fp
);
1525 char *ptr
= strrchr(buf
, '\n');
1528 Arg x
= { .s
= buf
};
1529 return open_arg(&x
);
1531 echo_message(Error
, "Quickmark %d not defined", b
);
1534 } else { return false; }
1538 script(const Arg
*arg
) {
1539 gchar
*value
= NULL
, *message
= NULL
;
1540 char text
[BUF_SIZE
] = "";
1542 WebKitNetworkRequest
*request
;
1543 WebKitDownload
*download
;
1546 set_error("Missing argument.");
1549 jsapi_evaluate_script(arg
->s
, &value
, &message
);
1557 if (arg
->i
!= Silent
&& value
) {
1558 echo_message(arg
->i
, value
);
1560 /* switch mode according to scripts return value */
1562 if (strncmp(value
, "done;", 5) == 0) {
1565 } else if (strncmp(value
, "insert;", 7) == 0) {
1568 client
.state
.manual_focus
= TRUE
;
1569 } else if (strncmp(value
, "save;", 5) == 0) {
1570 /* forced download */
1573 request
= webkit_network_request_new((value
+ 5));
1574 download
= webkit_download_new(request
);
1575 webview_download_cb(client
.gui
.webview
, download
, (gpointer
*)NULL
);
1576 } else if (strncmp(value
, "yank;", 5) == 0) {
1577 /* yank link URL to clipboard */
1580 a
.i
= ClipboardPrimary
| ClipboardGTK
;
1583 } else if (strncmp(value
, "colon;", 6) == 0) {
1584 /* use link URL for colon command */
1585 strncpy(text
, (char *)gtk_entry_get_text(GTK_ENTRY(client
.gui
.inputbox
)), 1023);
1590 a
.s
= g_strconcat(":open ", (value
+ 6), NULL
);
1593 a
.s
= g_strconcat(":tabopen ", (value
+ 6), NULL
);
1600 } else if (strncmp(value
, "open;", 5) == 0 || strncmp(value
, "tabopen;", 8) == 0) {
1601 /* TODO: open element */
1604 if (strncmp(value
, "open;", 5) == 0)
1605 a
.i
= TargetCurrent
;
1608 a
.s
= (strchr(value
, ';') + 1);
1610 } else if (strncmp(value
, "error;", 6) == 0) {
1620 scroll(const Arg
*arg
) {
1621 GtkAdjustment
*adjust
= (arg
->i
& OrientationHoriz
) ? client
.gui
.adjust_h
: client
.gui
.adjust_v
;
1622 int max
= gtk_adjustment_get_upper(adjust
) - gtk_adjustment_get_page_size(adjust
);
1623 float val
= gtk_adjustment_get_value(adjust
) / max
* 100;
1624 int direction
= (arg
->i
& (1 << 2)) ? 1 : -1;
1625 unsigned int count
= client
.state
.count
;
1627 if ((direction
== 1 && val
< 100) || (direction
== -1 && val
> 0)) {
1628 if (arg
->i
& ScrollMove
)
1629 gtk_adjustment_set_value(adjust
, gtk_adjustment_get_value(adjust
) +
1630 direction
* /* direction */
1631 ((arg
->i
& UnitLine
|| (arg
->i
& UnitBuffer
&& count
)) ? (scrollstep
* (count
? count
: 1)) : (
1632 arg
->i
& UnitBuffer
? gtk_adjustment_get_page_size(adjust
) / 2 :
1633 (count
? count
: 1) * (gtk_adjustment_get_page_size(adjust
) -
1634 (gtk_adjustment_get_page_size(adjust
) > pagingkeep
? pagingkeep
: 0)))));
1636 gtk_adjustment_set_value(adjust
,
1637 ((direction
== 1) ? gtk_adjustment_get_upper
: gtk_adjustment_get_lower
)(adjust
));
1644 zoom(const Arg
*arg
) {
1645 unsigned int count
= client
.state
.count
;
1647 webkit_web_view_set_full_content_zoom(client
.gui
.webview
, (arg
->i
& ZoomFullContent
) > 0);
1648 webkit_web_view_set_zoom_level(client
.gui
.webview
, (arg
->i
& ZoomOut
) ?
1649 webkit_web_view_get_zoom_level(client
.gui
.webview
) +
1650 (((float)(count
? count
: 1)) * (arg
->i
& (1 << 1) ? 1.0 : -1.0) * client
.config
.zoomstep
) :
1651 (count
? (float)count
/ 100.0 : 1.0));
1656 fake_key_event(const Arg
*a
) {
1657 if(!client
.state
.embed
) {
1661 if ( (xdpy
= XOpenDisplay(NULL
)) == NULL
) {
1662 echo_message(Error
, "Couldn't find the XDisplay.");
1668 xk
.subwindow
= None
;
1669 xk
.time
= CurrentTime
;
1670 xk
.same_screen
= True
;
1671 xk
.x
= xk
.y
= xk
.x_root
= xk
.y_root
= 1;
1672 xk
.window
= client
.state
.embed
;
1676 echo_message(Error
, "Zero pointer as argument! Check your config.h");
1681 if( (keysym
= XStringToKeysym(a
->s
)) == NoSymbol
) {
1682 echo_message(Error
, "Couldn't translate %s to keysym", a
->s
);
1686 if( (xk
.keycode
= XKeysymToKeycode(xdpy
, keysym
)) == NoSymbol
) {
1687 echo_message(Error
, "Couldn't translate keysym to keycode");
1692 if( !XSendEvent(xdpy
, client
.state
.embed
, True
, KeyPressMask
, (XEvent
*)&xk
) ) {
1693 echo_message(Error
, "XSendEvent failed");
1702 commandhistoryfetch(const Arg
*arg
) {
1703 State
*state
= &client
.state
;
1704 const int length
= g_list_length(client
.state
.commandhistory
);
1705 gchar
*input_message
= NULL
;
1708 if (arg
->i
== DirectionPrev
) {
1709 state
->commandpointer
= (length
+ state
->commandpointer
- 1) % length
;
1711 state
->commandpointer
= (length
+ state
->commandpointer
+ 1) % length
;
1714 const char* command
= (char *)g_list_nth_data(state
->commandhistory
, state
->commandpointer
);
1715 input_message
= g_strconcat(":", command
, NULL
);
1716 gtk_entry_set_text(GTK_ENTRY(client
.gui
.inputbox
), input_message
);
1717 g_free(input_message
);
1718 gtk_editable_set_position(GTK_EDITABLE(client
.gui
.inputbox
), -1);
1726 bookmark(const Arg
*arg
) {
1728 const char *filename
;
1729 const char *uri
= webkit_web_view_get_uri(client
.gui
.webview
);
1730 const char *title
= webkit_web_view_get_title(client
.gui
.webview
);
1731 filename
= g_strdup_printf(BOOKMARKS_STORAGE_FILENAME
);
1732 f
= fopen(filename
, "a");
1733 g_free((gpointer
*)filename
);
1734 if (uri
== NULL
|| strlen(uri
) == 0) {
1735 set_error("No URI found to bookmark.");
1739 fprintf(f
, "%s", uri
);
1740 if (title
!= NULL
) {
1741 fprintf(f
, "%s", " ");
1742 fprintf(f
, "%s", title
);
1744 if (arg
->s
&& strlen(arg
->s
)) {
1745 build_taglist(arg
, f
);
1747 fprintf(f
, "%s", "\n");
1749 echo_message(Info
, "Bookmark saved");
1752 set_error("Bookmarks file not found.");
1760 const char *filename
;
1761 const char *uri
= webkit_web_view_get_uri(client
.gui
.webview
);
1762 const char *title
= webkit_web_view_get_title(client
.gui
.webview
);
1763 char *entry
, buffer
[512], *new;
1765 gboolean finished
= FALSE
;
1767 if (title
!= NULL
) {
1768 entry
= malloc((strlen(uri
) + strlen(title
) + 2) * sizeof(char));
1769 memset(entry
, 0, strlen(uri
) + strlen(title
) + 2);
1771 entry
= malloc((strlen(uri
) + 1) * sizeof(char));
1772 memset(entry
, 0, strlen(uri
) + 1);
1774 if (entry
!= NULL
) {
1775 strncpy(entry
, uri
, strlen(uri
));
1776 if (title
!= NULL
) {
1777 strncat(entry
, " ", 1);
1778 strncat(entry
, title
, strlen(title
));
1781 filename
= g_strdup_printf(HISTORY_STORAGE_FILENAME
);
1782 f
= fopen(filename
, "r");
1784 new = (char *)malloc(HISTORY_MAX_ENTRIES
* 512 * sizeof(char) + 1);
1786 memset(new, 0, HISTORY_MAX_ENTRIES
* 512 * sizeof(char) + 1);
1787 /* newest entries go on top */
1788 strncpy(new, entry
, strlen(entry
));
1789 strncat(new, "\n", 1);
1790 /* retain at most HISTORY_MAX_ENTIRES - 1 old entries */
1791 while (finished
!= TRUE
) {
1792 if ((char *)NULL
== fgets(buffer
, 512, f
)) {
1793 /* check if end of file was reached / error occured */
1797 /* end of file reached */
1801 /* compare line (-1 because of newline character) */
1802 if (n
!= strlen(buffer
) - 1 || strncmp(entry
, buffer
, n
) != 0) {
1803 /* if the URI is already in history; we put it on top and skip it here */
1804 strncat(new, buffer
, 512);
1807 if ((i
+ 1) >= HISTORY_MAX_ENTRIES
) {
1813 f
= fopen(filename
, "w");
1814 g_free((gpointer
*)filename
);
1816 fprintf(f
, "%s", new);
1825 if (entry
!= NULL
) {
1834 view_source(const Arg
* arg
) {
1835 gboolean current_mode
= webkit_web_view_get_view_source_mode(client
.gui
.webview
);
1836 webkit_web_view_set_view_source_mode(client
.gui
.webview
, !current_mode
);
1837 webkit_web_view_reload(client
.gui
.webview
);
1841 /* open an external editor defined by the protocol handler for
1842 vimprobableedit on a text box or similar */
1844 open_editor(const Arg
*arg
) {
1848 gchar
*value
= NULL
, *message
= NULL
, *tag
= NULL
, *edit_url
= NULL
;
1849 gchar
*temp_file_name
= g_strdup_printf("%s/vimprobableeditXXXXXX",
1851 int temp_file_handle
= -1;
1853 /* check if active element is suitable for text editing */
1854 jsapi_evaluate_script("document.activeElement.tagName", &value
, &message
);
1855 if (value
== NULL
) {
1859 tag
= g_strdup(value
);
1860 if (strcmp(tag
, "INPUT") == 0) {
1861 /* extra check: type == text */
1862 jsapi_evaluate_script("document.activeElement.type", &value
, &message
);
1863 if (strcmp(value
, "text") != 0) {
1870 } else if (strcmp(tag
, "TEXTAREA") != 0) {
1875 jsapi_evaluate_script("document.activeElement.value", &value
, &message
);
1876 text
= g_strdup(value
);
1883 /* write text into temporary file */
1884 temp_file_handle
= mkstemp(temp_file_name
);
1885 if (temp_file_handle
== -1) {
1886 message
= g_strdup_printf("Could not create temporary file: %s",
1888 echo_message(Error
, message
);
1894 if (write(temp_file_handle
, text
, strlen(text
)) != strlen(text
)) {
1895 message
= g_strdup_printf("Short write to temporary file: %s",
1897 echo_message(Error
, message
);
1903 close(temp_file_handle
);
1907 edit_url
= g_strdup_printf("vimprobableedit:%s", temp_file_name
);
1908 success
= open_handler_pid(edit_url
, &child_pid
);
1911 echo_message(Error
, "External editor open failed (no handler for"
1912 " vimprobableedit protocol?)");
1913 unlink(temp_file_name
);
1919 /* mark the active text box as "under processing" */
1920 jsapi_evaluate_script(
1921 "document.activeElement.disabled = true;"
1922 "document.activeElement.originalBackground = "
1923 " document.activeElement.style.background;"
1924 "document.activeElement.style.background = '#aaaaaa';"
1927 g_child_watch_add(child_pid
, _resume_from_editor
, temp_file_name
);
1929 /* temp_file_name is freed in _resume_from_editor */
1937 /* pick up from where open_editor left the work to the glib event loop.
1939 This is called when the external editor exits.
1941 The data argument points to allocated memory containing the temporary file
1944 _resume_from_editor(GPid child_pid
, int child_status
, gpointer data
) {
1946 GString
*set_value_js
= g_string_new(
1947 "document.activeElement.value = \"");
1948 g_spawn_close_pid(child_pid
);
1949 gchar
*value
= NULL
, *message
= NULL
;
1950 gchar
*temp_file_name
= data
;
1951 gchar buffer
[BUF_SIZE
] = "";
1952 gchar
*buf_ptr
= buffer
;
1955 jsapi_evaluate_script(
1956 "document.activeElement.disabled = true;"
1957 "document.activeElement.style.background = '#aaaaaa';"
1963 echo_message(Error
, "External editor returned with non-zero status,"
1964 " discarding edits.");
1968 /* re-read the new contents of the file and put it into the HTML element */
1969 if (!access(temp_file_name
, R_OK
) == 0) {
1970 message
= g_strdup_printf("Could not access temporary file: %s",
1974 fp
= fopen(temp_file_name
, "r");
1976 /* this would be too weird to even emit an error message */
1979 jsapi_evaluate_script("document.activeElement.value = '';",
1984 while (EOF
!= (char_read
= fgetc(fp
))) {
1985 if (char_read
== '\n') {
1988 } else if (char_read
== '"') {
1992 *buf_ptr
++ = char_read
;
1994 /* ship out as the buffer when space gets tight. This has
1995 fuzz to save on thinking, plus we have enough space for the
1996 trailing "; in any case. */
1997 if (buf_ptr
-buffer
>=BUF_SIZE
-10) {
1999 g_string_append(set_value_js
, buffer
);
2006 g_string_append(set_value_js
, buffer
);
2009 jsapi_evaluate_script(set_value_js
->str
, &value
, &message
);
2011 /* Fall through, error and normal exit are identical */
2013 jsapi_evaluate_script(
2014 "document.activeElement.disabled = false;"
2015 "document.activeElement.style.background ="
2016 " document.activeElement.originalBackground;"
2019 g_string_free(set_value_js
, TRUE
);
2020 unlink(temp_file_name
);
2021 g_free(temp_file_name
);
2027 focus_input(const Arg
*arg
) {
2030 a
.s
= g_strdup("hints.focusInput();");
2035 client
.state
.manual_focus
= TRUE
;
2043 a
.s
= g_strdup("hints.clearFocus();");
2053 browser_settings(const Arg
*arg
) {
2056 set_error("Missing argument.");
2059 strncpy(line
, arg
->s
, 254);
2060 if (process_set_line(line
))
2063 set_error("Invalid setting.");
2069 search_word(int whichword
) {
2071 static char word
[240];
2072 char *c
= my_pair
.line
;
2074 while (isspace(*c
) && *c
)
2077 switch (whichword
) {
2079 while (*c
&& !isspace (*c
) && *c
!= '=' && k
< 240) {
2084 strncpy(my_pair
.what
, word
, 20);
2087 while (*c
&& k
< 240) {
2092 strncpy(my_pair
.value
, word
, 240);
2100 process_set_line(char *line
) {
2104 WebKitWebSettings
*settings
;
2106 settings
= webkit_web_view_get_settings(client
.gui
.webview
);
2107 my_pair
.line
= line
;
2109 if (!strlen(my_pair
.what
))
2112 while (isspace(*c
) && *c
)
2115 if (*c
== ':' || *c
== '=')
2121 listlen
= LENGTH(browsersettings
);
2122 for (i
= 0; i
< listlen
; i
++) {
2123 if (strlen(browsersettings
[i
].name
) == strlen(my_pair
.what
) && strncmp(browsersettings
[i
].name
, my_pair
.what
, strlen(my_pair
.what
)) == 0) {
2124 /* mandatory argument not provided */
2125 if (strlen(my_pair
.value
) == 0)
2127 /* process qmark? */
2128 if (strlen(my_pair
.what
) == 5 && strncmp("qmark", my_pair
.what
, 5) == 0) {
2129 return (process_save_qmark(my_pair
.value
, client
.gui
.webview
));
2131 /* interpret boolean values */
2132 if (browsersettings
[i
].boolval
) {
2133 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) {
2135 } 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) {
2140 } else if (browsersettings
[i
].colourval
) {
2141 /* interpret as hexadecimal colour */
2142 if (!parse_colour(my_pair
.value
)) {
2146 if (browsersettings
[i
].var
!= NULL
) {
2147 strncpy(browsersettings
[i
].var
, my_pair
.value
, MAX_SETTING_SIZE
);
2148 if (strlen(my_pair
.value
) > MAX_SETTING_SIZE
- 1) {
2149 /* in this case, \0 will not have been copied */
2150 browsersettings
[i
].var
[MAX_SETTING_SIZE
- 1] = '\0';
2151 /* in case this string is also used for a webkit setting, make sure it's consistent */
2152 my_pair
.value
[MAX_SETTING_SIZE
- 1] = '\0';
2153 echo_message(Info
, "String too long; automatically truncated!");
2156 if (strlen(browsersettings
[i
].webkit
) > 0) {
2157 /* activate appropriate webkit setting */
2158 if (browsersettings
[i
].boolval
) {
2159 g_object_set((GObject
*)settings
, browsersettings
[i
].webkit
, boolval
, NULL
);
2160 } else if (browsersettings
[i
].intval
) {
2161 g_object_set((GObject
*)settings
, browsersettings
[i
].webkit
, atoi(my_pair
.value
), NULL
);
2163 g_object_set((GObject
*)settings
, browsersettings
[i
].webkit
, my_pair
.value
, NULL
);
2165 webkit_web_view_set_settings(client
.gui
.webview
, settings
);
2168 if (strlen(my_pair
.what
) == 14) {
2169 if (strncmp("acceptlanguage", my_pair
.what
, 14) == 0) {
2170 g_object_set(G_OBJECT(client
.net
.session
), "accept-language", acceptlanguage
, NULL
);
2171 } else if (strncmp("completioncase", my_pair
.what
, 14) == 0) {
2172 complete_case_sensitive
= boolval
;
2174 } else if (strlen(my_pair
.what
) == 5 && strncmp("proxy", my_pair
.what
, 5) == 0) {
2175 toggle_proxy(boolval
);
2176 } else if (strlen(my_pair
.what
) == 10 && strncmp("scrollbars", my_pair
.what
, 10) == 0) {
2177 toggle_scrollbars(boolval
);
2178 } else if (strlen(my_pair
.what
) == 9 && strncmp("statusbar", my_pair
.what
, 9) == 0) {
2179 gtk_widget_set_visible(GTK_WIDGET(client
.gui
.statusbar
), boolval
);
2180 } else if (strlen(my_pair
.what
) == 8 && strncmp("inputbox", my_pair
.what
, 8) == 0) {
2181 gtk_widget_set_visible(client
.gui
.inputbox
, boolval
);
2182 } else if (strlen(my_pair
.what
) == 11 && strncmp("escapeinput", my_pair
.what
, 11) == 0) {
2183 escape_input_on_load
= boolval
;
2186 /* SSL certificate checking */
2187 if (strlen(my_pair
.what
) == 9 && strncmp("strictssl", my_pair
.what
, 9) == 0) {
2190 g_object_set(G_OBJECT(client
.net
.session
), "ssl-strict", TRUE
, NULL
);
2193 g_object_set(G_OBJECT(client
.net
.session
), "ssl-strict", FALSE
, NULL
);
2196 if (strlen(my_pair
.what
) == 8 && strncmp("cabundle", my_pair
.what
, 8) == 0) {
2197 g_object_set(G_OBJECT(client
.net
.session
), SOUP_SESSION_SSL_CA_FILE
, ca_bundle
, NULL
);
2199 if (strlen(my_pair
.what
) == 10 && strncmp("windowsize", my_pair
.what
, 10) == 0) {
2200 set_default_winsize(my_pair
.value
);
2204 if (browsersettings
[i
].reload
)
2205 webkit_web_view_reload(client
.gui
.webview
);
2213 process_line(char *line
) {
2214 char *c
= line
, *command_hist
;
2216 size_t len
, length
= strlen(line
);
2217 gboolean found
= FALSE
, success
= FALSE
;
2223 /* Ignore blank lines. */
2227 command_hist
= g_strdup(c
);
2229 /* check for colon command aliases first */
2230 for (l
= client
.config
.colon_aliases
; l
; l
= g_list_next(l
)) {
2231 Alias
*alias
= (Alias
*)l
->data
;
2232 if (length
== strlen(alias
->alias
) && strncmp(alias
->alias
, line
, length
) == 0) {
2233 /* reroute to target command */
2235 length
= strlen(alias
->target
);
2240 /* check standard commands */
2241 for (i
= 0; i
< LENGTH(commands
); i
++) {
2242 if (commands
[i
].cmd
== NULL
)
2244 len
= strlen(commands
[i
].cmd
);
2245 if (length
>= len
&& !strncmp(c
, commands
[i
].cmd
, len
) && (c
[len
] == ' ' || !c
[len
])) {
2247 a
.i
= commands
[i
].arg
.i
;
2248 a
.s
= g_strdup(length
> len
+ 1 ? &c
[len
+ 1] : commands
[i
].arg
.s
);
2249 success
= commands
[i
].func(&a
);
2255 save_command_history(command_hist
);
2256 g_free(command_hist
);
2259 echo_message(Error
, "Not a browser command: %s", c
);
2260 } else if (!success
) {
2261 if (client
.state
.error_msg
!= NULL
) {
2262 echo_message(Error
, client
.state
.error_msg
);
2263 g_free(client
.state
.error_msg
);
2264 client
.state
.error_msg
= NULL
;
2266 echo_message(Error
, "Unknown error. Please file a bug report!");
2273 search_tag(const Arg
* a
) {
2275 const char *filename
;
2276 const char *tag
= a
->s
;
2277 char s
[BUFFERSIZE
], foundtag
[40], url
[BUFFERSIZE
];
2281 /* The user must give us something to load up. */
2282 set_error("Bookmark tag required with this option.");
2286 if (strlen(tag
) > MAXTAGSIZE
) {
2287 set_error("Tag too long.");
2291 filename
= g_strdup_printf(BOOKMARKS_STORAGE_FILENAME
);
2292 f
= fopen(filename
, "r");
2293 g_free((gpointer
*)filename
);
2295 set_error("Couldn't open bookmarks file.");
2298 while (fgets(s
, BUFFERSIZE
-1, f
)) {
2301 while (isspace(s
[t
]))
2303 if (s
[t
] != ']') continue;
2316 foundtag
[i
++] = s
[k
++];
2318 /* foundtag now contains the tag */
2319 if (strlen(foundtag
) < MAXTAGSIZE
&& strcmp(tag
, foundtag
) == 0) {
2321 while (isspace(s
[i
])) i
++;
2323 while (s
[i
] && !isspace(s
[i
])) url
[k
++] = s
[i
++];
2325 Arg x
= { .i
= TargetNew
, .s
= url
};
2339 toggle_proxy(gboolean onoff
) {
2341 char *filename
, *new;
2343 if (onoff
== FALSE
) {
2344 g_object_set(client
.net
.session
, "proxy-uri", NULL
, NULL
);
2346 filename
= (char *)g_getenv("http_proxy");
2348 /* Fallthrough to checking HTTP_PROXY as well, since this can also be
2351 if (filename
== NULL
)
2352 filename
= (char *)g_getenv("HTTP_PROXY");
2354 if (filename
!= NULL
&& 0 < strlen(filename
)) {
2355 new = g_strrstr(filename
, "http://") ? g_strdup(filename
) : g_strdup_printf("http://%s", filename
);
2356 proxy_uri
= soup_uri_new(new);
2358 g_object_set(client
.net
.session
, "proxy-uri", proxy_uri
, NULL
);
2360 soup_uri_free(proxy_uri
);
2367 toggle_scrollbars(gboolean onoff
) {
2368 Gui
*gui
= &client
.gui
;
2369 if (onoff
== TRUE
) {
2370 gui
->adjust_h
= gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(gui
->viewport
));
2371 gui
->adjust_v
= gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(gui
->viewport
));
2372 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gui
->viewport
), GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
2374 gui
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(gui
->scroll_v
));
2375 gui
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(gui
->scroll_h
));
2376 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gui
->viewport
), GTK_POLICY_NEVER
, GTK_POLICY_NEVER
);
2378 gtk_widget_set_scroll_adjustments (GTK_WIDGET(gui
->webview
), gui
->adjust_h
, gui
->adjust_v
);
2383 void set_default_winsize(const char * const size
) {
2385 int x
= 640, y
= 480;
2387 x
= strtol(size
, &p
, 10);
2388 if (errno
== ERANGE
|| x
<= 0) {
2393 if (p
== size
|| strlen(size
) == p
- size
)
2396 y
= strtol(p
+ 1, NULL
, 10);
2397 if (errno
== ERANGE
|| y
<= 0)
2401 gtk_window_resize(GTK_WINDOW(client
.gui
.window
), x
, y
);
2405 update_url(const char *uri
) {
2406 Gui
*gui
= &client
.gui
;
2407 gboolean ssl
= g_str_has_prefix(uri
, "https://");
2409 WebKitWebFrame
*frame
;
2410 WebKitWebDataSource
*src
;
2411 WebKitNetworkRequest
*request
;
2414 char *sslactivecolor
;
2416 #ifdef ENABLE_HISTORY_INDICATOR
2417 char before
[] = " [";
2419 gboolean back
= webkit_web_view_can_go_back(gui
->webview
);
2420 gboolean fwd
= webkit_web_view_can_go_forward(gui
->webview
);
2423 before
[0] = after
[0] = '\0';
2425 markup
= g_markup_printf_escaped(
2426 #ifdef ENABLE_HISTORY_INDICATOR
2427 "<span font=\"%s\">%s%s%s%s%s</span>", statusfont
, uri
,
2428 before
, back
? "+" : "", fwd
? "-" : "", after
2430 "<span font=\"%s\">%s</span>", statusfont
, uri
2433 gtk_label_set_markup(GTK_LABEL(gui
->status_url
), markup
);
2436 frame
= webkit_web_view_get_main_frame(gui
->webview
);
2437 src
= webkit_web_frame_get_data_source(frame
);
2438 request
= webkit_web_data_source_get_request(src
);
2439 msg
= webkit_network_request_get_message(request
);
2440 ssl_ok
= soup_message_get_flags(msg
) & SOUP_MESSAGE_CERTIFICATE_TRUSTED
;
2442 sslactivecolor
= sslbgcolor
;
2444 sslactivecolor
= sslinvalidbgcolor
;
2446 gdk_color_parse(ssl
? sslactivecolor
: statusbgcolor
, &color
);
2447 gtk_widget_modify_bg(gui
->eventbox
, GTK_STATE_NORMAL
, &color
);
2448 gdk_color_parse(ssl
? sslcolor
: statuscolor
, &color
);
2449 gtk_widget_modify_fg(GTK_WIDGET(gui
->status_url
), GTK_STATE_NORMAL
, &color
);
2450 gtk_widget_modify_fg(GTK_WIDGET(gui
->status_state
), GTK_STATE_NORMAL
, &color
);
2455 State
* state
= &client
.state
;
2457 int download_count
= g_list_length(state
->activeDownloads
);
2458 GString
*status
= g_string_new("");
2460 /* construct the status line */
2462 /* count, modkey and input buffer */
2463 g_string_append_printf(status
, "%.0d", state
->count
);
2464 if (state
->current_modkey
) g_string_append_c(status
, state
->current_modkey
);
2466 /* the number of active downloads */
2467 if (state
->activeDownloads
) {
2468 g_string_append_printf(status
, " %d active %s", download_count
,
2469 (download_count
== 1) ? "download" : "downloads");
2472 #ifdef ENABLE_WGET_PROGRESS_BAR
2473 /* the progressbar */
2476 char progressbar
[progressbartick
+ 1];
2478 if (state
->activeDownloads
) {
2482 for (ptr
= state
->activeDownloads
; ptr
; ptr
= g_list_next(ptr
)) {
2483 progress
+= 100 * webkit_download_get_progress(ptr
->data
);
2486 progress
/= download_count
;
2488 } else if (webkit_web_view_get_load_status(client
.gui
.webview
) != WEBKIT_LOAD_FINISHED
2489 && webkit_web_view_get_load_status(client
.gui
.webview
) != WEBKIT_LOAD_FAILED
) {
2491 progress
= webkit_web_view_get_progress(client
.gui
.webview
) * 100;
2494 if (progress
>= 0) {
2495 ascii_bar(progressbartick
, progress
* progressbartick
/ 100, progressbar
);
2496 g_string_append_printf(status
, " %c%s%c",
2497 progressborderleft
, progressbar
, progressborderright
);
2502 /* and the current scroll position */
2504 int max
= gtk_adjustment_get_upper(client
.gui
.adjust_v
) - gtk_adjustment_get_page_size(client
.gui
.adjust_v
);
2505 int val
= (int)(gtk_adjustment_get_value(client
.gui
.adjust_v
) / max
* 100);
2508 g_string_append(status
, " All");
2510 g_string_append(status
, " Top");
2511 else if (val
== 100)
2512 g_string_append(status
, " Bot");
2514 g_string_append_printf(status
, " %d%%", val
);
2518 markup
= g_markup_printf_escaped("<span font=\"%s\">%s</span>", statusfont
, status
->str
);
2519 gtk_label_set_markup(GTK_LABEL(client
.gui
.status_state
), markup
);
2522 g_string_free(status
, TRUE
);
2526 setup_client(void) {
2527 State
*state
= &client
.state
;
2528 Config
*config
= &client
.config
;
2530 state
->mode
= ModeNormal
;
2532 state
->rememberedURI
[0] = '\0';
2533 state
->manual_focus
= FALSE
;
2534 state
->is_inspecting
= FALSE
;
2535 state
->commandhistory
= NULL
;
2536 state
->commandpointer
= 0;
2538 config
->colon_aliases
= NULL
;
2539 config
->cookie_timeout
= 4800;
2545 client
.config
.modkeys
= calloc(LENGTH(keys
) + 1, sizeof(char));
2546 char *ptr
= client
.config
.modkeys
;
2548 for (i
= 0; i
< LENGTH(keys
); i
++)
2549 if (keys
[i
].modkey
&& !strchr(client
.config
.modkeys
, keys
[i
].modkey
))
2550 *(ptr
++) = keys
[i
].modkey
;
2551 client
.config
.modkeys
= realloc(client
.config
.modkeys
, &ptr
[0] - &client
.config
.modkeys
[0] + 1);
2556 Gui
*gui
= &client
.gui
;
2557 State
*state
= &client
.state
;
2559 gui
->scroll_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
2560 gui
->scroll_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
2561 gui
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(gui
->scroll_h
));
2562 gui
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(gui
->scroll_v
));
2563 if (client
.state
.embed
) {
2564 gui
->window
= GTK_WINDOW(gtk_plug_new(client
.state
.embed
));
2566 gui
->window
= GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL
));
2567 gtk_window_set_wmclass(GTK_WINDOW(gui
->window
), "vimprobable2", "Vimprobable2");
2569 gtk_window_set_default_size(GTK_WINDOW(gui
->window
), 640, 480);
2570 gui
->box
= GTK_BOX(gtk_vbox_new(FALSE
, 0));
2571 gui
->inputbox
= gtk_entry_new();
2572 gui
->webview
= (WebKitWebView
*)webkit_web_view_new();
2573 gui
->statusbar
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
2574 gui
->eventbox
= gtk_event_box_new();
2575 gui
->status_url
= gtk_label_new(NULL
);
2576 gui
->status_state
= gtk_label_new(NULL
);
2578 PangoFontDescription
*font
;
2579 GdkGeometry hints
= { 1, 1 };
2580 gui
->inspector
= webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(gui
->webview
));
2582 state
->clipboards
[0] = gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2583 state
->clipboards
[1] = gtk_clipboard_get(GDK_NONE
);
2585 gdk_color_parse(statusbgcolor
, &bg
);
2586 gtk_widget_modify_bg(gui
->eventbox
, GTK_STATE_NORMAL
, &bg
);
2587 gtk_widget_set_name(GTK_WIDGET(gui
->window
), "Vimprobable2");
2588 gtk_window_set_geometry_hints(gui
->window
, NULL
, &hints
, GDK_HINT_MIN_SIZE
);
2590 state
->keymap
= gdk_keymap_get_default();
2592 #ifdef DISABLE_SCROLLBAR
2593 gui
->viewport
= gtk_scrolled_window_new(NULL
, NULL
);
2594 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gui
->viewport
), GTK_POLICY_NEVER
, GTK_POLICY_NEVER
);
2596 /* Ensure we still see scrollbars. */
2597 gui
->viewport
= gtk_scrolled_window_new(gui
->adjust_h
, gui
->adjust_v
);
2600 gui
->pane
= gtk_vpaned_new();
2601 gtk_paned_pack1(GTK_PANED(gui
->pane
), GTK_WIDGET(gui
->box
), TRUE
, TRUE
);
2604 gtk_container_add(GTK_CONTAINER(gui
->viewport
), GTK_WIDGET(gui
->webview
));
2606 /* Ensure we set the scroll adjustments now, so that if we're not drawing
2607 * titlebars, we can still scroll.
2609 gtk_widget_set_scroll_adjustments(GTK_WIDGET(gui
->webview
), gui
->adjust_h
, gui
->adjust_v
);
2611 font
= pango_font_description_from_string(urlboxfont
[0]);
2612 gtk_widget_modify_font(GTK_WIDGET(gui
->inputbox
), font
);
2613 pango_font_description_free(font
);
2614 gtk_entry_set_inner_border(GTK_ENTRY(gui
->inputbox
), NULL
);
2615 gtk_misc_set_alignment(GTK_MISC(gui
->status_url
), 0.0, 0.0);
2616 gtk_misc_set_alignment(GTK_MISC(gui
->status_state
), 1.0, 0.0);
2617 gtk_box_pack_start(gui
->statusbar
, gui
->status_url
, TRUE
, TRUE
, 2);
2618 gtk_box_pack_start(gui
->statusbar
, gui
->status_state
, FALSE
, FALSE
, 2);
2619 gtk_container_add(GTK_CONTAINER(gui
->eventbox
), GTK_WIDGET(gui
->statusbar
));
2620 gtk_box_pack_start(gui
->box
, gui
->viewport
, TRUE
, TRUE
, 0);
2621 gtk_box_pack_start(gui
->box
, gui
->eventbox
, FALSE
, FALSE
, 0);
2622 gtk_entry_set_has_frame(GTK_ENTRY(gui
->inputbox
), FALSE
);
2623 gtk_box_pack_end(gui
->box
, gui
->inputbox
, FALSE
, FALSE
, 0);
2624 gtk_container_add(GTK_CONTAINER(gui
->window
), GTK_WIDGET(gui
->pane
));
2625 gtk_widget_grab_focus(GTK_WIDGET(gui
->webview
));
2626 gtk_widget_show_all(GTK_WIDGET(gui
->window
));
2627 set_widget_font_and_color(gui
->inputbox
, urlboxfont
[0], urlboxbgcolor
[0], urlboxcolor
[0]);
2628 g_object_set(gtk_widget_get_settings(gui
->inputbox
), "gtk-entry-select-on-focus", FALSE
, NULL
);
2633 WebKitWebSettings
*settings
= (WebKitWebSettings
*)webkit_web_settings_new();
2634 char *filename
, *file_url
;
2636 client
.net
.session
= webkit_get_default_session();
2637 g_object_set(G_OBJECT(client
.net
.session
), "ssl-ca-file", ca_bundle
, NULL
);
2638 g_object_set(G_OBJECT(client
.net
.session
), "ssl-strict", strict_ssl
, NULL
);
2639 g_object_set(G_OBJECT(settings
), "default-font-size", DEFAULT_FONT_SIZE
, NULL
);
2640 g_object_set(G_OBJECT(settings
), "enable-scripts", enablePlugins
, NULL
);
2641 g_object_set(G_OBJECT(settings
), "enable-plugins", enablePlugins
, NULL
);
2642 g_object_set(G_OBJECT(settings
), "enable-java-applet", enableJava
, NULL
);
2643 g_object_set(G_OBJECT(settings
), "enable-page-cache", enablePagecache
, NULL
);
2644 filename
= g_strdup_printf(USER_STYLESHEET
);
2645 file_url
= g_strdup_printf("file://%s", filename
);
2646 g_object_set(G_OBJECT(settings
), "user-stylesheet-uri", file_url
, NULL
);
2649 g_object_set(G_OBJECT(settings
), "user-agent", useragent
, NULL
);
2650 g_object_get(G_OBJECT(settings
), "zoom-step", &client
.config
.zoomstep
, NULL
);
2651 webkit_web_view_set_settings(client
.gui
.webview
, settings
);
2654 toggle_proxy(use_proxy
);
2659 WebKitWebFrame
*frame
= webkit_web_view_get_main_frame(client
.gui
.webview
);
2660 #ifdef ENABLE_COOKIE_SUPPORT
2662 g_signal_connect_after(G_OBJECT(client
.net
.session
), "request-started", G_CALLBACK(new_generic_request
), NULL
);
2664 /* Accept-language header */
2665 g_object_set(G_OBJECT(client
.net
.session
), "accept-language", acceptlanguage
, NULL
);
2667 g_object_connect(G_OBJECT(client
.gui
.window
),
2668 "signal::destroy", G_CALLBACK(window_destroyed_cb
), NULL
,
2671 g_signal_connect(G_OBJECT(frame
),
2672 "scrollbars-policy-changed", G_CALLBACK(blank_cb
), NULL
);
2674 g_object_connect(G_OBJECT(client
.gui
.webview
),
2675 "signal::title-changed", G_CALLBACK(webview_title_changed_cb
), NULL
,
2676 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), NULL
,
2677 "signal::load-committed", G_CALLBACK(webview_load_committed_cb
), NULL
,
2678 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), NULL
,
2679 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_navigation_cb
), NULL
,
2680 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_new_window_cb
), NULL
,
2681 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), NULL
,
2682 "signal::download-requested", G_CALLBACK(webview_download_cb
), NULL
,
2683 "signal::key-press-event", G_CALLBACK(webview_keypress_cb
), NULL
,
2684 "signal::hovering-over-link", G_CALLBACK(webview_hoverlink_cb
), NULL
,
2685 "signal::console-message", G_CALLBACK(webview_console_cb
), NULL
,
2686 "signal::create-web-view", G_CALLBACK(webview_open_in_new_window_cb
), NULL
,
2687 "signal::event", G_CALLBACK(notify_event_cb
), NULL
,
2689 /* webview adjustment */
2690 g_object_connect(G_OBJECT(client
.gui
.adjust_v
),
2691 "signal::value-changed", G_CALLBACK(webview_scroll_cb
), NULL
,
2694 g_object_connect(G_OBJECT(client
.gui
.inputbox
),
2695 "signal::activate", G_CALLBACK(inputbox_activate_cb
), NULL
,
2696 "signal::key-press-event", G_CALLBACK(inputbox_keypress_cb
), NULL
,
2697 "signal::key-release-event", G_CALLBACK(inputbox_keyrelease_cb
), NULL
,
2698 "signal::changed", G_CALLBACK(inputbox_changed_cb
), NULL
,
2701 g_signal_connect(G_OBJECT(client
.gui
.inspector
),
2702 "inspect-web-view", G_CALLBACK(inspector_new_cb
), NULL
);
2703 g_signal_connect(G_OBJECT(client
.gui
.inspector
),
2704 "show-window", G_CALLBACK(inspector_show_cb
), NULL
);
2705 g_signal_connect(G_OBJECT(client
.gui
.inspector
),
2706 "close-window", G_CALLBACK(inspector_close_cb
), NULL
);
2707 g_signal_connect(G_OBJECT(client
.gui
.inspector
),
2708 "finished", G_CALLBACK(inspector_finished_cb
), NULL
);
2711 #ifdef ENABLE_USER_SCRIPTFILE
2713 scripts_run_user_file() {
2714 gchar
*js
= NULL
, *user_scriptfile
= NULL
;
2715 GError
*error
= NULL
;
2717 user_scriptfile
= g_strdup_printf(USER_SCRIPTFILE
);
2719 /* run the users script file */
2720 if (g_file_test(user_scriptfile
, G_FILE_TEST_IS_REGULAR
)
2721 && g_file_get_contents(user_scriptfile
, &js
, NULL
, &error
)) {
2723 gchar
*value
= NULL
, *message
= NULL
;
2725 jsapi_evaluate_script(js
, &value
, &message
);
2728 fprintf(stderr
, "%s", message
);
2733 fprintf(stderr
, "Cannot open %s: %s\n", user_scriptfile
, error
? error
->message
: "file not found");
2736 g_free(user_scriptfile
);
2740 #ifdef ENABLE_COOKIE_SUPPORT
2744 Network
*net
= &client
.net
;
2745 if (net
->file_cookie_jar
)
2746 g_object_unref(net
->file_cookie_jar
);
2748 if (net
->session_cookie_jar
)
2749 g_object_unref(net
->session_cookie_jar
);
2751 net
->session_cookie_jar
= soup_cookie_jar_new();
2753 net
->cookie_store
= g_strdup_printf(COOKIES_STORAGE_FILENAME
);
2757 g_signal_connect(G_OBJECT(net
->file_cookie_jar
), "changed",
2758 G_CALLBACK(update_cookie_jar
), NULL
);
2761 /* TA: XXX - we should be using this callback for any header-requests we
2762 * receive (hence the name "new_generic_request" -- but for now, its use
2763 * is limited to handling cookies.
2766 new_generic_request(SoupSession
*session
, SoupMessage
*soup_msg
, gpointer unused
)
2768 SoupMessageHeaders
*soup_msg_h
;
2772 soup_msg_h
= soup_msg
->request_headers
;
2773 soup_message_headers_remove(soup_msg_h
, "Cookie");
2774 uri
= soup_message_get_uri(soup_msg
);
2775 if ((cookie_str
= get_cookies(uri
))) {
2776 soup_message_headers_append(soup_msg_h
, "Cookie", cookie_str
);
2780 g_signal_connect_after(G_OBJECT(soup_msg
), "got-headers", G_CALLBACK(handle_cookie_request
), NULL
);
2786 get_cookies(SoupURI
*soup_uri
) {
2789 cookie_str
= soup_cookie_jar_get_cookies(client
.net
.file_cookie_jar
, soup_uri
, TRUE
);
2795 handle_cookie_request(SoupMessage
*soup_msg
, gpointer unused
)
2797 GSList
*resp_cookie
= NULL
, *cookie_list
;
2800 cookie_list
= soup_cookies_from_response(soup_msg
);
2801 for(resp_cookie
= cookie_list
; resp_cookie
; resp_cookie
= g_slist_next(resp_cookie
))
2803 SoupDate
*soup_date
;
2804 cookie
= soup_cookie_copy((SoupCookie
*)resp_cookie
->data
);
2806 if (client
.config
.cookie_timeout
&& cookie
->expires
== NULL
) {
2807 soup_date
= soup_date_new_from_time_t(time(NULL
) + client
.config
.cookie_timeout
* 10);
2808 soup_cookie_set_expires(cookie
, soup_date
);
2809 soup_date_free(soup_date
);
2811 soup_cookie_jar_add_cookie(client
.net
.file_cookie_jar
, cookie
);
2814 soup_cookies_free(cookie_list
);
2820 update_cookie_jar(SoupCookieJar
*jar
, SoupCookie
*old
, SoupCookie
*new)
2823 /* Nothing to do. */
2828 copy
= soup_cookie_copy(new);
2830 soup_cookie_jar_add_cookie(client
.net
.session_cookie_jar
, copy
);
2836 load_all_cookies(void)
2838 Network
*net
= &client
.net
;
2839 GSList
*cookie_list
;
2840 net
->file_cookie_jar
= soup_cookie_jar_text_new(net
->cookie_store
, COOKIES_STORAGE_READONLY
);
2842 /* Put them back in the session store. */
2843 GSList
*cookies_from_file
= soup_cookie_jar_all_cookies(net
->file_cookie_jar
);
2844 cookie_list
= cookies_from_file
;
2846 for (; cookies_from_file
;
2847 cookies_from_file
= cookies_from_file
->next
)
2849 soup_cookie_jar_add_cookie(net
->session_cookie_jar
, cookies_from_file
->data
);
2852 soup_cookies_free(cookies_from_file
);
2853 g_slist_free(cookie_list
);
2862 /* Free up any nasty globals before exiting. */
2863 #ifdef ENABLE_COOKIE_SUPPORT
2864 if (client
.net
.cookie_store
)
2865 g_free(client
.net
.cookie_store
);
2871 main(int argc
, char *argv
[]) {
2873 static char url
[256] = "";
2874 static gboolean ver
= false;
2875 static gboolean configfile_exists
= FALSE
;
2876 static const char *cfile
= NULL
;
2877 static gchar
*winid
= NULL
;
2878 static GOptionEntry opts
[] = {
2879 { "version", 'v', 0, G_OPTION_ARG_NONE
, &ver
, "print version", NULL
},
2880 { "embed", 'e', 0, G_OPTION_ARG_STRING
, &winid
, "embedded", NULL
},
2881 { "configfile", 'c', 0, G_OPTION_ARG_STRING
, &cfile
, "config file", NULL
},
2886 Config
*config
= &client
.config
;
2888 /* command line argument: version */
2889 if (!gtk_init_with_args(&argc
, &argv
, "[<uri>]", opts
, NULL
, &err
)) {
2890 g_printerr("can't init gtk: %s\n", err
->message
);
2892 return EXIT_FAILURE
;
2896 printf("%s\n", INTERNAL_VERSION
);
2897 return EXIT_SUCCESS
;
2902 if (getenv("TMPDIR")) {
2903 strncpy(temp_dir
, getenv("TMPDIR"), MAX_SETTING_SIZE
);
2904 temp_dir
[MAX_SETTING_SIZE
-1] = 0;
2907 if( getenv("XDG_CONFIG_HOME") )
2908 config
->config_base
= g_strdup_printf("%s", getenv("XDG_CONFIG_HOME"));
2910 config
->config_base
= g_strdup_printf("%s/.config/", getenv("HOME"));
2913 config
->configfile
= g_strdup(cfile
);
2915 config
->configfile
= g_strdup_printf(RCFILE
);
2917 if (!g_thread_supported())
2918 g_thread_init(NULL
);
2921 if (strncmp(winid
, "0x", 2) == 0) {
2922 client
.state
.embed
= strtol(winid
, NULL
, 16);
2924 client
.state
.embed
= atoi(winid
);
2931 #ifdef ENABLE_COOKIE_SUPPORT
2935 make_searchengines_list(searchengines
, LENGTH(searchengines
));
2936 make_uri_handlers_list(uri_handlers
, LENGTH(uri_handlers
));
2938 /* Check if the specified file exists. */
2939 /* And only warn the user, if they explicitly asked for a config on the
2942 if (!(access(config
->configfile
, F_OK
) == 0) && cfile
) {
2943 echo_message(Info
, "Config file '%s' doesn't exist", cfile
);
2944 } else if ((access(config
->configfile
, F_OK
) == 0))
2945 configfile_exists
= true;
2947 /* read config file */
2948 /* But only report errors if we failed, and the file existed. */
2949 if ((SUCCESS
!= read_rcfile(config
->configfile
)) && configfile_exists
) {
2950 echo_message(Error
, "Error in config file '%s'", config
->configfile
);
2951 g_free(config
->configfile
);
2954 /* command line argument: URL */
2956 strncpy(url
, argv
[argc
- 1], 255);
2958 strncpy(url
, startpage
, 255);
2961 a
.i
= TargetCurrent
;
2968 return EXIT_SUCCESS
;