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
8 (c) 2011-2013 by Daniel Carl
9 (c) 2012 by Matthew Carter
14 #include <sys/types.h>
19 #include "vimprobable.h"
20 #include "utilities.h"
21 #include "callbacks.h"
22 #include "javascript.h"
24 /* the CLEAN_MOD_*_MASK defines have all the bits set that will be stripped from the modifier bit field */
25 #define CLEAN_MOD_NUMLOCK_MASK (GDK_MOD2_MASK)
26 #define CLEAN_MOD_BUTTON_MASK (GDK_BUTTON1_MASK|GDK_BUTTON2_MASK|GDK_BUTTON3_MASK|GDK_BUTTON4_MASK|GDK_BUTTON5_MASK)
28 /* remove unused bits, numlock symbol and buttons from keymask */
29 #define CLEAN(mask) (mask & (GDK_MODIFIER_MASK) & ~(CLEAN_MOD_NUMLOCK_MASK) & ~(CLEAN_MOD_BUTTON_MASK))
31 #define IS_ESCAPE(event) (IS_ESCAPE_KEY(CLEAN(event->state), event->keyval))
32 #define IS_ESCAPE_KEY(s, k) ((s == 0 && k == GDK_Escape) || \
33 (s == GDK_CONTROL_MASK && k == GDK_bracketleft))
36 static void inputbox_activate_cb(GtkEntry
*entry
, gpointer user_data
);
37 static gboolean
inputbox_keypress_cb(GtkEntry
*entry
, GdkEventKey
*event
);
38 static gboolean
inputbox_keyrelease_cb(GtkEntry
*entry
, GdkEventKey
*event
);
39 static gboolean
inputbox_changed_cb(GtkEditable
*entry
, gpointer user_data
);
40 static WebKitWebView
* inspector_inspect_web_view_cb(gpointer inspector
, WebKitWebView
* web_view
);
41 static gboolean
notify_event_cb(GtkWidget
*widget
, GdkEvent
*event
, gpointer user_data
);
42 static gboolean
webview_console_cb(WebKitWebView
*webview
, char *message
, int line
, char *source
, gpointer user_data
);
43 static gboolean
webview_download_cb(WebKitWebView
*webview
, WebKitDownload
*download
, gpointer user_data
);
44 static void webview_hoverlink_cb(WebKitWebView
*webview
, char *title
, char *link
, gpointer data
);
45 static gboolean
webview_keypress_cb(WebKitWebView
*webview
, GdkEventKey
*event
);
46 static void webview_load_committed_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
);
47 static void webview_load_finished_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
);
48 static gboolean
webview_mimetype_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
,
49 char *mime_type
, WebKitWebPolicyDecision
*decision
, gpointer user_data
);
50 static void webview_open_js_window_cb(WebKitWebView
* temp_view
, GParamSpec param_spec
);
51 static gboolean
webview_new_window_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
,
52 WebKitWebNavigationAction
*action
, WebKitWebPolicyDecision
*decision
, gpointer user_data
);
53 static WebKitWebView
* webview_open_in_new_window_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
);
54 static void webview_progress_changed_cb(WebKitWebView
*webview
, int progress
, gpointer user_data
);
55 static void webview_title_changed_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, char *title
, gpointer user_data
);
56 static void window_destroyed_cb(GtkWidget
*window
, gpointer func_data
);
57 static gboolean
blank_cb(void);
60 static gboolean
bookmark(const Arg
*arg
);
61 static gboolean
browser_settings(const Arg
*arg
);
62 static gboolean
commandhistoryfetch(const Arg
*arg
);
63 static gboolean
complete(const Arg
*arg
);
64 static gboolean
descend(const Arg
*arg
);
65 gboolean
echo(const Arg
*arg
);
66 static gboolean
focus_input(const Arg
*arg
);
67 static gboolean
open_editor(const Arg
*arg
);
68 void _resume_from_editor(GPid child_pid
, int status
, gpointer data
);
69 static gboolean
input(const Arg
*arg
);
70 static gboolean
open_inspector(const Arg
* arg
);
71 static gboolean
navigate(const Arg
*arg
);
72 static gboolean
number(const Arg
*arg
);
73 static gboolean
open_arg(const Arg
*arg
);
74 static gboolean
open_remembered(const Arg
*arg
);
75 static gboolean
paste(const Arg
*arg
);
76 static gboolean
quickmark(const Arg
*arg
);
77 static gboolean
quit(const Arg
*arg
);
78 static gboolean
revive(const Arg
*arg
);
79 static gboolean
print_frame(const Arg
*arg
);
80 static gboolean
search(const Arg
*arg
);
81 static gboolean
set(const Arg
*arg
);
82 static gboolean
script(const Arg
*arg
);
83 static gboolean
scroll(const Arg
*arg
);
84 static gboolean
search_tag(const Arg
*arg
);
85 static gboolean
yank(const Arg
*arg
);
86 static gboolean
view_source(const Arg
* arg
);
87 static gboolean
zoom(const Arg
*arg
);
88 static gboolean
fake_key_event(const Arg
*arg
);
90 static void clear_focus(void);
91 static void update_url(const char *uri
);
92 static void setup_client(void);
93 static void setup_modkeys(void);
94 static void setup_gui(void);
95 static void setup_settings(void);
96 static void setup_signals(void);
97 static void ascii_bar(int total
, int state
, char *string
);
98 static gchar
*jsapi_ref_to_string(JSContextRef context
, JSValueRef ref
);
99 static void jsapi_evaluate_script(const gchar
*script
, gchar
**value
, gchar
**message
);
100 static void download_progress(WebKitDownload
*d
, GParamSpec
*pspec
);
101 static void set_widget_font_and_color(GtkWidget
*widget
, const char *font_str
,
102 const char *bg_color_str
, const char *fg_color_str
);
103 static void scripts_run_user_file(void);
105 static gboolean
history(void);
106 static gboolean
process_set_line(char *line
);
107 void save_command_history(char *line
);
108 void toggle_proxy(gboolean onoff
);
109 void toggle_scrollbars(gboolean onoff
);
110 void set_default_winsize(const char * const size
);
112 gboolean
process_keypress(GdkEventKey
*event
);
113 void fill_suggline(char * suggline
, const char * command
, const char *fill_with
);
114 GtkWidget
* fill_eventbox(const char * completion_line
);
115 static void mop_up(void);
125 /* Cookie support. */
126 #ifdef ENABLE_COOKIE_SUPPORT
127 static void setup_cookies(void);
128 static char *get_cookies(SoupURI
*soup_uri
);
129 static void load_all_cookies(void);
130 static void new_generic_request(SoupSession
*soup_ses
, SoupMessage
*soup_msg
, gpointer unused
);
131 static void update_cookie_jar(SoupCookieJar
*jar
, SoupCookie
*old
, SoupCookie
*new);
132 static void handle_cookie_request(SoupMessage
*soup_msg
, gpointer unused
);
139 window_destroyed_cb(GtkWidget
*window
, gpointer func_data
) {
144 webview_title_changed_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, char *title
, gpointer user_data
) {
145 gtk_window_set_title(client
.gui
.window
, title
);
149 webview_progress_changed_cb(WebKitWebView
*webview
, int progress
, gpointer user_data
) {
150 #ifdef ENABLE_GTK_PROGRESS_BAR
151 gtk_entry_set_progress_fraction(GTK_ENTRY(client
.gui
.inputbox
), progress
== 100 ? 0 : (double)progress
/100);
156 #ifdef ENABLE_WGET_PROGRESS_BAR
158 ascii_bar(int total
, int state
, char *string
) {
161 for (i
= 0; i
< state
; i
++)
162 string
[i
] = progressbartickchar
;
163 string
[i
++] = progressbarcurrent
;
164 for (; i
< total
; i
++)
165 string
[i
] = progressbarspacer
;
171 webview_load_committed_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
) {
172 Arg a
= { .i
= Silent
, .s
= g_strdup(JS_SETUP_HINTS
) };
173 const char *uri
= webkit_web_view_get_uri(webview
);
178 scripts_run_user_file();
180 if (client
.state
.mode
== ModeInsert
|| client
.state
.mode
== ModeHints
) {
181 Arg a
= { .i
= ModeNormal
};
184 client
.state
.manual_focus
= FALSE
;
188 webview_load_finished_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
) {
189 WebKitWebSettings
*settings
= webkit_web_view_get_settings(webview
);
192 g_object_get(settings
, "enable-scripts", &scripts
, NULL
);
193 if (escape_input_on_load
&& scripts
&& !client
.state
.manual_focus
&& !gtk_widget_is_focus(client
.gui
.inputbox
)) {
196 if (HISTORY_MAX_ENTRIES
> 0)
202 webview_open_js_window_cb(WebKitWebView
* temp_view
, GParamSpec param_spec
) {
203 /* retrieve the URI of the temporary webview */
204 Arg a
= { .i
= TargetNew
, .s
= (char*)webkit_web_view_get_uri(temp_view
) };
206 webkit_web_view_stop_loading(temp_view
);
207 gtk_widget_destroy(GTK_WIDGET(temp_view
));
208 /* open the requested window */
212 static WebKitWebView
*
213 webview_open_in_new_window_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
) {
214 if (client
.state
.rememberedURI
!= NULL
&& strlen(client
.state
.rememberedURI
) > 0) {
215 if (strncmp(client
.state
.rememberedURI
, "javascript:", 11) != 0) {
216 Arg a
= { .i
= TargetNew
, .s
= client
.state
.rememberedURI
};
221 /* create a temporary webview to execute the script in */
222 WebKitWebView
*temp_view
= WEBKIT_WEB_VIEW(webkit_web_view_new());
223 /* wait until the new webview receives its new URI */
224 g_object_connect(temp_view
, "signal::notify::uri", G_CALLBACK(webview_open_js_window_cb
), NULL
, NULL
);
229 webview_new_window_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
,
230 WebKitWebNavigationAction
*action
, WebKitWebPolicyDecision
*decision
, gpointer user_data
) {
231 Arg a
= { .i
= TargetNew
, .s
= (char*)webkit_network_request_get_uri(request
) };
233 webkit_web_policy_decision_ignore(decision
);
238 webview_mimetype_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
,
239 char *mime_type
, WebKitWebPolicyDecision
*decision
, gpointer user_data
) {
240 if (webkit_web_view_can_show_mime_type(webview
, mime_type
) == FALSE
) {
241 webkit_web_policy_decision_download(decision
);
248 static WebKitWebView
*
249 inspector_inspect_web_view_cb(gpointer inspector
, WebKitWebView
* web_view
) {
250 gchar
* inspector_title
;
251 GtkWidget
* inspector_window
;
252 GtkWidget
* inspector_view
;
254 /* just enough code to show the inspector - no signal handling etc. */
255 inspector_title
= g_strdup_printf("Inspect page - %s - Vimprobable2", webkit_web_view_get_uri(web_view
));
256 if (client
.state
.embed
) {
257 inspector_window
= gtk_plug_new(client
.state
.embed
);
259 inspector_window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
260 gtk_window_set_wmclass(client
.gui
.window
, "vimprobable2", "Vimprobable2");
262 gtk_window_set_title(GTK_WINDOW(inspector_window
), inspector_title
);
263 g_free(inspector_title
);
264 inspector_view
= webkit_web_view_new();
265 gtk_container_add(GTK_CONTAINER(inspector_window
), inspector_view
);
266 gtk_widget_show_all(inspector_window
);
267 return WEBKIT_WEB_VIEW(inspector_view
);
271 webview_download_cb(WebKitWebView
*webview
, WebKitDownload
*download
, gpointer user_data
) {
272 const gchar
*filename
;
275 WebKitDownloadStatus status
;
277 filename
= webkit_download_get_suggested_filename(download
);
278 if (filename
== NULL
|| strlen(filename
) == 0) {
279 filename
= "vimprobable_download";
281 path
= g_build_filename(g_strdup_printf(DOWNLOADS_PATH
), filename
, NULL
);
282 uri
= g_strconcat("file://", path
, NULL
);
283 webkit_download_set_destination_uri(download
, uri
);
286 size
= (uint32_t)webkit_download_get_total_size(download
);
288 echo_message(Info
, "Download %s started (expected size: %u bytes)...", filename
, size
);
290 echo_message(Info
, "Download %s started (unknown size)...", filename
);
291 client
.state
.activeDownloads
= g_list_prepend(client
.state
.activeDownloads
, download
);
292 g_signal_connect(download
, "notify::progress", G_CALLBACK(download_progress
), NULL
);
293 g_signal_connect(download
, "notify::status", G_CALLBACK(download_progress
), NULL
);
294 status
= webkit_download_get_status(download
);
295 if (status
== WEBKIT_DOWNLOAD_STATUS_CREATED
)
296 webkit_download_start(download
);
307 download_progress(WebKitDownload
*d
, GParamSpec
*pspec
) {
308 WebKitDownloadStatus status
= webkit_download_get_status(d
);
310 if (status
!= WEBKIT_DOWNLOAD_STATUS_STARTED
&& status
!= WEBKIT_DOWNLOAD_STATUS_CREATED
) {
311 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
) {
312 echo_message(Error
, "Error while downloading %s", webkit_download_get_suggested_filename(d
));
314 echo_message(Info
, "Download %s finished", webkit_download_get_suggested_filename(d
));
316 client
.state
.activeDownloads
= g_list_remove(client
.state
.activeDownloads
, d
);
323 process_keypress(GdkEventKey
*event
) {
324 State
*state
= &client
.state
;
327 GdkModifierType irrelevant
;
329 /* Get a mask of modifiers that shouldn't be considered for this event.
330 * E.g.: It shouldn't matter whether ';' is shifted or not. */
331 gdk_keymap_translate_keyboard_state(state
->keymap
, event
->hardware_keycode
,
332 event
->state
, event
->group
, &keyval
, NULL
, NULL
, &irrelevant
);
334 current
= client
.config
.keylistroot
;
336 while (current
!= NULL
) {
337 if (current
->Element
.mask
== (CLEAN(event
->state
) & ~irrelevant
)
338 && (current
->Element
.modkey
== state
->current_modkey
339 || (!current
->Element
.modkey
&& !state
->current_modkey
)
340 || current
->Element
.modkey
== GDK_VoidSymbol
) /* wildcard */
341 && current
->Element
.key
== keyval
342 && current
->Element
.func
)
343 if (current
->Element
.func(¤t
->Element
.arg
)) {
344 state
->current_modkey
= state
->count
= 0;
348 current
= current
->next
;
354 webview_keypress_cb(WebKitWebView
*webview
, GdkEventKey
*event
) {
355 State
*state
= &client
.state
;
356 Arg a
= { .i
= ModeNormal
, .s
= NULL
};
358 GdkModifierType irrelevant
;
360 /* Get a mask of modifiers that shouldn't be considered for this event.
361 * E.g.: It shouldn't matter whether ';' is shifted or not. */
362 gdk_keymap_translate_keyboard_state(state
->keymap
, event
->hardware_keycode
,
363 event
->state
, event
->group
, &keyval
, NULL
, NULL
, &irrelevant
);
365 switch (state
->mode
) {
367 if ((CLEAN(event
->state
) & ~irrelevant
) == 0) {
368 if (IS_ESCAPE(event
)) {
369 echo_message(Info
, "");
371 } else if (state
->current_modkey
== 0 && ((event
->keyval
>= GDK_1
&& event
->keyval
<= GDK_9
)
372 || (event
->keyval
== GDK_0
&& state
->count
))) {
373 state
->count
= (state
->count
? state
->count
* 10 : 0) + (event
->keyval
- GDK_0
);
376 } else if (strchr(client
.config
.modkeys
, event
->keyval
) && state
->current_modkey
!= event
->keyval
) {
377 state
->current_modkey
= event
->keyval
;
383 if (process_keypress(event
) == TRUE
) return TRUE
;
387 if (IS_ESCAPE(event
)) {
389 a
.s
= g_strdup("hints.clearFocus();");
394 } else if (CLEAN(event
->state
) & GDK_CONTROL_MASK
) {
395 /* keybindings of non-printable characters */
396 if (process_keypress(event
) == TRUE
) return TRUE
;
398 case ModePassThrough
:
399 if (IS_ESCAPE(event
)) {
400 echo_message(Info
, "");
406 echo_message(Info
, "");
414 set_widget_font_and_color(GtkWidget
*widget
, const char *font_str
, const char *bg_color_str
,
415 const char *fg_color_str
) {
418 PangoFontDescription
*font
;
420 font
= pango_font_description_from_string(font_str
);
421 gtk_widget_modify_font(widget
, font
);
422 pango_font_description_free(font
);
425 gdk_color_parse(fg_color_str
, &fg_color
);
427 gdk_color_parse(bg_color_str
, &bg_color
);
429 gtk_widget_modify_text(widget
, GTK_STATE_NORMAL
, fg_color_str
? &fg_color
: NULL
);
430 gtk_widget_modify_base(widget
, GTK_STATE_NORMAL
, bg_color_str
? &bg_color
: NULL
);
436 webview_hoverlink_cb(WebKitWebView
*webview
, char *title
, char *link
, gpointer data
) {
437 const char *uri
= webkit_web_view_get_uri(webview
);
440 memset(client
.state
.rememberedURI
, 0, BUF_SIZE
);
442 markup
= g_markup_printf_escaped("<span font=\"%s\">Link: %s</span>", statusfont
, link
);
443 gtk_label_set_markup(GTK_LABEL(client
.gui
.status_url
), markup
);
444 strncpy(client
.state
.rememberedURI
, link
, BUF_SIZE
);
451 webview_console_cb(WebKitWebView
*webview
, char *message
, int line
, char *source
, gpointer user_data
) {
454 /* Don't change internal mode if the browser doesn't have focus to prevent inconsistent states */
455 if (gtk_window_has_toplevel_focus(client
.gui
.window
)) {
456 if (!strcmp(message
, "hintmode_off") || !strcmp(message
, "insertmode_off")) {
459 } else if (!strcmp(message
, "insertmode_on")) {
468 inputbox_activate_cb(GtkEntry
*entry
, gpointer user_data
) {
469 Gui
*gui
= &client
.gui
;
470 State
*state
= &client
.state
;
472 guint16 length
= gtk_entry_get_text_length(entry
);
474 gboolean forward
= FALSE
;
476 a
.i
= HideCompletion
;
480 text
= (char*)gtk_entry_get_text(entry
);
482 /* move focus from inputbox to print potential messages that could not be
483 * printed as long as the inputbox is focused */
484 gtk_widget_grab_focus(GTK_WIDGET(gui
->webview
));
486 if (length
> 1 && text
[0] == ':') {
487 process_line((text
+ 1));
488 } else if (length
> 1 && ((forward
= text
[0] == '/') || text
[0] == '?')) {
489 webkit_web_view_unmark_text_matches(gui
->webview
);
490 #ifdef ENABLE_MATCH_HIGHLITING
491 webkit_web_view_mark_text_matches(gui
->webview
, &text
[1], FALSE
, 0);
492 webkit_web_view_set_highlight_text_matches(gui
->webview
, TRUE
);
495 #ifndef ENABLE_INCREMENTAL_SEARCH
497 a
.i
= searchoptions
| (forward
? DirectionForward
: DirectionBackwards
);
500 state
->search_direction
= forward
;
501 state
->search_handle
= g_strdup(&text
[1]);
503 } else if (text
[0] == '.' || text
[0] == ',' || text
[0] == ';') {
505 a
.s
= g_strdup_printf("hints.fire();");
511 gtk_widget_grab_focus(GTK_WIDGET(gui
->webview
));
515 inputbox_keypress_cb(GtkEntry
*entry
, GdkEventKey
*event
) {
518 State
*state
= &client
.state
;
520 if (state
->mode
== ModeHints
) {
521 if (event
->keyval
== GDK_Tab
) {
523 a
.s
= g_strdup_printf("hints.focusNextHint();");
529 if (event
->keyval
== GDK_ISO_Left_Tab
) {
531 a
.s
= g_strdup_printf("hints.focusPreviousHint();");
537 if (event
->keyval
== GDK_Return
) {
539 a
.s
= g_strdup_printf("hints.fire();");
546 switch (event
->keyval
) {
547 case GDK_bracketleft
:
549 if (!IS_ESCAPE(event
)) break;
550 a
.i
= HideCompletion
;
553 state
->commandpointer
= 0;
562 return commandhistoryfetch(&a
);
566 return commandhistoryfetch(&a
);
568 case GDK_ISO_Left_Tab
:
574 if (state
->mode
== ModeHints
) {
575 if ((CLEAN(event
->state
) & GDK_SHIFT_MASK
) &&
576 (CLEAN(event
->state
) & GDK_CONTROL_MASK
) &&
577 (event
->keyval
== GDK_BackSpace
)) {
580 a
.s
= g_strdup_printf("hints.updateHints(%d);", state
->count
);
587 numval
= g_unichar_digit_value((gunichar
) gdk_keyval_to_unicode(event
->keyval
));
588 if ((numval
>= 1 && numval
<= 9) || (numval
== 0 && state
->count
)) {
589 /* allow a zero as non-first number */
590 state
->count
= (state
->count
? state
->count
* 10 : 0) + numval
;
592 a
.s
= g_strdup_printf("hints.updateHints(%d);", state
->count
);
604 notify_event_cb(GtkWidget
*widget
, GdkEvent
*event
, gpointer user_data
) {
606 WebKitHitTestResult
*result
;
607 WebKitHitTestResultContext context
;
608 State
*state
= &client
.state
;
609 if (state
->mode
== ModeNormal
&& event
->type
== GDK_BUTTON_RELEASE
) {
610 /* handle mouse click events */
611 for (i
= 0; i
< LENGTH(mouse
); i
++) {
612 if (mouse
[i
].mask
== CLEAN(event
->button
.state
)
613 && (mouse
[i
].modkey
== state
->current_modkey
614 || (!mouse
[i
].modkey
&& !state
->current_modkey
)
615 || mouse
[i
].modkey
== GDK_VoidSymbol
) /* wildcard */
616 && mouse
[i
].button
== event
->button
.button
618 if (mouse
[i
].func(&mouse
[i
].arg
)) {
619 state
->current_modkey
= state
->count
= 0;
625 result
= webkit_web_view_get_hit_test_result(WEBKIT_WEB_VIEW(widget
), (GdkEventButton
*)event
);
626 g_object_get(result
, "context", &context
, NULL
);
627 if (context
& WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE
) {
628 Arg a
= { .i
= ModeInsert
};
630 state
->manual_focus
= TRUE
;
632 } else if (state
->mode
== ModeInsert
&& event
->type
== GDK_BUTTON_RELEASE
) {
633 result
= webkit_web_view_get_hit_test_result(WEBKIT_WEB_VIEW(widget
), (GdkEventButton
*)event
);
634 g_object_get(result
, "context", &context
, NULL
);
635 if (!(context
& WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE
)) {
636 Arg a
= { .i
= ModeNormal
};
640 gchar
*value
= NULL
, *message
= NULL
;
641 jsapi_evaluate_script("window.getSelection().focusNode", &value
, &message
);
642 if (value
&& !strcmp(value
, "[object HTMLFormElement]")) {
643 Arg a
= { .i
= ModeInsert
, .s
= NULL
};
645 state
->manual_focus
= TRUE
;
653 static gboolean
inputbox_keyrelease_cb(GtkEntry
*entry
, GdkEventKey
*event
) {
655 guint16 length
= gtk_entry_get_text_length(entry
);
658 a
.i
= HideCompletion
;
666 static gboolean
inputbox_changed_cb(GtkEditable
*entry
, gpointer user_data
) {
668 char *text
= (char*)gtk_entry_get_text(GTK_ENTRY(entry
));
669 guint16 length
= gtk_entry_get_text_length(GTK_ENTRY(entry
));
670 gboolean forward
= FALSE
;
672 /* Update incremental search if the user changes the search text.
674 * Note: gtk_widget_is_focus() is a poor way to check if the change comes
675 * from the user. But if the entry is focused and the text is set
676 * through gtk_entry_set_text() in some asyncrounous operation,
677 * I would consider that a bug.
680 if (gtk_widget_is_focus(GTK_WIDGET(entry
)) && length
> 1 && ((forward
= text
[0] == '/') || text
[0] == '?')) {
681 webkit_web_view_unmark_text_matches(client
.gui
.webview
);
682 webkit_web_view_search_text(client
.gui
.webview
, &text
[1], searchoptions
& CaseSensitive
, forward
, searchoptions
& Wrapping
);
684 } else if (gtk_widget_is_focus(GTK_WIDGET(entry
)) && length
>= 1 &&
685 (text
[0] == '.' || text
[0] == ',' || text
[0] == ';')) {
689 a
.s
= g_strconcat("hints.createHints('", text
+ 1, "', 'f');", NULL
);
693 a
.s
= g_strconcat("hints.createHints('", text
+ 1, "', 'F');", NULL
);
700 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 's');", NULL
);
703 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'y');", NULL
);
706 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'f');", NULL
);
709 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'F');", NULL
);
711 case 'O': case 'T': case 'W':
712 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'O');", NULL
);
715 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'i');", NULL
);
718 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'I');", NULL
);
723 client
.state
.count
= 0;
730 } else if (length
== 0) {
731 client
.state
.mode
= ModeNormal
;
733 a
.s
= g_strdup("hints.clearHints();");
736 client
.state
.count
= 0;
745 void fill_suggline(char * suggline
, const char * command
, const char *fill_with
) {
746 memset(suggline
, 0, 512);
747 strncpy(suggline
, command
, 512);
748 strncat(suggline
, " ", 1);
749 strncat(suggline
, fill_with
, 512 - strlen(suggline
) - 1);
752 GtkWidget
* fill_eventbox(const char * completion_line
) {
754 GtkWidget
*row_eventbox
, *el
;
756 char *markup
, *markup_tmp
;
758 row
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
759 row_eventbox
= gtk_event_box_new();
760 gdk_color_parse(completionbgcolor
[0], &color
);
761 gtk_widget_modify_bg(row_eventbox
, GTK_STATE_NORMAL
, &color
);
762 el
= gtk_label_new(NULL
);
763 markup_tmp
= g_markup_escape_text(completion_line
, strlen(completion_line
));
764 markup
= g_strconcat("<span font=\"", completionfont
[0], "\" color=\"", completioncolor
[0], "\">",
765 markup_tmp
, "</span>", NULL
);
766 gtk_label_set_markup(GTK_LABEL(el
), markup
);
769 gtk_misc_set_alignment(GTK_MISC(el
), 0, 0);
770 gtk_box_pack_start(row
, el
, TRUE
, TRUE
, 2);
771 gtk_container_add(GTK_CONTAINER(row_eventbox
), GTK_WIDGET(row
));
776 complete(const Arg
*arg
) {
777 char *str
, *p
, *s
, *markup
, *entry
, *searchfor
, command
[32] = "", suggline
[512] = "", **suggurls
;
778 size_t listlen
, len
, cmdlen
;
780 Listelement
*elementlist
= NULL
, *elementpointer
;
781 gboolean highlight
= FALSE
;
783 GtkWidget
*row_eventbox
, *el
;
786 static GtkWidget
*table
, *top_border
;
788 static char **suggestions
;
789 static GtkWidget
**widgets
;
790 static int n
= 0, m
, current
= -1;
791 Gui
*gui
= &client
.gui
;
793 str
= (char*)gtk_entry_get_text(GTK_ENTRY(gui
->inputbox
));
796 /* Get the length of the list of commands for completion. We need this to
797 * malloc/realloc correctly.
799 listlen
= LENGTH(commands
);
801 if ((len
== 0 || str
[0] != ':') && arg
->i
!= HideCompletion
)
804 if (arg
->i
!= HideCompletion
&& widgets
&& current
!= -1 && !strcmp(&str
[1], suggestions
[current
])) {
805 gdk_color_parse(completionbgcolor
[0], &color
);
806 gtk_widget_modify_bg(widgets
[current
], GTK_STATE_NORMAL
, &color
);
807 current
= (n
+ current
+ (arg
->i
== DirectionPrev
? -1 : 1)) % n
;
808 if ((arg
->i
== DirectionNext
&& current
== 0)
809 || (arg
->i
== DirectionPrev
&& current
== n
- 1))
815 gtk_widget_destroy(GTK_WIDGET(table
));
816 gtk_widget_destroy(GTK_WIDGET(top_border
));
823 if (arg
->i
== HideCompletion
)
826 } else if (arg
->i
== HideCompletion
)
829 prefix
= g_strdup(str
);
830 widgets
= malloc(sizeof(GtkWidget
*) * listlen
);
831 suggestions
= malloc(sizeof(char*) * listlen
);
832 top_border
= gtk_event_box_new();
833 gtk_widget_set_size_request(GTK_WIDGET(top_border
), 0, 1);
834 gdk_color_parse(completioncolor
[2], &color
);
835 gtk_widget_modify_bg(top_border
, GTK_STATE_NORMAL
, &color
);
836 table
= gtk_event_box_new();
837 gdk_color_parse(completionbgcolor
[0], &color
);
838 _table
= GTK_BOX(gtk_vbox_new(FALSE
, 0));
840 if (strchr(str
, ' ') == NULL
) {
841 /* command completion */
842 listlen
= LENGTH(commands
);
843 for (i
= 0; i
< listlen
; i
++) {
844 if (commands
[i
].cmd
== NULL
)
846 cmdlen
= strlen(commands
[i
].cmd
);
847 if (!highlight
|| (n
< MAX_LIST_SIZE
&& len
- 1 <= cmdlen
&& !strncmp(&str
[1], commands
[i
].cmd
, len
- 1))) {
848 p
= s
= malloc(sizeof(char*) * (highlight
? sizeof(COMPLETION_TAG_OPEN
) + sizeof(COMPLETION_TAG_CLOSE
) - 1 : 1) + cmdlen
);
850 memcpy(p
, COMPLETION_TAG_OPEN
, sizeof(COMPLETION_TAG_OPEN
) - 1);
851 memcpy((p
+= sizeof(COMPLETION_TAG_OPEN
) - 1), &str
[1], len
- 1);
852 memcpy((p
+= len
- 1), COMPLETION_TAG_CLOSE
, sizeof(COMPLETION_TAG_CLOSE
) - 1);
853 p
+= sizeof(COMPLETION_TAG_CLOSE
) - 1;
855 memcpy(p
, &commands
[i
].cmd
[len
- 1], cmdlen
- len
+ 2);
856 row
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
857 row_eventbox
= gtk_event_box_new();
858 gtk_widget_modify_bg(row_eventbox
, GTK_STATE_NORMAL
, &color
);
859 el
= gtk_label_new(NULL
);
860 markup
= g_strconcat("<span font=\"", completionfont
[0], "\" color=\"", completioncolor
[0], "\">", s
, "</span>", NULL
);
862 gtk_label_set_markup(GTK_LABEL(el
), markup
);
864 gtk_misc_set_alignment(GTK_MISC(el
), 0, 0);
865 gtk_box_pack_start(row
, el
, TRUE
, TRUE
, 2);
866 gtk_container_add(GTK_CONTAINER(row_eventbox
), GTK_WIDGET(row
));
867 gtk_box_pack_start(_table
, GTK_WIDGET(row_eventbox
), FALSE
, FALSE
, 0);
868 suggestions
[n
] = commands
[i
].cmd
;
869 widgets
[n
++] = row_eventbox
;
873 entry
= (char *)malloc(512 * sizeof(char));
877 memset(entry
, 0, 512);
878 suggurls
= malloc(sizeof(char*) * listlen
);
879 if (suggurls
== NULL
) {
882 spacepos
= strcspn(str
, " ");
883 searchfor
= (str
+ spacepos
+ 1);
884 strncpy(command
, (str
+ 1), spacepos
- 1);
885 if (strlen(command
) == 3 && strncmp(command
, "set", 3) == 0) {
886 /* browser settings */
887 listlen
= LENGTH(browsersettings
);
888 for (i
= 0; i
< listlen
; i
++) {
889 if (n
< MAX_LIST_SIZE
&& strstr(browsersettings
[i
].name
, searchfor
) != NULL
) {
891 fill_suggline(suggline
, command
, browsersettings
[i
].name
);
892 /* FIXME(HP): This memory is never freed */
893 suggurls
[n
] = (char *)malloc(sizeof(char) * 512 + 1);
894 strncpy(suggurls
[n
], suggline
, 512);
895 suggestions
[n
] = suggurls
[n
];
896 row_eventbox
= fill_eventbox(suggline
);
897 gtk_box_pack_start(_table
, GTK_WIDGET(row_eventbox
), FALSE
, FALSE
, 0);
898 widgets
[n
++] = row_eventbox
;
902 } else if (strlen(command
) == 2 && strncmp(command
, "qt", 2) == 0) {
903 /* completion on tags */
904 spacepos
= strcspn(str
, " ");
905 searchfor
= (str
+ spacepos
+ 1);
906 elementlist
= complete_list(searchfor
, 1, elementlist
);
908 /* URL completion: bookmarks */
909 elementlist
= complete_list(searchfor
, 0, elementlist
);
910 m
= count_list(elementlist
);
911 if (m
< MAX_LIST_SIZE
) {
912 /* URL completion: history */
913 elementlist
= complete_list(searchfor
, 2, elementlist
);
916 elementpointer
= elementlist
;
917 while (elementpointer
!= NULL
) {
918 fill_suggline(suggline
, command
, elementpointer
->element
);
919 /* FIXME(HP): This memory is never freed */
920 suggurls
[n
] = (char *)malloc(sizeof(char) * 512 + 1);
921 strncpy(suggurls
[n
], suggline
, 512);
922 suggestions
[n
] = suggurls
[n
];
923 row_eventbox
= fill_eventbox(suggline
);
924 gtk_box_pack_start(_table
, GTK_WIDGET(row_eventbox
), FALSE
, FALSE
, 0);
925 widgets
[n
++] = row_eventbox
;
926 elementpointer
= elementpointer
->next
;
927 if (n
>= MAX_LIST_SIZE
)
930 free_list(elementlist
);
931 if (suggurls
!= NULL
) {
940 /* TA: FIXME - this needs rethinking entirely. */
942 GtkWidget
**widgets_temp
= realloc(widgets
, sizeof(*widgets
) * n
);
943 if (widgets_temp
== NULL
&& widgets
== NULL
) {
944 fprintf(stderr
, "Couldn't realloc() widgets\n");
947 widgets
= widgets_temp
;
948 char **suggestions_temp
= realloc(suggestions
, sizeof(*suggestions
) * n
);
949 if (suggestions_temp
== NULL
&& suggestions
== NULL
) {
950 fprintf(stderr
, "Couldn't realloc() suggestions\n");
953 suggestions
= suggestions_temp
;
956 gdk_color_parse(completionbgcolor
[1], &color
);
957 gtk_widget_modify_bg(table
, GTK_STATE_NORMAL
, &color
);
958 el
= gtk_label_new(NULL
);
959 gtk_misc_set_alignment(GTK_MISC(el
), 0, 0);
960 markup
= g_strconcat("<span font=\"", completionfont
[1], "\" color=\"", completioncolor
[1], "\">No Completions</span>", NULL
);
961 gtk_label_set_markup(GTK_LABEL(el
), markup
);
963 gtk_box_pack_start(_table
, GTK_WIDGET(el
), FALSE
, FALSE
, 0);
965 gtk_box_pack_start(gui
->box
, GTK_WIDGET(top_border
), FALSE
, FALSE
, 0);
966 gtk_container_add(GTK_CONTAINER(table
), GTK_WIDGET(_table
));
967 gtk_box_pack_start(gui
->box
, GTK_WIDGET(table
), FALSE
, FALSE
, 0);
968 gtk_widget_show_all(GTK_WIDGET(gui
->window
));
971 current
= arg
->i
== DirectionPrev
? n
- 1 : 0;
974 gdk_color_parse(completionbgcolor
[2], &color
);
975 gtk_widget_modify_bg(GTK_WIDGET(widgets
[current
]), GTK_STATE_NORMAL
, &color
);
976 s
= g_strconcat(":", suggestions
[current
], NULL
);
977 gtk_entry_set_text(GTK_ENTRY(gui
->inputbox
), s
);
980 gtk_entry_set_text(GTK_ENTRY(gui
->inputbox
), prefix
);
981 gtk_editable_set_position(GTK_EDITABLE(gui
->inputbox
), -1);
986 descend(const Arg
*arg
) {
987 char *source
= (char*)webkit_web_view_get_uri(client
.gui
.webview
), *p
= &source
[0], *new;
989 client
.state
.count
= client
.state
.count
? client
.state
.count
: 1;
993 if (arg
->i
== Rootdir
) {
994 for (i
= 0; i
< 3; i
++) /* get to the third slash */
995 if (!(p
= strchr(++p
, '/')))
996 return TRUE
; /* if we cannot find it quit */
998 len
= strlen(source
);
999 if (!len
) /* if string is empty quit */
1001 p
= source
+ len
; /* start at the end */
1002 if (*(p
- 1) == '/') /* /\/$/ is not an additional level */
1003 ++client
.state
.count
;
1004 for (i
= 0; i
< client
.state
.count
; i
++)
1005 while(*(p
--) != '/' || *p
== '/') /* count /\/+/ as one slash */
1006 if (p
== source
) /* if we reach the first char pointer quit */
1008 ++p
; /* since we do p-- in the while, we are pointing at
1009 the char before the slash, so +1 */
1011 len
= p
- source
+ 1; /* new length = end - start + 1 */
1012 new = malloc(len
+ 1);
1013 memcpy(new, source
, len
);
1015 webkit_web_view_load_uri(client
.gui
.webview
, new);
1021 echo(const Arg
*arg
) {
1022 int index
= !arg
->s
? 0 : arg
->i
& (~NoAutoHide
);
1024 if (index
< Info
|| index
> Error
)
1027 if (!gtk_widget_is_focus(GTK_WIDGET(client
.gui
.inputbox
))) {
1028 set_widget_font_and_color(client
.gui
.inputbox
, urlboxfont
[index
], urlboxbgcolor
[index
], urlboxcolor
[index
]);
1029 gtk_entry_set_text(GTK_ENTRY(client
.gui
.inputbox
), !arg
->s
? "" : arg
->s
);
1036 open_inspector(const Arg
* arg
) {
1037 gboolean inspect_enabled
;
1038 WebKitWebSettings
*settings
;
1040 settings
= webkit_web_view_get_settings(client
.gui
.webview
);
1041 g_object_get(G_OBJECT(settings
), "enable-developer-extras", &inspect_enabled
, NULL
);
1042 if (inspect_enabled
) {
1043 webkit_web_inspector_show(client
.gui
.inspector
);
1046 echo_message(Error
, "Webinspector is not enabled");
1052 input(const Arg
*arg
) {
1054 client
.state
.count
= 0;
1058 GtkWidget
*inputbox
= client
.gui
.inputbox
;
1060 /* if inputbox hidden, show it again */
1061 if (!gtk_widget_get_visible(inputbox
))
1062 gtk_widget_set_visible(inputbox
, TRUE
);
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,
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(client
.gui
.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 client
.state
.mode
= ModeHints
;
1084 switch (arg
->s
[0]) {
1086 a
.s
= g_strdup("hints.createHints('', 'f');");
1090 a
.s
= g_strdup("hints.createHints('', 'F');");
1096 switch (arg
->s
[1]) {
1098 a
.s
= g_strdup("hints.createHints('', 's');");
1101 a
.s
= g_strdup("hints.createHints('', 'y');");
1104 a
.s
= g_strdup("hints.createHints('', 'f');");
1107 a
.s
= g_strdup("hints.createHints('', 'F');");
1109 case 'O': case 'T': case 'W':
1110 a
.s
= g_strdup("hints.createHints('', 'O');");
1113 a
.s
= g_strdup("hints.createHints('', 'i');");
1116 a
.s
= g_strdup("hints.createHints('', 'I');");
1122 client
.state
.count
= 0;
1133 navigate(const Arg
*arg
) {
1134 if (arg
->i
& NavigationForwardBack
)
1135 webkit_web_view_go_back_or_forward(client
.gui
.webview
, (arg
->i
== NavigationBack
? -1 : 1) * (client
.state
.count
? client
.state
.count
: 1));
1136 else if (arg
->i
& NavigationReloadActions
)
1137 (arg
->i
== NavigationReload
? webkit_web_view_reload
: webkit_web_view_reload_bypass_cache
)(client
.gui
.webview
);
1139 webkit_web_view_stop_loading(client
.gui
.webview
);
1144 number(const Arg
*arg
) {
1145 const char *source
= webkit_web_view_get_uri(client
.gui
.webview
);
1146 char *uri
, *p
, *new;
1147 int number
, diff
= (client
.state
.count
? client
.state
.count
: 1) * (arg
->i
== Increment
? 1 : -1);
1151 uri
= g_strdup(source
); /* copy string */
1153 while(*p
!= '\0') /* goto the end of the string */
1156 while(*p
>= '0' && *p
<= '9') /* go back until non number char is reached */
1158 if (*(++p
) == '\0') { /* if no numbers were found abort */
1162 number
= atoi(p
) + diff
; /* apply diff on number */
1164 new = g_strdup_printf("%s%d", uri
, number
); /* create new uri */
1165 webkit_web_view_load_uri(client
.gui
.webview
, new);
1172 open_arg(const Arg
*arg
) {
1174 char *s
= arg
->s
, *p
= NULL
, *new;
1175 Arg a
= { .i
= NavigationReload
};
1177 char *search_uri
, *search_term
;
1179 if (client
.state
.embed
) {
1181 snprintf(winid
, LENGTH(winid
), "%u", (gint
)client
.state
.embed
);
1195 else if (arg
->i
== TargetCurrent
) {
1196 while(*s
== ' ') /* strip leading whitespace */
1198 p
= (s
+ strlen(s
) - 1);
1199 while(*p
== ' ') /* strip trailing whitespace */
1204 /* check for external handlers */
1205 if (open_handler(s
))
1207 /* check for search engines */
1209 if (p
) { /* check for search engines */
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
);
1220 if (len
> 3 && strstr(s
, "://")) { /* valid url? */
1221 p
= new = g_malloc(len
+ 1);
1222 while(*s
!= '\0') { /* strip whitespaces */
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(client
.gui
.webview
, new);
1248 g_spawn_async(NULL
, argv
, NULL
, G_SPAWN_SEARCH_PATH
, NULL
, NULL
, NULL
, NULL
);
1253 open_remembered(const Arg
*arg
)
1255 Arg a
= {arg
->i
, client
.state
.rememberedURI
};
1257 if (strcmp(client
.state
.rememberedURI
, "")) {
1264 yank(const Arg
*arg
) {
1265 const char *url
, *content
;
1267 if (arg
->i
& SourceSelection
) {
1268 webkit_web_view_copy_clipboard(client
.gui
.webview
);
1269 if (arg
->i
& ClipboardPrimary
)
1270 content
= gtk_clipboard_wait_for_text(client
.state
.clipboards
[0]);
1271 if (!content
&& arg
->i
& ClipboardGTK
)
1272 content
= gtk_clipboard_wait_for_text(client
.state
.clipboards
[1]);
1274 echo_message(Info
, "Yanked %s", content
);
1275 g_free((gpointer
*)content
);
1278 if (arg
->i
& SourceURL
) {
1279 url
= webkit_web_view_get_uri(client
.gui
.webview
);
1286 echo_message(Info
, "Yanked %s", url
);
1287 if (arg
->i
& ClipboardPrimary
)
1288 gtk_clipboard_set_text(client
.state
.clipboards
[0], url
, -1);
1289 if (arg
->i
& ClipboardGTK
)
1290 gtk_clipboard_set_text(client
.state
.clipboards
[1], url
, -1);
1296 paste(const Arg
*arg
) {
1297 Arg a
= { .i
= arg
->i
& TargetNew
, .s
= NULL
};
1299 /* If we're over a link, open it in a new target. */
1300 if (strlen(client
.state
.rememberedURI
) > 0) {
1301 Arg new_target
= { .i
= TargetNew
, .s
= arg
->s
};
1302 open_arg(&new_target
);
1306 if (arg
->i
& ClipboardPrimary
)
1307 a
.s
= gtk_clipboard_wait_for_text(client
.state
.clipboards
[0]);
1308 if (!a
.s
&& arg
->i
& ClipboardGTK
)
1309 a
.s
= gtk_clipboard_wait_for_text(client
.state
.clipboards
[1]);
1318 quit(const Arg
*arg
) {
1320 const char *filename
;
1321 const char *uri
= webkit_web_view_get_uri(client
.gui
.webview
);
1323 /* write last URL into status file for recreation with "u" */
1324 filename
= g_strdup_printf("%s", client
.config
.config_base
);
1325 filename
= g_strdup_printf(CLOSED_URL_FILENAME
);
1326 f
= fopen(filename
, "w");
1327 g_free((gpointer
*)filename
);
1329 fprintf(f
, "%s", uri
);
1338 revive(const Arg
*arg
) {
1340 const char *filename
;
1341 char buffer
[512] = "";
1342 Arg a
= { .i
= TargetNew
, .s
= NULL
};
1343 /* get the URL of the window which has been closed last */
1344 filename
= g_strdup_printf(CLOSED_URL_FILENAME
);
1345 f
= fopen(filename
, "r");
1346 g_free((gpointer
*)filename
);
1348 fgets(buffer
, 512, f
);
1351 if (strlen(buffer
) > 0) {
1360 gboolean
print_frame(const Arg
*arg
)
1362 WebKitWebFrame
*frame
= webkit_web_view_get_main_frame(client
.gui
.webview
);
1363 webkit_web_frame_print (frame
);
1368 search(const Arg
*arg
) {
1369 State
*state
= &client
.state
;
1370 state
->count
= state
->count
? state
->count
: 1;
1371 gboolean success
, direction
= arg
->i
& DirectionPrev
;
1374 free(state
->search_handle
);
1375 state
->search_handle
= g_strdup(arg
->s
);
1377 if (!state
->search_handle
)
1379 if (arg
->i
& DirectionAbsolute
)
1380 state
->search_direction
= direction
;
1382 direction
^= state
->search_direction
;
1384 success
= webkit_web_view_search_text(client
.gui
.webview
, state
->search_handle
, arg
->i
& CaseSensitive
, direction
, FALSE
);
1386 if (arg
->i
& Wrapping
) {
1387 success
= webkit_web_view_search_text(client
.gui
.webview
, state
->search_handle
, arg
->i
& CaseSensitive
, direction
, TRUE
);
1389 echo_message(Warning
, "search hit %s, continuing at %s",
1390 direction
? "BOTTOM" : "TOP",
1391 direction
? "TOP" : "BOTTOM");
1397 } while(--state
->count
);
1399 echo_message(Error
, "Pattern not found: %s", state
->search_handle
);
1405 set(const Arg
*arg
) {
1408 if (client
.state
.search_handle
) {
1409 client
.state
.search_handle
= NULL
;
1410 webkit_web_view_unmark_text_matches(client
.gui
.webview
);
1412 gtk_entry_set_text(GTK_ENTRY(client
.gui
.inputbox
), "");
1413 gtk_widget_grab_focus(GTK_WIDGET(client
.gui
.webview
));
1415 case ModePassThrough
:
1416 echo_message(Info
| NoAutoHide
, "-- PASS THROUGH --");
1419 echo_message(Info
| NoAutoHide
, "-- PASS TROUGH (next) --");
1421 case ModeInsert
: /* should not be called manually but automatically */
1422 echo_message(Info
| NoAutoHide
, "-- INSERT --");
1427 client
.state
.mode
= arg
->i
;
1432 jsapi_ref_to_string(JSContextRef context
, JSValueRef ref
) {
1433 JSStringRef string_ref
;
1437 string_ref
= JSValueToStringCopy(context
, ref
, NULL
);
1438 length
= JSStringGetMaximumUTF8CStringSize(string_ref
);
1439 string
= g_new(gchar
, length
);
1440 JSStringGetUTF8CString(string_ref
, string
, length
);
1441 JSStringRelease(string_ref
);
1446 jsapi_evaluate_script(const gchar
*script
, gchar
**value
, gchar
**message
) {
1447 WebKitWebFrame
*frame
= webkit_web_view_get_main_frame(client
.gui
.webview
);
1448 JSGlobalContextRef context
= webkit_web_frame_get_global_context(frame
);
1450 JSValueRef val
, exception
;
1452 str
= JSStringCreateWithUTF8CString(script
);
1453 val
= JSEvaluateScript(context
, str
, JSContextGetGlobalObject(context
), NULL
, 0, &exception
);
1454 JSStringRelease(str
);
1456 *message
= jsapi_ref_to_string(context
, exception
);
1458 *value
= jsapi_ref_to_string(context
, val
);
1462 quickmark(const Arg
*a
) {
1465 char *fn
= g_strdup_printf(QUICKMARK_FILE
);
1467 fp
= fopen(fn
, "r");
1472 if (fp
!= NULL
&& b
< 10) {
1473 for( i
=0; i
< b
; ++i
) {
1477 fgets(buf
, 100, fp
);
1479 char *ptr
= strrchr(buf
, '\n');
1482 Arg x
= { .s
= buf
};
1483 return open_arg(&x
);
1485 echo_message(Error
, "Quickmark %d not defined", b
);
1488 } else { return false; }
1492 script(const Arg
*arg
) {
1493 gchar
*value
= NULL
, *message
= NULL
;
1494 char text
[BUF_SIZE
] = "";
1496 WebKitNetworkRequest
*request
;
1497 WebKitDownload
*download
;
1500 set_error("Missing argument.");
1503 jsapi_evaluate_script(arg
->s
, &value
, &message
);
1511 if (arg
->i
!= Silent
&& value
) {
1512 echo_message(arg
->i
, value
);
1514 /* switch mode according to scripts return value */
1516 if (strncmp(value
, "done;", 5) == 0) {
1519 } else if (strncmp(value
, "insert;", 7) == 0) {
1522 client
.state
.manual_focus
= TRUE
;
1523 } else if (strncmp(value
, "save;", 5) == 0) {
1524 /* forced download */
1527 request
= webkit_network_request_new((value
+ 5));
1528 download
= webkit_download_new(request
);
1529 webview_download_cb(client
.gui
.webview
, download
, (gpointer
*)NULL
);
1530 } else if (strncmp(value
, "yank;", 5) == 0) {
1531 /* yank link URL to clipboard */
1534 a
.i
= ClipboardPrimary
| ClipboardGTK
;
1537 } else if (strncmp(value
, "colon;", 6) == 0) {
1538 /* use link URL for colon command */
1539 strncpy(text
, (char *)gtk_entry_get_text(GTK_ENTRY(client
.gui
.inputbox
)), 1023);
1544 a
.s
= g_strconcat(":open ", (value
+ 6), NULL
);
1547 a
.s
= g_strconcat(":tabopen ", (value
+ 6), NULL
);
1554 } else if (strncmp(value
, "error;", 6) == 0) {
1564 scroll(const Arg
*arg
) {
1565 GtkAdjustment
*adjust
= (arg
->i
& OrientationHoriz
) ? client
.gui
.adjust_h
: client
.gui
.adjust_v
;
1566 int max
= gtk_adjustment_get_upper(adjust
) - gtk_adjustment_get_page_size(adjust
);
1567 float val
= gtk_adjustment_get_value(adjust
) / max
* 100;
1568 int direction
= (arg
->i
& (1 << 2)) ? 1 : -1;
1569 unsigned int count
= client
.state
.count
;
1571 if ((direction
== 1 && val
< 100) || (direction
== -1 && val
> 0)) {
1572 if (arg
->i
& ScrollMove
)
1573 gtk_adjustment_set_value(adjust
, gtk_adjustment_get_value(adjust
) +
1574 direction
* /* direction */
1575 ((arg
->i
& UnitLine
|| (arg
->i
& UnitBuffer
&& count
)) ? (scrollstep
* (count
? count
: 1)) : (
1576 arg
->i
& UnitBuffer
? gtk_adjustment_get_page_size(adjust
) / 2 :
1577 (count
? count
: 1) * (gtk_adjustment_get_page_size(adjust
) -
1578 (gtk_adjustment_get_page_size(adjust
) > pagingkeep
? pagingkeep
: 0)))));
1580 gtk_adjustment_set_value(adjust
,
1581 ((direction
== 1) ? gtk_adjustment_get_upper
: gtk_adjustment_get_lower
)(adjust
));
1588 zoom(const Arg
*arg
) {
1589 unsigned int count
= client
.state
.count
;
1591 webkit_web_view_set_full_content_zoom(client
.gui
.webview
, (arg
->i
& ZoomFullContent
) > 0);
1592 webkit_web_view_set_zoom_level(client
.gui
.webview
, (arg
->i
& ZoomOut
) ?
1593 webkit_web_view_get_zoom_level(client
.gui
.webview
) +
1594 (((float)(count
? count
: 1)) * (arg
->i
& (1 << 1) ? 1.0 : -1.0) * client
.config
.zoomstep
) :
1595 (count
? (float)count
/ 100.0 : 1.0));
1600 fake_key_event(const Arg
*a
) {
1601 if(!client
.state
.embed
) {
1605 if ( (xdpy
= XOpenDisplay(NULL
)) == NULL
) {
1606 echo_message(Error
, "Couldn't find the XDisplay.");
1612 xk
.subwindow
= None
;
1613 xk
.time
= CurrentTime
;
1614 xk
.same_screen
= True
;
1615 xk
.x
= xk
.y
= xk
.x_root
= xk
.y_root
= 1;
1616 xk
.window
= client
.state
.embed
;
1620 echo_message(Error
, "Zero pointer as argument! Check your config.h");
1625 if( (keysym
= XStringToKeysym(a
->s
)) == NoSymbol
) {
1626 echo_message(Error
, "Couldn't translate %s to keysym", a
->s
);
1630 if( (xk
.keycode
= XKeysymToKeycode(xdpy
, keysym
)) == NoSymbol
) {
1631 echo_message(Error
, "Couldn't translate keysym to keycode");
1636 if( !XSendEvent(xdpy
, client
.state
.embed
, True
, KeyPressMask
, (XEvent
*)&xk
) ) {
1637 echo_message(Error
, "XSendEvent failed");
1646 commandhistoryfetch(const Arg
*arg
) {
1647 State
*state
= &client
.state
;
1648 const int length
= g_list_length(client
.state
.commandhistory
);
1649 gchar
*input_message
= NULL
;
1652 if (arg
->i
== DirectionPrev
) {
1653 state
->commandpointer
= (length
+ state
->commandpointer
- 1) % length
;
1655 state
->commandpointer
= (length
+ state
->commandpointer
+ 1) % length
;
1658 const char* command
= (char *)g_list_nth_data(state
->commandhistory
, state
->commandpointer
);
1659 input_message
= g_strconcat(":", command
, NULL
);
1660 gtk_entry_set_text(GTK_ENTRY(client
.gui
.inputbox
), input_message
);
1661 g_free(input_message
);
1662 gtk_editable_set_position(GTK_EDITABLE(client
.gui
.inputbox
), -1);
1670 bookmark(const Arg
*arg
) {
1672 const char *filename
;
1673 const char *uri
= webkit_web_view_get_uri(client
.gui
.webview
);
1674 const char *title
= webkit_web_view_get_title(client
.gui
.webview
);
1675 filename
= g_strdup_printf(BOOKMARKS_STORAGE_FILENAME
);
1676 f
= fopen(filename
, "a");
1677 g_free((gpointer
*)filename
);
1678 if (uri
== NULL
|| strlen(uri
) == 0) {
1679 set_error("No URI found to bookmark.");
1683 fprintf(f
, "%s", uri
);
1684 if (title
!= NULL
) {
1685 fprintf(f
, "%s", " ");
1686 fprintf(f
, "%s", title
);
1688 if (arg
->s
&& strlen(arg
->s
)) {
1689 build_taglist(arg
, f
);
1691 fprintf(f
, "%s", "\n");
1693 echo_message(Info
, "Bookmark saved");
1696 set_error("Bookmarks file not found.");
1704 const char *filename
;
1705 const char *uri
= webkit_web_view_get_uri(client
.gui
.webview
);
1706 const char *title
= webkit_web_view_get_title(client
.gui
.webview
);
1707 char *entry
, buffer
[512], *new;
1709 gboolean finished
= FALSE
;
1711 if (title
!= NULL
) {
1712 entry
= malloc((strlen(uri
) + strlen(title
) + 2) * sizeof(char));
1713 memset(entry
, 0, strlen(uri
) + strlen(title
) + 2);
1715 entry
= malloc((strlen(uri
) + 1) * sizeof(char));
1716 memset(entry
, 0, strlen(uri
) + 1);
1718 if (entry
!= NULL
) {
1719 strncpy(entry
, uri
, strlen(uri
));
1720 if (title
!= NULL
) {
1721 strncat(entry
, " ", 1);
1722 strncat(entry
, title
, strlen(title
));
1725 filename
= g_strdup_printf(HISTORY_STORAGE_FILENAME
);
1726 f
= fopen(filename
, "r");
1728 new = (char *)malloc(HISTORY_MAX_ENTRIES
* 512 * sizeof(char) + 1);
1730 memset(new, 0, HISTORY_MAX_ENTRIES
* 512 * sizeof(char) + 1);
1731 /* newest entries go on top */
1732 strncpy(new, entry
, strlen(entry
));
1733 strncat(new, "\n", 1);
1734 /* retain at most HISTORY_MAX_ENTIRES - 1 old entries */
1735 while (finished
!= TRUE
) {
1736 if ((char *)NULL
== fgets(buffer
, 512, f
)) {
1737 /* check if end of file was reached / error occured */
1741 /* end of file reached */
1745 /* compare line (-1 because of newline character) */
1746 if (n
!= strlen(buffer
) - 1 || strncmp(entry
, buffer
, n
) != 0) {
1747 /* if the URI is already in history; we put it on top and skip it here */
1748 strncat(new, buffer
, 512);
1751 if ((i
+ 1) >= HISTORY_MAX_ENTRIES
) {
1757 f
= fopen(filename
, "w");
1758 g_free((gpointer
*)filename
);
1760 fprintf(f
, "%s", new);
1769 if (entry
!= NULL
) {
1778 view_source(const Arg
* arg
) {
1779 gboolean current_mode
= webkit_web_view_get_view_source_mode(client
.gui
.webview
);
1780 webkit_web_view_set_view_source_mode(client
.gui
.webview
, !current_mode
);
1781 webkit_web_view_reload(client
.gui
.webview
);
1785 /* open an external editor defined by the protocol handler for
1786 vimprobableedit on a text box or similar */
1788 open_editor(const Arg
*arg
) {
1792 gchar
*value
= NULL
, *message
= NULL
, *tag
= NULL
, *edit_url
= NULL
;
1793 gchar
*temp_file_name
= g_strdup_printf("%s/vimprobableeditXXXXXX",
1795 int temp_file_handle
= -1;
1797 /* check if active element is suitable for text editing */
1798 jsapi_evaluate_script("document.activeElement.tagName", &value
, &message
);
1799 if (value
== NULL
) {
1803 tag
= g_strdup(value
);
1804 if (strcmp(tag
, "INPUT") == 0) {
1805 /* extra check: type == text */
1806 jsapi_evaluate_script("document.activeElement.type", &value
, &message
);
1807 if (strcmp(value
, "text") != 0) {
1814 } else if (strcmp(tag
, "TEXTAREA") != 0) {
1819 jsapi_evaluate_script("document.activeElement.value", &value
, &message
);
1820 text
= g_strdup(value
);
1827 /* write text into temporary file */
1828 temp_file_handle
= mkstemp(temp_file_name
);
1829 if (temp_file_handle
== -1) {
1830 message
= g_strdup_printf("Could not create temporary file: %s",
1832 echo_message(Error
, message
);
1838 if (write(temp_file_handle
, text
, strlen(text
)) != strlen(text
)) {
1839 message
= g_strdup_printf("Short write to temporary file: %s",
1841 echo_message(Error
, message
);
1847 close(temp_file_handle
);
1851 edit_url
= g_strdup_printf("vimprobableedit:%s", temp_file_name
);
1852 success
= open_handler_pid(edit_url
, &child_pid
);
1855 echo_message(Error
, "External editor open failed (no handler for"
1856 " vimprobableedit protocol?)");
1857 unlink(temp_file_name
);
1863 /* mark the active text box as "under processing" */
1864 jsapi_evaluate_script(
1865 "document.activeElement.disabled = true;"
1866 "document.activeElement.originalBackground = "
1867 " document.activeElement.style.background;"
1868 "document.activeElement.style.background = '#aaaaaa';"
1871 g_child_watch_add(child_pid
, _resume_from_editor
, temp_file_name
);
1873 /* temp_file_name is freed in _resume_from_editor */
1881 /* pick up from where open_editor left the work to the glib event loop.
1883 This is called when the external editor exits.
1885 The data argument points to allocated memory containing the temporary file
1888 _resume_from_editor(GPid child_pid
, int child_status
, gpointer data
) {
1890 GString
*set_value_js
= g_string_new(
1891 "document.activeElement.value = \"");
1892 g_spawn_close_pid(child_pid
);
1893 gchar
*value
= NULL
, *message
= NULL
;
1894 gchar
*temp_file_name
= data
;
1895 gchar buffer
[BUF_SIZE
] = "";
1896 gchar
*buf_ptr
= buffer
;
1899 jsapi_evaluate_script(
1900 "document.activeElement.disabled = true;"
1901 "document.activeElement.style.background = '#aaaaaa';"
1907 echo_message(Error
, "External editor returned with non-zero status,"
1908 " discarding edits.");
1912 /* re-read the new contents of the file and put it into the HTML element */
1913 if (!access(temp_file_name
, R_OK
) == 0) {
1914 message
= g_strdup_printf("Could not access temporary file: %s",
1918 fp
= fopen(temp_file_name
, "r");
1920 /* this would be too weird to even emit an error message */
1923 jsapi_evaluate_script("document.activeElement.value = '';",
1928 while (EOF
!= (char_read
= fgetc(fp
))) {
1929 if (char_read
== '\n') {
1932 } else if (char_read
== '"') {
1936 *buf_ptr
++ = char_read
;
1938 /* ship out as the buffer when space gets tight. This has
1939 fuzz to save on thinking, plus we have enough space for the
1940 trailing "; in any case. */
1941 if (buf_ptr
-buffer
>=BUF_SIZE
-10) {
1943 g_string_append(set_value_js
, buffer
);
1950 g_string_append(set_value_js
, buffer
);
1953 jsapi_evaluate_script(set_value_js
->str
, &value
, &message
);
1957 /* Fall through, error and normal exit are identical */
1959 jsapi_evaluate_script(
1960 "document.activeElement.disabled = false;"
1961 "document.activeElement.style.background ="
1962 " document.activeElement.originalBackground;"
1965 g_string_free(set_value_js
, TRUE
);
1966 unlink(temp_file_name
);
1967 g_free(temp_file_name
);
1973 focus_input(const Arg
*arg
) {
1976 a
.s
= g_strdup("hints.focusInput();");
1981 client
.state
.manual_focus
= TRUE
;
1989 a
.s
= g_strdup("hints.clearFocus();");
1999 browser_settings(const Arg
*arg
) {
2002 set_error("Missing argument.");
2005 strncpy(line
, arg
->s
, 254);
2006 if (process_set_line(line
))
2009 set_error("Invalid setting.");
2015 search_word(int whichword
) {
2017 static char word
[240];
2018 char *c
= my_pair
.line
;
2020 while (isspace(*c
) && *c
)
2023 switch (whichword
) {
2025 while (*c
&& !isspace (*c
) && *c
!= '=' && k
< 240) {
2030 strncpy(my_pair
.what
, word
, 20);
2033 while (*c
&& k
< 240) {
2038 strncpy(my_pair
.value
, word
, 240);
2046 process_set_line(char *line
) {
2050 WebKitWebSettings
*settings
;
2052 settings
= webkit_web_view_get_settings(client
.gui
.webview
);
2053 my_pair
.line
= line
;
2055 if (!strlen(my_pair
.what
))
2058 while (isspace(*c
) && *c
)
2061 if (*c
== ':' || *c
== '=')
2067 listlen
= LENGTH(browsersettings
);
2068 for (i
= 0; i
< listlen
; i
++) {
2069 if (strlen(browsersettings
[i
].name
) == strlen(my_pair
.what
) && strncmp(browsersettings
[i
].name
, my_pair
.what
, strlen(my_pair
.what
)) == 0) {
2070 /* mandatory argument not provided */
2071 if (strlen(my_pair
.value
) == 0)
2073 /* process qmark? */
2074 if (strlen(my_pair
.what
) == 5 && strncmp("qmark", my_pair
.what
, 5) == 0) {
2075 return (process_save_qmark(my_pair
.value
, client
.gui
.webview
));
2077 /* interpret boolean values */
2078 if (browsersettings
[i
].boolval
) {
2079 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) {
2081 } 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) {
2086 } else if (browsersettings
[i
].colourval
) {
2087 /* interpret as hexadecimal colour */
2088 if (!parse_colour(my_pair
.value
)) {
2092 if (browsersettings
[i
].var
!= NULL
) {
2093 strncpy(browsersettings
[i
].var
, my_pair
.value
, MAX_SETTING_SIZE
);
2094 if (strlen(my_pair
.value
) > MAX_SETTING_SIZE
- 1) {
2095 /* in this case, \0 will not have been copied */
2096 browsersettings
[i
].var
[MAX_SETTING_SIZE
- 1] = '\0';
2097 /* in case this string is also used for a webkit setting, make sure it's consistent */
2098 my_pair
.value
[MAX_SETTING_SIZE
- 1] = '\0';
2099 echo_message(Info
, "String too long; automatically truncated!");
2102 if (strlen(browsersettings
[i
].webkit
) > 0) {
2103 /* activate appropriate webkit setting */
2104 if (browsersettings
[i
].boolval
) {
2105 g_object_set((GObject
*)settings
, browsersettings
[i
].webkit
, boolval
, NULL
);
2106 } else if (browsersettings
[i
].intval
) {
2107 g_object_set((GObject
*)settings
, browsersettings
[i
].webkit
, atoi(my_pair
.value
), NULL
);
2109 g_object_set((GObject
*)settings
, browsersettings
[i
].webkit
, my_pair
.value
, NULL
);
2111 webkit_web_view_set_settings(client
.gui
.webview
, settings
);
2114 if (strlen(my_pair
.what
) == 14) {
2115 if (strncmp("acceptlanguage", my_pair
.what
, 14) == 0) {
2116 g_object_set(G_OBJECT(client
.net
.session
), "accept-language", acceptlanguage
, NULL
);
2117 } else if (strncmp("completioncase", my_pair
.what
, 14) == 0) {
2118 complete_case_sensitive
= boolval
;
2120 } else if (strlen(my_pair
.what
) == 5 && strncmp("proxy", my_pair
.what
, 5) == 0) {
2121 toggle_proxy(boolval
);
2122 } else if (strlen(my_pair
.what
) == 10 && strncmp("scrollbars", my_pair
.what
, 10) == 0) {
2123 toggle_scrollbars(boolval
);
2124 } else if (strlen(my_pair
.what
) == 9 && strncmp("statusbar", my_pair
.what
, 9) == 0) {
2125 gtk_widget_set_visible(GTK_WIDGET(client
.gui
.statusbar
), boolval
);
2126 } else if (strlen(my_pair
.what
) == 8 && strncmp("inputbox", my_pair
.what
, 8) == 0) {
2127 gtk_widget_set_visible(client
.gui
.inputbox
, boolval
);
2128 } else if (strlen(my_pair
.what
) == 11 && strncmp("escapeinput", my_pair
.what
, 11) == 0) {
2129 escape_input_on_load
= boolval
;
2132 /* SSL certificate checking */
2133 if (strlen(my_pair
.what
) == 9 && strncmp("strictssl", my_pair
.what
, 9) == 0) {
2136 g_object_set(G_OBJECT(client
.net
.session
), "ssl-strict", TRUE
, NULL
);
2139 g_object_set(G_OBJECT(client
.net
.session
), "ssl-strict", FALSE
, NULL
);
2142 if (strlen(my_pair
.what
) == 8 && strncmp("cabundle", my_pair
.what
, 8) == 0) {
2143 g_object_set(G_OBJECT(client
.net
.session
), SOUP_SESSION_SSL_CA_FILE
, ca_bundle
, NULL
);
2145 if (strlen(my_pair
.what
) == 10 && strncmp("windowsize", my_pair
.what
, 10) == 0) {
2146 set_default_winsize(my_pair
.value
);
2150 if (browsersettings
[i
].reload
)
2151 webkit_web_view_reload(client
.gui
.webview
);
2159 process_line(char *line
) {
2160 char *c
= line
, *command_hist
;
2162 size_t len
, length
= strlen(line
);
2163 gboolean found
= FALSE
, success
= FALSE
;
2169 /* Ignore blank lines. */
2173 command_hist
= g_strdup(c
);
2175 /* check for colon command aliases first */
2176 for (l
= client
.config
.colon_aliases
; l
; l
= g_list_next(l
)) {
2177 Alias
*alias
= (Alias
*)l
->data
;
2178 if (length
== strlen(alias
->alias
) && strncmp(alias
->alias
, line
, length
) == 0) {
2179 /* reroute to target command */
2181 length
= strlen(alias
->target
);
2186 /* check standard commands */
2187 for (i
= 0; i
< LENGTH(commands
); i
++) {
2188 if (commands
[i
].cmd
== NULL
)
2190 len
= strlen(commands
[i
].cmd
);
2191 if (length
>= len
&& !strncmp(c
, commands
[i
].cmd
, len
) && (c
[len
] == ' ' || !c
[len
])) {
2193 a
.i
= commands
[i
].arg
.i
;
2194 a
.s
= g_strdup(length
> len
+ 1 ? &c
[len
+ 1] : commands
[i
].arg
.s
);
2195 success
= commands
[i
].func(&a
);
2201 save_command_history(command_hist
);
2202 g_free(command_hist
);
2205 echo_message(Error
, "Not a browser command: %s", c
);
2206 } else if (!success
) {
2207 if (client
.state
.error_msg
!= NULL
) {
2208 echo_message(Error
, client
.state
.error_msg
);
2209 g_free(client
.state
.error_msg
);
2210 client
.state
.error_msg
= NULL
;
2212 echo_message(Error
, "Unknown error. Please file a bug report!");
2219 search_tag(const Arg
* a
) {
2221 const char *filename
;
2222 const char *tag
= a
->s
;
2223 char s
[BUFFERSIZE
], foundtag
[40], url
[BUFFERSIZE
];
2227 /* The user must give us something to load up. */
2228 set_error("Bookmark tag required with this option.");
2232 if (strlen(tag
) > MAXTAGSIZE
) {
2233 set_error("Tag too long.");
2237 filename
= g_strdup_printf(BOOKMARKS_STORAGE_FILENAME
);
2238 f
= fopen(filename
, "r");
2239 g_free((gpointer
*)filename
);
2241 set_error("Couldn't open bookmarks file.");
2244 while (fgets(s
, BUFFERSIZE
-1, f
)) {
2247 while (isspace(s
[t
]))
2249 if (s
[t
] != ']') continue;
2262 foundtag
[i
++] = s
[k
++];
2264 /* foundtag now contains the tag */
2265 if (strlen(foundtag
) < MAXTAGSIZE
&& strcmp(tag
, foundtag
) == 0) {
2267 while (isspace(s
[i
])) i
++;
2269 while (s
[i
] && !isspace(s
[i
])) url
[k
++] = s
[i
++];
2271 Arg x
= { .i
= TargetNew
, .s
= url
};
2285 toggle_proxy(gboolean onoff
) {
2287 char *filename
, *new;
2289 if (onoff
== FALSE
) {
2290 g_object_set(client
.net
.session
, "proxy-uri", NULL
, NULL
);
2292 filename
= (char *)g_getenv("http_proxy");
2294 /* Fallthrough to checking HTTP_PROXY as well, since this can also be
2297 if (filename
== NULL
)
2298 filename
= (char *)g_getenv("HTTP_PROXY");
2300 if (filename
!= NULL
&& 0 < strlen(filename
)) {
2301 new = g_strrstr(filename
, "http://") ? g_strdup(filename
) : g_strdup_printf("http://%s", filename
);
2302 proxy_uri
= soup_uri_new(new);
2304 g_object_set(client
.net
.session
, "proxy-uri", proxy_uri
, NULL
);
2306 soup_uri_free(proxy_uri
);
2313 toggle_scrollbars(gboolean onoff
) {
2314 Gui
*gui
= &client
.gui
;
2315 if (onoff
== TRUE
) {
2316 gui
->adjust_h
= gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(gui
->viewport
));
2317 gui
->adjust_v
= gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(gui
->viewport
));
2318 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gui
->viewport
), GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
2320 gui
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(gui
->scroll_v
));
2321 gui
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(gui
->scroll_h
));
2322 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gui
->viewport
), GTK_POLICY_NEVER
, GTK_POLICY_NEVER
);
2324 gtk_widget_set_scroll_adjustments (GTK_WIDGET(gui
->webview
), gui
->adjust_h
, gui
->adjust_v
);
2329 void set_default_winsize(const char * const size
) {
2331 int x
= 640, y
= 480;
2333 x
= strtol(size
, &p
, 10);
2334 if (errno
== ERANGE
|| x
<= 0) {
2339 if (p
== size
|| strlen(size
) == p
- size
)
2342 y
= strtol(p
+ 1, NULL
, 10);
2343 if (errno
== ERANGE
|| y
<= 0)
2347 gtk_window_resize(GTK_WINDOW(client
.gui
.window
), x
, y
);
2351 update_url(const char *uri
) {
2352 Gui
*gui
= &client
.gui
;
2353 gboolean ssl
= g_str_has_prefix(uri
, "https://");
2355 WebKitWebFrame
*frame
;
2356 WebKitWebDataSource
*src
;
2357 WebKitNetworkRequest
*request
;
2360 char *sslactivecolor
;
2362 #ifdef ENABLE_HISTORY_INDICATOR
2363 char before
[] = " [";
2365 gboolean back
= webkit_web_view_can_go_back(gui
->webview
);
2366 gboolean fwd
= webkit_web_view_can_go_forward(gui
->webview
);
2369 before
[0] = after
[0] = '\0';
2371 markup
= g_markup_printf_escaped(
2372 #ifdef ENABLE_HISTORY_INDICATOR
2373 "<span font=\"%s\">%s%s%s%s%s</span>", statusfont
, uri
,
2374 before
, back
? "+" : "", fwd
? "-" : "", after
2376 "<span font=\"%s\">%s</span>", statusfont
, uri
2379 gtk_label_set_markup(GTK_LABEL(gui
->status_url
), markup
);
2382 frame
= webkit_web_view_get_main_frame(gui
->webview
);
2383 src
= webkit_web_frame_get_data_source(frame
);
2384 request
= webkit_web_data_source_get_request(src
);
2385 msg
= webkit_network_request_get_message(request
);
2386 ssl_ok
= soup_message_get_flags(msg
) & SOUP_MESSAGE_CERTIFICATE_TRUSTED
;
2388 sslactivecolor
= sslbgcolor
;
2390 sslactivecolor
= sslinvalidbgcolor
;
2392 gdk_color_parse(ssl
? sslactivecolor
: statusbgcolor
, &color
);
2393 gtk_widget_modify_bg(gui
->eventbox
, GTK_STATE_NORMAL
, &color
);
2394 gdk_color_parse(ssl
? sslcolor
: statuscolor
, &color
);
2395 gtk_widget_modify_fg(GTK_WIDGET(gui
->status_url
), GTK_STATE_NORMAL
, &color
);
2396 gtk_widget_modify_fg(GTK_WIDGET(gui
->status_state
), GTK_STATE_NORMAL
, &color
);
2401 State
* state
= &client
.state
;
2403 int download_count
= g_list_length(state
->activeDownloads
);
2404 GString
*status
= g_string_new("");
2406 /* construct the status line */
2408 /* count, modkey and input buffer */
2409 g_string_append_printf(status
, "%.0d", state
->count
);
2410 if (state
->current_modkey
) g_string_append_c(status
, state
->current_modkey
);
2412 /* the number of active downloads */
2413 if (state
->activeDownloads
) {
2414 g_string_append_printf(status
, " %d active %s", download_count
,
2415 (download_count
== 1) ? "download" : "downloads");
2418 #ifdef ENABLE_WGET_PROGRESS_BAR
2419 /* the progressbar */
2422 char progressbar
[progressbartick
+ 1];
2424 if (state
->activeDownloads
) {
2428 for (ptr
= state
->activeDownloads
; ptr
; ptr
= g_list_next(ptr
)) {
2429 progress
+= 100 * webkit_download_get_progress(ptr
->data
);
2432 progress
/= download_count
;
2434 } else if (webkit_web_view_get_load_status(client
.gui
.webview
) != WEBKIT_LOAD_FINISHED
2435 && webkit_web_view_get_load_status(client
.gui
.webview
) != WEBKIT_LOAD_FAILED
) {
2437 progress
= webkit_web_view_get_progress(client
.gui
.webview
) * 100;
2440 if (progress
>= 0) {
2441 ascii_bar(progressbartick
, progress
* progressbartick
/ 100, progressbar
);
2442 g_string_append_printf(status
, " %c%s%c",
2443 progressborderleft
, progressbar
, progressborderright
);
2448 /* and the current scroll position */
2450 int max
= gtk_adjustment_get_upper(client
.gui
.adjust_v
) - gtk_adjustment_get_page_size(client
.gui
.adjust_v
);
2451 int val
= (int)(gtk_adjustment_get_value(client
.gui
.adjust_v
) / max
* 100);
2454 g_string_append(status
, " All");
2456 g_string_append(status
, " Top");
2457 else if (val
== 100)
2458 g_string_append(status
, " Bot");
2460 g_string_append_printf(status
, " %d%%", val
);
2464 markup
= g_markup_printf_escaped("<span font=\"%s\">%s</span>", statusfont
, status
->str
);
2465 gtk_label_set_markup(GTK_LABEL(client
.gui
.status_state
), markup
);
2468 g_string_free(status
, TRUE
);
2472 setup_client(void) {
2473 State
*state
= &client
.state
;
2474 Config
*config
= &client
.config
;
2476 state
->mode
= ModeNormal
;
2478 state
->rememberedURI
[0] = '\0';
2479 state
->manual_focus
= FALSE
;
2480 state
->commandhistory
= NULL
;
2481 state
->commandpointer
= 0;
2483 config
->colon_aliases
= NULL
;
2484 config
->cookie_timeout
= 4800;
2490 client
.config
.modkeys
= calloc(LENGTH(keys
) + 1, sizeof(char));
2491 char *ptr
= client
.config
.modkeys
;
2493 for (i
= 0; i
< LENGTH(keys
); i
++)
2494 if (keys
[i
].modkey
&& !strchr(client
.config
.modkeys
, keys
[i
].modkey
))
2495 *(ptr
++) = keys
[i
].modkey
;
2496 client
.config
.modkeys
= realloc(client
.config
.modkeys
, &ptr
[0] - &client
.config
.modkeys
[0] + 1);
2501 Gui
*gui
= &client
.gui
;
2502 State
*state
= &client
.state
;
2504 gui
->scroll_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
2505 gui
->scroll_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
2506 gui
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(gui
->scroll_h
));
2507 gui
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(gui
->scroll_v
));
2508 if (client
.state
.embed
) {
2509 gui
->window
= GTK_WINDOW(gtk_plug_new(client
.state
.embed
));
2511 gui
->window
= GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL
));
2512 gtk_window_set_wmclass(GTK_WINDOW(gui
->window
), "vimprobable2", "Vimprobable2");
2514 gtk_window_set_default_size(GTK_WINDOW(gui
->window
), 640, 480);
2515 gui
->box
= GTK_BOX(gtk_vbox_new(FALSE
, 0));
2516 gui
->inputbox
= gtk_entry_new();
2517 gui
->webview
= (WebKitWebView
*)webkit_web_view_new();
2518 gui
->statusbar
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
2519 gui
->eventbox
= gtk_event_box_new();
2520 gui
->status_url
= gtk_label_new(NULL
);
2521 gui
->status_state
= gtk_label_new(NULL
);
2523 PangoFontDescription
*font
;
2524 GdkGeometry hints
= { 1, 1 };
2525 gui
->inspector
= webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(gui
->webview
));
2527 state
->clipboards
[0] = gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2528 state
->clipboards
[1] = gtk_clipboard_get(GDK_NONE
);
2530 gdk_color_parse(statusbgcolor
, &bg
);
2531 gtk_widget_modify_bg(gui
->eventbox
, GTK_STATE_NORMAL
, &bg
);
2532 gtk_widget_set_name(GTK_WIDGET(gui
->window
), "Vimprobable2");
2533 gtk_window_set_geometry_hints(gui
->window
, NULL
, &hints
, GDK_HINT_MIN_SIZE
);
2535 state
->keymap
= gdk_keymap_get_default();
2537 #ifdef DISABLE_SCROLLBAR
2538 gui
->viewport
= gtk_scrolled_window_new(NULL
, NULL
);
2539 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gui
->viewport
), GTK_POLICY_NEVER
, GTK_POLICY_NEVER
);
2541 /* Ensure we still see scrollbars. */
2542 gui
->viewport
= gtk_scrolled_window_new(gui
->adjust_h
, gui
->adjust_v
);
2546 gtk_container_add(GTK_CONTAINER(gui
->viewport
), GTK_WIDGET(gui
->webview
));
2548 /* Ensure we set the scroll adjustments now, so that if we're not drawing
2549 * titlebars, we can still scroll.
2551 gtk_widget_set_scroll_adjustments(GTK_WIDGET(gui
->webview
), gui
->adjust_h
, gui
->adjust_v
);
2553 font
= pango_font_description_from_string(urlboxfont
[0]);
2554 gtk_widget_modify_font(GTK_WIDGET(gui
->inputbox
), font
);
2555 pango_font_description_free(font
);
2556 gtk_entry_set_inner_border(GTK_ENTRY(gui
->inputbox
), NULL
);
2557 gtk_misc_set_alignment(GTK_MISC(gui
->status_url
), 0.0, 0.0);
2558 gtk_misc_set_alignment(GTK_MISC(gui
->status_state
), 1.0, 0.0);
2559 gtk_box_pack_start(gui
->statusbar
, gui
->status_url
, TRUE
, TRUE
, 2);
2560 gtk_box_pack_start(gui
->statusbar
, gui
->status_state
, FALSE
, FALSE
, 2);
2561 gtk_container_add(GTK_CONTAINER(gui
->eventbox
), GTK_WIDGET(gui
->statusbar
));
2562 gtk_box_pack_start(gui
->box
, gui
->viewport
, TRUE
, TRUE
, 0);
2563 gtk_box_pack_start(gui
->box
, gui
->eventbox
, FALSE
, FALSE
, 0);
2564 gtk_entry_set_has_frame(GTK_ENTRY(gui
->inputbox
), FALSE
);
2565 gtk_box_pack_end(gui
->box
, gui
->inputbox
, FALSE
, FALSE
, 0);
2566 gtk_container_add(GTK_CONTAINER(gui
->window
), GTK_WIDGET(gui
->box
));
2567 gtk_widget_grab_focus(GTK_WIDGET(gui
->webview
));
2568 gtk_widget_show_all(GTK_WIDGET(gui
->window
));
2569 set_widget_font_and_color(gui
->inputbox
, urlboxfont
[0], urlboxbgcolor
[0], urlboxcolor
[0]);
2570 g_object_set(gtk_widget_get_settings(gui
->inputbox
), "gtk-entry-select-on-focus", FALSE
, NULL
);
2575 WebKitWebSettings
*settings
= (WebKitWebSettings
*)webkit_web_settings_new();
2576 char *filename
, *file_url
;
2578 client
.net
.session
= webkit_get_default_session();
2579 g_object_set(G_OBJECT(client
.net
.session
), "ssl-ca-file", ca_bundle
, NULL
);
2580 g_object_set(G_OBJECT(client
.net
.session
), "ssl-strict", strict_ssl
, NULL
);
2581 g_object_set(G_OBJECT(settings
), "default-font-size", DEFAULT_FONT_SIZE
, NULL
);
2582 g_object_set(G_OBJECT(settings
), "enable-scripts", enablePlugins
, NULL
);
2583 g_object_set(G_OBJECT(settings
), "enable-plugins", enablePlugins
, NULL
);
2584 g_object_set(G_OBJECT(settings
), "enable-java-applet", enableJava
, NULL
);
2585 g_object_set(G_OBJECT(settings
), "enable-page-cache", enablePagecache
, NULL
);
2586 filename
= g_strdup_printf(USER_STYLESHEET
);
2587 file_url
= g_strdup_printf("file://%s", filename
);
2588 g_object_set(G_OBJECT(settings
), "user-stylesheet-uri", file_url
, NULL
);
2591 g_object_set(G_OBJECT(settings
), "user-agent", useragent
, NULL
);
2592 g_object_get(G_OBJECT(settings
), "zoom-step", &client
.config
.zoomstep
, NULL
);
2593 webkit_web_view_set_settings(client
.gui
.webview
, settings
);
2596 toggle_proxy(use_proxy
);
2601 WebKitWebFrame
*frame
= webkit_web_view_get_main_frame(client
.gui
.webview
);
2602 #ifdef ENABLE_COOKIE_SUPPORT
2604 g_signal_connect_after(G_OBJECT(client
.net
.session
), "request-started", G_CALLBACK(new_generic_request
), NULL
);
2606 /* Accept-language header */
2607 g_object_set(G_OBJECT(client
.net
.session
), "accept-language", acceptlanguage
, NULL
);
2609 g_object_connect(G_OBJECT(client
.gui
.window
),
2610 "signal::destroy", G_CALLBACK(window_destroyed_cb
), NULL
,
2613 g_signal_connect(G_OBJECT(frame
),
2614 "scrollbars-policy-changed", G_CALLBACK(blank_cb
), NULL
);
2616 g_object_connect(G_OBJECT(client
.gui
.webview
),
2617 "signal::title-changed", G_CALLBACK(webview_title_changed_cb
), NULL
,
2618 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), NULL
,
2619 "signal::load-committed", G_CALLBACK(webview_load_committed_cb
), NULL
,
2620 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), NULL
,
2621 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_navigation_cb
), NULL
,
2622 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_new_window_cb
), NULL
,
2623 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), NULL
,
2624 "signal::download-requested", G_CALLBACK(webview_download_cb
), NULL
,
2625 "signal::key-press-event", G_CALLBACK(webview_keypress_cb
), NULL
,
2626 "signal::hovering-over-link", G_CALLBACK(webview_hoverlink_cb
), NULL
,
2627 "signal::console-message", G_CALLBACK(webview_console_cb
), NULL
,
2628 "signal::create-web-view", G_CALLBACK(webview_open_in_new_window_cb
), NULL
,
2629 "signal::event", G_CALLBACK(notify_event_cb
), NULL
,
2631 /* webview adjustment */
2632 g_object_connect(G_OBJECT(client
.gui
.adjust_v
),
2633 "signal::value-changed", G_CALLBACK(webview_scroll_cb
), NULL
,
2636 g_object_connect(G_OBJECT(client
.gui
.inputbox
),
2637 "signal::activate", G_CALLBACK(inputbox_activate_cb
), NULL
,
2638 "signal::key-press-event", G_CALLBACK(inputbox_keypress_cb
), NULL
,
2639 "signal::key-release-event", G_CALLBACK(inputbox_keyrelease_cb
), NULL
,
2640 #ifdef ENABLE_INCREMENTAL_SEARCH
2641 "signal::changed", G_CALLBACK(inputbox_changed_cb
), NULL
,
2645 g_signal_connect(G_OBJECT(client
.gui
.inspector
),
2646 "inspect-web-view", G_CALLBACK(inspector_inspect_web_view_cb
), NULL
);
2649 #ifdef ENABLE_USER_SCRIPTFILE
2651 scripts_run_user_file() {
2652 gchar
*js
= NULL
, *user_scriptfile
= NULL
;
2653 GError
*error
= NULL
;
2655 user_scriptfile
= g_strdup_printf(USER_SCRIPTFILE
);
2657 /* run the users script file */
2658 if (g_file_test(user_scriptfile
, G_FILE_TEST_IS_REGULAR
)
2659 && g_file_get_contents(user_scriptfile
, &js
, NULL
, &error
)) {
2661 gchar
*value
= NULL
, *message
= NULL
;
2663 jsapi_evaluate_script(js
, &value
, &message
);
2666 fprintf(stderr
, "%s", message
);
2671 fprintf(stderr
, "Cannot open %s: %s\n", user_scriptfile
, error
? error
->message
: "file not found");
2674 g_free(user_scriptfile
);
2678 #ifdef ENABLE_COOKIE_SUPPORT
2682 Network
*net
= &client
.net
;
2683 if (net
->file_cookie_jar
)
2684 g_object_unref(net
->file_cookie_jar
);
2686 if (net
->session_cookie_jar
)
2687 g_object_unref(net
->session_cookie_jar
);
2689 net
->session_cookie_jar
= soup_cookie_jar_new();
2691 net
->cookie_store
= g_strdup_printf(COOKIES_STORAGE_FILENAME
);
2695 g_signal_connect(G_OBJECT(net
->file_cookie_jar
), "changed",
2696 G_CALLBACK(update_cookie_jar
), NULL
);
2699 /* TA: XXX - we should be using this callback for any header-requests we
2700 * receive (hence the name "new_generic_request" -- but for now, its use
2701 * is limited to handling cookies.
2704 new_generic_request(SoupSession
*session
, SoupMessage
*soup_msg
, gpointer unused
)
2706 SoupMessageHeaders
*soup_msg_h
;
2710 soup_msg_h
= soup_msg
->request_headers
;
2711 soup_message_headers_remove(soup_msg_h
, "Cookie");
2712 uri
= soup_message_get_uri(soup_msg
);
2713 if ((cookie_str
= get_cookies(uri
))) {
2714 soup_message_headers_append(soup_msg_h
, "Cookie", cookie_str
);
2718 g_signal_connect_after(G_OBJECT(soup_msg
), "got-headers", G_CALLBACK(handle_cookie_request
), NULL
);
2724 get_cookies(SoupURI
*soup_uri
) {
2727 cookie_str
= soup_cookie_jar_get_cookies(client
.net
.file_cookie_jar
, soup_uri
, TRUE
);
2733 handle_cookie_request(SoupMessage
*soup_msg
, gpointer unused
)
2735 GSList
*resp_cookie
= NULL
, *cookie_list
;
2738 cookie_list
= soup_cookies_from_response(soup_msg
);
2739 for(resp_cookie
= cookie_list
; resp_cookie
; resp_cookie
= g_slist_next(resp_cookie
))
2741 SoupDate
*soup_date
;
2742 cookie
= soup_cookie_copy((SoupCookie
*)resp_cookie
->data
);
2744 if (client
.config
.cookie_timeout
&& cookie
->expires
== NULL
) {
2745 soup_date
= soup_date_new_from_time_t(time(NULL
) + client
.config
.cookie_timeout
* 10);
2746 soup_cookie_set_expires(cookie
, soup_date
);
2747 soup_date_free(soup_date
);
2749 soup_cookie_jar_add_cookie(client
.net
.file_cookie_jar
, cookie
);
2752 soup_cookies_free(cookie_list
);
2758 update_cookie_jar(SoupCookieJar
*jar
, SoupCookie
*old
, SoupCookie
*new)
2761 /* Nothing to do. */
2766 copy
= soup_cookie_copy(new);
2768 soup_cookie_jar_add_cookie(client
.net
.session_cookie_jar
, copy
);
2774 load_all_cookies(void)
2776 Network
*net
= &client
.net
;
2777 GSList
*cookie_list
;
2778 net
->file_cookie_jar
= soup_cookie_jar_text_new(net
->cookie_store
, COOKIES_STORAGE_READONLY
);
2780 /* Put them back in the session store. */
2781 GSList
*cookies_from_file
= soup_cookie_jar_all_cookies(net
->file_cookie_jar
);
2782 cookie_list
= cookies_from_file
;
2784 for (; cookies_from_file
;
2785 cookies_from_file
= cookies_from_file
->next
)
2787 soup_cookie_jar_add_cookie(net
->session_cookie_jar
, cookies_from_file
->data
);
2790 soup_cookies_free(cookies_from_file
);
2791 g_slist_free(cookie_list
);
2800 /* Free up any nasty globals before exiting. */
2801 #ifdef ENABLE_COOKIE_SUPPORT
2802 if (client
.net
.cookie_store
)
2803 g_free(client
.net
.cookie_store
);
2809 main(int argc
, char *argv
[]) {
2811 static char url
[256] = "";
2812 static gboolean ver
= false;
2813 static gboolean configfile_exists
= FALSE
;
2814 static const char *cfile
= NULL
;
2815 static gchar
*winid
= NULL
;
2816 static GOptionEntry opts
[] = {
2817 { "version", 'v', 0, G_OPTION_ARG_NONE
, &ver
, "print version", NULL
},
2818 { "embed", 'e', 0, G_OPTION_ARG_STRING
, &winid
, "embedded", NULL
},
2819 { "configfile", 'c', 0, G_OPTION_ARG_STRING
, &cfile
, "config file", NULL
},
2824 Config
*config
= &client
.config
;
2826 /* command line argument: version */
2827 if (!gtk_init_with_args(&argc
, &argv
, "[<uri>]", opts
, NULL
, &err
)) {
2828 g_printerr("can't init gtk: %s\n", err
->message
);
2830 return EXIT_FAILURE
;
2834 printf("%s\n", INTERNAL_VERSION
);
2835 return EXIT_SUCCESS
;
2840 if (getenv("TMPDIR")) {
2841 strncpy(temp_dir
, getenv("TMPDIR"), MAX_SETTING_SIZE
);
2842 temp_dir
[MAX_SETTING_SIZE
-1] = 0;
2845 if( getenv("XDG_CONFIG_HOME") )
2846 config
->config_base
= g_strdup_printf("%s", getenv("XDG_CONFIG_HOME"));
2848 config
->config_base
= g_strdup_printf("%s/.config/", getenv("HOME"));
2851 config
->configfile
= g_strdup(cfile
);
2853 config
->configfile
= g_strdup_printf(RCFILE
);
2855 if (!g_thread_supported())
2856 g_thread_init(NULL
);
2859 if (strncmp(winid
, "0x", 2) == 0) {
2860 client
.state
.embed
= strtol(winid
, NULL
, 16);
2862 client
.state
.embed
= atoi(winid
);
2869 #ifdef ENABLE_COOKIE_SUPPORT
2873 make_searchengines_list(searchengines
, LENGTH(searchengines
));
2874 make_uri_handlers_list(uri_handlers
, LENGTH(uri_handlers
));
2876 /* Check if the specified file exists. */
2877 /* And only warn the user, if they explicitly asked for a config on the
2880 if (!(access(config
->configfile
, F_OK
) == 0) && cfile
) {
2881 echo_message(Info
, "Config file '%s' doesn't exist", cfile
);
2882 } else if ((access(config
->configfile
, F_OK
) == 0))
2883 configfile_exists
= true;
2885 /* read config file */
2886 /* But only report errors if we failed, and the file existed. */
2887 if ((SUCCESS
!= read_rcfile(config
->configfile
)) && configfile_exists
) {
2888 echo_message(Error
, "Error in config file '%s'", config
->configfile
);
2889 g_free(config
->configfile
);
2892 /* command line argument: URL */
2894 strncpy(url
, argv
[argc
- 1], 255);
2896 strncpy(url
, startpage
, 255);
2899 a
.i
= TargetCurrent
;
2906 return EXIT_SUCCESS
;