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 if (state
->search_handle
) {
502 g_free(state
->search_handle
);
504 state
->search_handle
= g_strdup(&text
[1]);
506 } else if (text
[0] == '.' || text
[0] == ',' || text
[0] == ';') {
508 a
.s
= g_strdup_printf("hints.fire();");
514 gtk_widget_grab_focus(GTK_WIDGET(gui
->webview
));
518 inputbox_keypress_cb(GtkEntry
*entry
, GdkEventKey
*event
) {
521 State
*state
= &client
.state
;
523 if (state
->mode
== ModeHints
) {
524 if (event
->keyval
== GDK_Tab
) {
526 a
.s
= g_strdup_printf("hints.focusNextHint();");
532 if (event
->keyval
== GDK_ISO_Left_Tab
) {
534 a
.s
= g_strdup_printf("hints.focusPreviousHint();");
540 if (event
->keyval
== GDK_Return
) {
542 a
.s
= g_strdup_printf("hints.fire();");
549 switch (event
->keyval
) {
550 case GDK_bracketleft
:
552 if (!IS_ESCAPE(event
)) break;
553 a
.i
= HideCompletion
;
556 state
->commandpointer
= 0;
565 return commandhistoryfetch(&a
);
569 return commandhistoryfetch(&a
);
571 case GDK_ISO_Left_Tab
:
577 if (state
->mode
== ModeHints
) {
578 if ((CLEAN(event
->state
) & GDK_SHIFT_MASK
) &&
579 (CLEAN(event
->state
) & GDK_CONTROL_MASK
) &&
580 (event
->keyval
== GDK_BackSpace
)) {
583 a
.s
= g_strdup_printf("hints.updateHints(%d);", state
->count
);
590 numval
= g_unichar_digit_value((gunichar
) gdk_keyval_to_unicode(event
->keyval
));
591 if ((numval
>= 1 && numval
<= 9) || (numval
== 0 && state
->count
)) {
592 /* allow a zero as non-first number */
593 state
->count
= (state
->count
? state
->count
* 10 : 0) + numval
;
595 a
.s
= g_strdup_printf("hints.updateHints(%d);", state
->count
);
607 notify_event_cb(GtkWidget
*widget
, GdkEvent
*event
, gpointer user_data
) {
609 WebKitHitTestResult
*result
;
610 WebKitHitTestResultContext context
;
611 State
*state
= &client
.state
;
612 if (state
->mode
== ModeNormal
&& event
->type
== GDK_BUTTON_RELEASE
) {
613 /* handle mouse click events */
614 for (i
= 0; i
< LENGTH(mouse
); i
++) {
615 if (mouse
[i
].mask
== CLEAN(event
->button
.state
)
616 && (mouse
[i
].modkey
== state
->current_modkey
617 || (!mouse
[i
].modkey
&& !state
->current_modkey
)
618 || mouse
[i
].modkey
== GDK_VoidSymbol
) /* wildcard */
619 && mouse
[i
].button
== event
->button
.button
621 if (mouse
[i
].func(&mouse
[i
].arg
)) {
622 state
->current_modkey
= state
->count
= 0;
628 result
= webkit_web_view_get_hit_test_result(WEBKIT_WEB_VIEW(widget
), (GdkEventButton
*)event
);
629 g_object_get(result
, "context", &context
, NULL
);
630 if (context
& WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE
) {
631 Arg a
= { .i
= ModeInsert
};
633 state
->manual_focus
= TRUE
;
635 } else if (state
->mode
== ModeInsert
&& event
->type
== GDK_BUTTON_RELEASE
) {
636 result
= webkit_web_view_get_hit_test_result(WEBKIT_WEB_VIEW(widget
), (GdkEventButton
*)event
);
637 g_object_get(result
, "context", &context
, NULL
);
638 if (!(context
& WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE
)) {
639 Arg a
= { .i
= ModeNormal
};
643 gchar
*value
= NULL
, *message
= NULL
;
644 jsapi_evaluate_script("window.getSelection().focusNode", &value
, &message
);
645 if (value
&& !strcmp(value
, "[object HTMLFormElement]")) {
646 Arg a
= { .i
= ModeInsert
, .s
= NULL
};
648 state
->manual_focus
= TRUE
;
656 static gboolean
inputbox_keyrelease_cb(GtkEntry
*entry
, GdkEventKey
*event
) {
658 guint16 length
= gtk_entry_get_text_length(entry
);
661 a
.i
= HideCompletion
;
669 static gboolean
inputbox_changed_cb(GtkEditable
*entry
, gpointer user_data
) {
671 char *text
= (char*)gtk_entry_get_text(GTK_ENTRY(entry
));
672 guint16 length
= gtk_entry_get_text_length(GTK_ENTRY(entry
));
673 gboolean forward
= FALSE
;
675 /* Update incremental search if the user changes the search text.
677 * Note: gtk_widget_is_focus() is a poor way to check if the change comes
678 * from the user. But if the entry is focused and the text is set
679 * through gtk_entry_set_text() in some asyncrounous operation,
680 * I would consider that a bug.
683 if (gtk_widget_is_focus(GTK_WIDGET(entry
)) && length
> 1 && ((forward
= text
[0] == '/') || text
[0] == '?')) {
684 webkit_web_view_unmark_text_matches(client
.gui
.webview
);
685 webkit_web_view_search_text(client
.gui
.webview
, &text
[1], searchoptions
& CaseSensitive
, forward
, searchoptions
& Wrapping
);
687 } else if (gtk_widget_is_focus(GTK_WIDGET(entry
)) && length
>= 1 &&
688 (text
[0] == '.' || text
[0] == ',' || text
[0] == ';')) {
692 a
.s
= g_strconcat("hints.createHints('", text
+ 1, "', 'f');", NULL
);
696 a
.s
= g_strconcat("hints.createHints('", text
+ 1, "', 'F');", NULL
);
703 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 's');", NULL
);
706 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'y');", NULL
);
709 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'f');", NULL
);
712 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'F');", NULL
);
714 case 'O': case 'T': case 'W':
715 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'O');", NULL
);
718 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'i');", NULL
);
721 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'I');", NULL
);
726 client
.state
.count
= 0;
733 } else if (length
== 0) {
734 client
.state
.mode
= ModeNormal
;
736 a
.s
= g_strdup("hints.clearHints();");
739 client
.state
.count
= 0;
748 void fill_suggline(char * suggline
, const char * command
, const char *fill_with
) {
749 memset(suggline
, 0, 512);
750 strncpy(suggline
, command
, 512);
751 strncat(suggline
, " ", 1);
752 strncat(suggline
, fill_with
, 512 - strlen(suggline
) - 1);
755 GtkWidget
* fill_eventbox(const char * completion_line
) {
757 GtkWidget
*row_eventbox
, *el
;
759 char *markup
, *markup_tmp
;
761 row
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
762 row_eventbox
= gtk_event_box_new();
763 gdk_color_parse(completionbgcolor
[0], &color
);
764 gtk_widget_modify_bg(row_eventbox
, GTK_STATE_NORMAL
, &color
);
765 el
= gtk_label_new(NULL
);
766 markup_tmp
= g_markup_escape_text(completion_line
, strlen(completion_line
));
767 markup
= g_strconcat("<span font=\"", completionfont
[0], "\" color=\"", completioncolor
[0], "\">",
768 markup_tmp
, "</span>", NULL
);
769 gtk_label_set_markup(GTK_LABEL(el
), markup
);
772 gtk_misc_set_alignment(GTK_MISC(el
), 0, 0);
773 gtk_box_pack_start(row
, el
, TRUE
, TRUE
, 2);
774 gtk_container_add(GTK_CONTAINER(row_eventbox
), GTK_WIDGET(row
));
779 complete(const Arg
*arg
) {
780 char *str
, *p
, *s
, *markup
, *entry
, *searchfor
, command
[32] = "", suggline
[512] = "", **suggurls
;
781 size_t listlen
, len
, cmdlen
;
783 Listelement
*elementlist
= NULL
, *elementpointer
;
784 gboolean highlight
= FALSE
;
786 GtkWidget
*row_eventbox
, *el
;
789 static GtkWidget
*table
, *top_border
;
791 static char **suggestions
;
792 static GtkWidget
**widgets
;
793 static int n
= 0, m
, current
= -1;
794 Gui
*gui
= &client
.gui
;
796 str
= (char*)gtk_entry_get_text(GTK_ENTRY(gui
->inputbox
));
799 /* Get the length of the list of commands for completion. We need this to
800 * malloc/realloc correctly.
802 listlen
= LENGTH(commands
);
804 if ((len
== 0 || str
[0] != ':') && arg
->i
!= HideCompletion
)
807 if (arg
->i
!= HideCompletion
&& widgets
&& current
!= -1 && !strcmp(&str
[1], suggestions
[current
])) {
808 gdk_color_parse(completionbgcolor
[0], &color
);
809 gtk_widget_modify_bg(widgets
[current
], GTK_STATE_NORMAL
, &color
);
810 current
= (n
+ current
+ (arg
->i
== DirectionPrev
? -1 : 1)) % n
;
811 if ((arg
->i
== DirectionNext
&& current
== 0)
812 || (arg
->i
== DirectionPrev
&& current
== n
- 1))
818 gtk_widget_destroy(GTK_WIDGET(table
));
819 gtk_widget_destroy(GTK_WIDGET(top_border
));
826 if (arg
->i
== HideCompletion
)
829 } else if (arg
->i
== HideCompletion
)
832 prefix
= g_strdup(str
);
833 widgets
= malloc(sizeof(GtkWidget
*) * listlen
);
834 suggestions
= malloc(sizeof(char*) * listlen
);
835 top_border
= gtk_event_box_new();
836 gtk_widget_set_size_request(GTK_WIDGET(top_border
), 0, 1);
837 gdk_color_parse(completioncolor
[2], &color
);
838 gtk_widget_modify_bg(top_border
, GTK_STATE_NORMAL
, &color
);
839 table
= gtk_event_box_new();
840 gdk_color_parse(completionbgcolor
[0], &color
);
841 _table
= GTK_BOX(gtk_vbox_new(FALSE
, 0));
843 if (strchr(str
, ' ') == NULL
) {
844 /* command completion */
845 listlen
= LENGTH(commands
);
846 for (i
= 0; i
< listlen
; i
++) {
847 if (commands
[i
].cmd
== NULL
)
849 cmdlen
= strlen(commands
[i
].cmd
);
850 if (!highlight
|| (n
< MAX_LIST_SIZE
&& len
- 1 <= cmdlen
&& !strncmp(&str
[1], commands
[i
].cmd
, len
- 1))) {
851 p
= s
= malloc(sizeof(char*) * (highlight
? sizeof(COMPLETION_TAG_OPEN
) + sizeof(COMPLETION_TAG_CLOSE
) - 1 : 1) + cmdlen
);
853 memcpy(p
, COMPLETION_TAG_OPEN
, sizeof(COMPLETION_TAG_OPEN
) - 1);
854 memcpy((p
+= sizeof(COMPLETION_TAG_OPEN
) - 1), &str
[1], len
- 1);
855 memcpy((p
+= len
- 1), COMPLETION_TAG_CLOSE
, sizeof(COMPLETION_TAG_CLOSE
) - 1);
856 p
+= sizeof(COMPLETION_TAG_CLOSE
) - 1;
858 memcpy(p
, &commands
[i
].cmd
[len
- 1], cmdlen
- len
+ 2);
859 row
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
860 row_eventbox
= gtk_event_box_new();
861 gtk_widget_modify_bg(row_eventbox
, GTK_STATE_NORMAL
, &color
);
862 el
= gtk_label_new(NULL
);
863 markup
= g_strconcat("<span font=\"", completionfont
[0], "\" color=\"", completioncolor
[0], "\">", s
, "</span>", NULL
);
865 gtk_label_set_markup(GTK_LABEL(el
), markup
);
867 gtk_misc_set_alignment(GTK_MISC(el
), 0, 0);
868 gtk_box_pack_start(row
, el
, TRUE
, TRUE
, 2);
869 gtk_container_add(GTK_CONTAINER(row_eventbox
), GTK_WIDGET(row
));
870 gtk_box_pack_start(_table
, GTK_WIDGET(row_eventbox
), FALSE
, FALSE
, 0);
871 suggestions
[n
] = commands
[i
].cmd
;
872 widgets
[n
++] = row_eventbox
;
876 entry
= (char *)malloc(512 * sizeof(char));
880 memset(entry
, 0, 512);
881 suggurls
= malloc(sizeof(char*) * listlen
);
882 if (suggurls
== NULL
) {
885 spacepos
= strcspn(str
, " ");
886 searchfor
= (str
+ spacepos
+ 1);
887 strncpy(command
, (str
+ 1), spacepos
- 1);
888 if (strlen(command
) == 3 && strncmp(command
, "set", 3) == 0) {
889 /* browser settings */
890 listlen
= LENGTH(browsersettings
);
891 for (i
= 0; i
< listlen
; i
++) {
892 if (n
< MAX_LIST_SIZE
&& strstr(browsersettings
[i
].name
, searchfor
) != NULL
) {
894 fill_suggline(suggline
, command
, browsersettings
[i
].name
);
895 /* FIXME(HP): This memory is never freed */
896 suggurls
[n
] = (char *)malloc(sizeof(char) * 512 + 1);
897 strncpy(suggurls
[n
], suggline
, 512);
898 suggestions
[n
] = suggurls
[n
];
899 row_eventbox
= fill_eventbox(suggline
);
900 gtk_box_pack_start(_table
, GTK_WIDGET(row_eventbox
), FALSE
, FALSE
, 0);
901 widgets
[n
++] = row_eventbox
;
905 } else if (strlen(command
) == 2 && strncmp(command
, "qt", 2) == 0) {
906 /* completion on tags */
907 spacepos
= strcspn(str
, " ");
908 searchfor
= (str
+ spacepos
+ 1);
909 elementlist
= complete_list(searchfor
, 1, elementlist
);
911 /* URL completion: bookmarks */
912 elementlist
= complete_list(searchfor
, 0, elementlist
);
913 m
= count_list(elementlist
);
914 if (m
< MAX_LIST_SIZE
) {
915 /* URL completion: history */
916 elementlist
= complete_list(searchfor
, 2, elementlist
);
919 elementpointer
= elementlist
;
920 while (elementpointer
!= NULL
) {
921 fill_suggline(suggline
, command
, elementpointer
->element
);
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
;
929 elementpointer
= elementpointer
->next
;
930 if (n
>= MAX_LIST_SIZE
)
933 free_list(elementlist
);
934 if (suggurls
!= NULL
) {
943 /* TA: FIXME - this needs rethinking entirely. */
945 GtkWidget
**widgets_temp
= realloc(widgets
, sizeof(*widgets
) * n
);
946 if (widgets_temp
== NULL
&& widgets
== NULL
) {
947 fprintf(stderr
, "Couldn't realloc() widgets\n");
950 widgets
= widgets_temp
;
951 char **suggestions_temp
= realloc(suggestions
, sizeof(*suggestions
) * n
);
952 if (suggestions_temp
== NULL
&& suggestions
== NULL
) {
953 fprintf(stderr
, "Couldn't realloc() suggestions\n");
956 suggestions
= suggestions_temp
;
959 gdk_color_parse(completionbgcolor
[1], &color
);
960 gtk_widget_modify_bg(table
, GTK_STATE_NORMAL
, &color
);
961 el
= gtk_label_new(NULL
);
962 gtk_misc_set_alignment(GTK_MISC(el
), 0, 0);
963 markup
= g_strconcat("<span font=\"", completionfont
[1], "\" color=\"", completioncolor
[1], "\">No Completions</span>", NULL
);
964 gtk_label_set_markup(GTK_LABEL(el
), markup
);
966 gtk_box_pack_start(_table
, GTK_WIDGET(el
), FALSE
, FALSE
, 0);
968 gtk_box_pack_start(gui
->box
, GTK_WIDGET(top_border
), FALSE
, FALSE
, 0);
969 gtk_container_add(GTK_CONTAINER(table
), GTK_WIDGET(_table
));
970 gtk_box_pack_start(gui
->box
, GTK_WIDGET(table
), FALSE
, FALSE
, 0);
971 gtk_widget_show_all(GTK_WIDGET(gui
->window
));
974 current
= arg
->i
== DirectionPrev
? n
- 1 : 0;
977 gdk_color_parse(completionbgcolor
[2], &color
);
978 gtk_widget_modify_bg(GTK_WIDGET(widgets
[current
]), GTK_STATE_NORMAL
, &color
);
979 s
= g_strconcat(":", suggestions
[current
], NULL
);
980 gtk_entry_set_text(GTK_ENTRY(gui
->inputbox
), s
);
983 gtk_entry_set_text(GTK_ENTRY(gui
->inputbox
), prefix
);
984 gtk_editable_set_position(GTK_EDITABLE(gui
->inputbox
), -1);
989 descend(const Arg
*arg
) {
990 char *source
= (char*)webkit_web_view_get_uri(client
.gui
.webview
), *p
= &source
[0], *new;
992 client
.state
.count
= client
.state
.count
? client
.state
.count
: 1;
996 if (arg
->i
== Rootdir
) {
997 for (i
= 0; i
< 3; i
++) /* get to the third slash */
998 if (!(p
= strchr(++p
, '/')))
999 return TRUE
; /* if we cannot find it quit */
1001 len
= strlen(source
);
1002 if (!len
) /* if string is empty quit */
1004 p
= source
+ len
; /* start at the end */
1005 if (*(p
- 1) == '/') /* /\/$/ is not an additional level */
1006 ++client
.state
.count
;
1007 for (i
= 0; i
< client
.state
.count
; i
++)
1008 while(*(p
--) != '/' || *p
== '/') /* count /\/+/ as one slash */
1009 if (p
== source
) /* if we reach the first char pointer quit */
1011 ++p
; /* since we do p-- in the while, we are pointing at
1012 the char before the slash, so +1 */
1014 len
= p
- source
+ 1; /* new length = end - start + 1 */
1015 new = malloc(len
+ 1);
1016 memcpy(new, source
, len
);
1018 webkit_web_view_load_uri(client
.gui
.webview
, new);
1024 echo(const Arg
*arg
) {
1025 int index
= !arg
->s
? 0 : arg
->i
& (~NoAutoHide
);
1027 if (index
< Info
|| index
> Error
)
1030 if (!gtk_widget_is_focus(GTK_WIDGET(client
.gui
.inputbox
))) {
1031 set_widget_font_and_color(client
.gui
.inputbox
, urlboxfont
[index
], urlboxbgcolor
[index
], urlboxcolor
[index
]);
1032 gtk_entry_set_text(GTK_ENTRY(client
.gui
.inputbox
), !arg
->s
? "" : arg
->s
);
1039 open_inspector(const Arg
* arg
) {
1040 gboolean inspect_enabled
;
1041 WebKitWebSettings
*settings
;
1043 settings
= webkit_web_view_get_settings(client
.gui
.webview
);
1044 g_object_get(G_OBJECT(settings
), "enable-developer-extras", &inspect_enabled
, NULL
);
1045 if (inspect_enabled
) {
1046 webkit_web_inspector_show(client
.gui
.inspector
);
1049 echo_message(Error
, "Webinspector is not enabled");
1055 input(const Arg
*arg
) {
1057 client
.state
.count
= 0;
1061 GtkWidget
*inputbox
= client
.gui
.inputbox
;
1063 /* if inputbox hidden, show it again */
1064 if (!gtk_widget_get_visible(inputbox
))
1065 gtk_widget_set_visible(inputbox
, TRUE
);
1069 /* Set the colour and font back to the default, so that we don't still
1070 * maintain a red colour from a warning from an end of search indicator,
1073 set_widget_font_and_color(inputbox
, urlboxfont
[index
], urlboxbgcolor
[index
], urlboxcolor
[index
]);
1075 /* to avoid things like :open URL :open URL2 or :open :open URL */
1076 gtk_entry_set_text(GTK_ENTRY(inputbox
), "");
1077 gtk_editable_insert_text(GTK_EDITABLE(inputbox
), arg
->s
, -1, &pos
);
1078 if (arg
->i
& InsertCurrentURL
&& (url
= webkit_web_view_get_uri(client
.gui
.webview
)))
1079 gtk_editable_insert_text(GTK_EDITABLE(inputbox
), url
, -1, &pos
);
1081 gtk_widget_grab_focus(inputbox
);
1082 gtk_editable_set_position(GTK_EDITABLE(inputbox
), -1);
1084 if (arg
->s
[0] == '.' || arg
->s
[0] == ',' || arg
->s
[0] == ';') {
1085 client
.state
.mode
= ModeHints
;
1087 switch (arg
->s
[0]) {
1089 a
.s
= g_strdup("hints.createHints('', 'f');");
1093 a
.s
= g_strdup("hints.createHints('', 'F');");
1099 switch (arg
->s
[1]) {
1101 a
.s
= g_strdup("hints.createHints('', 's');");
1104 a
.s
= g_strdup("hints.createHints('', 'y');");
1107 a
.s
= g_strdup("hints.createHints('', 'f');");
1110 a
.s
= g_strdup("hints.createHints('', 'F');");
1112 case 'O': case 'T': case 'W':
1113 a
.s
= g_strdup("hints.createHints('', 'O');");
1116 a
.s
= g_strdup("hints.createHints('', 'i');");
1119 a
.s
= g_strdup("hints.createHints('', 'I');");
1125 client
.state
.count
= 0;
1136 navigate(const Arg
*arg
) {
1137 if (arg
->i
& NavigationForwardBack
)
1138 webkit_web_view_go_back_or_forward(client
.gui
.webview
, (arg
->i
== NavigationBack
? -1 : 1) * (client
.state
.count
? client
.state
.count
: 1));
1139 else if (arg
->i
& NavigationReloadActions
)
1140 (arg
->i
== NavigationReload
? webkit_web_view_reload
: webkit_web_view_reload_bypass_cache
)(client
.gui
.webview
);
1142 webkit_web_view_stop_loading(client
.gui
.webview
);
1147 number(const Arg
*arg
) {
1148 const char *source
= webkit_web_view_get_uri(client
.gui
.webview
);
1149 char *uri
, *p
, *new;
1150 int number
, diff
= (client
.state
.count
? client
.state
.count
: 1) * (arg
->i
== Increment
? 1 : -1);
1154 uri
= g_strdup(source
); /* copy string */
1156 while(*p
!= '\0') /* goto the end of the string */
1159 while(*p
>= '0' && *p
<= '9') /* go back until non number char is reached */
1161 if (*(++p
) == '\0') { /* if no numbers were found abort */
1165 number
= atoi(p
) + diff
; /* apply diff on number */
1167 new = g_strdup_printf("%s%d", uri
, number
); /* create new uri */
1168 webkit_web_view_load_uri(client
.gui
.webview
, new);
1175 open_arg(const Arg
*arg
) {
1177 char *s
= arg
->s
, *p
= NULL
, *new;
1178 Arg a
= { .i
= NavigationReload
};
1180 char *search_uri
, *search_term
;
1182 if (client
.state
.embed
) {
1184 snprintf(winid
, LENGTH(winid
), "%u", (gint
)client
.state
.embed
);
1198 else if (arg
->i
== TargetCurrent
) {
1199 while(*s
== ' ') /* strip leading whitespace */
1201 p
= (s
+ strlen(s
) - 1);
1202 while(*p
== ' ') /* strip trailing whitespace */
1207 /* check for external handlers */
1208 if (open_handler(s
))
1210 /* check for search engines */
1212 if (p
) { /* check for search engines */
1214 search_uri
= find_uri_for_searchengine(s
);
1215 if (search_uri
!= NULL
) {
1216 search_term
= soup_uri_encode(p
+1, "&");
1217 new = g_strdup_printf(search_uri
, search_term
);
1218 g_free(search_term
);
1223 if (len
> 3 && strstr(s
, "://")) { /* valid url? */
1224 p
= new = g_malloc(len
+ 1);
1225 while(*s
!= '\0') { /* strip whitespaces */
1231 } else if (strcspn(s
, "/") == 0 || strcspn(s
, "./") == 0) { /* prepend "file://" */
1232 new = g_malloc(sizeof("file://") + len
);
1233 strcpy(new, "file://");
1234 memcpy(&new[sizeof("file://") - 1], s
, len
+ 1);
1235 } else if (p
|| !strchr(s
, '.')) { /* whitespaces or no dot? */
1236 search_uri
= find_uri_for_searchengine(defaultsearch
);
1237 if (search_uri
!= NULL
) {
1238 search_term
= soup_uri_encode(s
, "&");
1239 new = g_strdup_printf(search_uri
, search_term
);
1240 g_free(search_term
);
1242 } else { /* prepend "http://" */
1243 new = g_malloc(sizeof("http://") + len
);
1244 strcpy(new, "http://");
1245 memcpy(&new[sizeof("http://") - 1], s
, len
+ 1);
1248 webkit_web_view_load_uri(client
.gui
.webview
, new);
1251 g_spawn_async(NULL
, argv
, NULL
, G_SPAWN_SEARCH_PATH
, NULL
, NULL
, NULL
, NULL
);
1256 open_remembered(const Arg
*arg
)
1258 Arg a
= {arg
->i
, client
.state
.rememberedURI
};
1260 if (strcmp(client
.state
.rememberedURI
, "")) {
1267 yank(const Arg
*arg
) {
1268 const char *url
, *content
;
1270 if (arg
->i
& SourceSelection
) {
1271 webkit_web_view_copy_clipboard(client
.gui
.webview
);
1272 if (arg
->i
& ClipboardPrimary
)
1273 content
= gtk_clipboard_wait_for_text(client
.state
.clipboards
[0]);
1274 if (!content
&& arg
->i
& ClipboardGTK
)
1275 content
= gtk_clipboard_wait_for_text(client
.state
.clipboards
[1]);
1277 echo_message(Info
, "Yanked %s", content
);
1278 g_free((gpointer
*)content
);
1281 if (arg
->i
& SourceURL
) {
1282 url
= webkit_web_view_get_uri(client
.gui
.webview
);
1289 echo_message(Info
, "Yanked %s", url
);
1290 if (arg
->i
& ClipboardPrimary
)
1291 gtk_clipboard_set_text(client
.state
.clipboards
[0], url
, -1);
1292 if (arg
->i
& ClipboardGTK
)
1293 gtk_clipboard_set_text(client
.state
.clipboards
[1], url
, -1);
1299 paste(const Arg
*arg
) {
1300 Arg a
= { .i
= arg
->i
& TargetNew
, .s
= NULL
};
1302 /* If we're over a link, open it in a new target. */
1303 if (strlen(client
.state
.rememberedURI
) > 0) {
1304 Arg new_target
= { .i
= TargetNew
, .s
= arg
->s
};
1305 open_arg(&new_target
);
1309 if (arg
->i
& ClipboardPrimary
)
1310 a
.s
= gtk_clipboard_wait_for_text(client
.state
.clipboards
[0]);
1311 if (!a
.s
&& arg
->i
& ClipboardGTK
)
1312 a
.s
= gtk_clipboard_wait_for_text(client
.state
.clipboards
[1]);
1321 quit(const Arg
*arg
) {
1323 const char *filename
;
1324 const char *uri
= webkit_web_view_get_uri(client
.gui
.webview
);
1326 /* write last URL into status file for recreation with "u" */
1327 filename
= g_strdup_printf("%s", client
.config
.config_base
);
1328 filename
= g_strdup_printf(CLOSED_URL_FILENAME
);
1329 f
= fopen(filename
, "w");
1330 g_free((gpointer
*)filename
);
1332 fprintf(f
, "%s", uri
);
1341 revive(const Arg
*arg
) {
1343 const char *filename
;
1344 char buffer
[512] = "";
1345 Arg a
= { .i
= TargetNew
, .s
= NULL
};
1346 /* get the URL of the window which has been closed last */
1347 filename
= g_strdup_printf(CLOSED_URL_FILENAME
);
1348 f
= fopen(filename
, "r");
1349 g_free((gpointer
*)filename
);
1351 fgets(buffer
, 512, f
);
1354 if (strlen(buffer
) > 0) {
1363 gboolean
print_frame(const Arg
*arg
)
1365 WebKitWebFrame
*frame
= webkit_web_view_get_main_frame(client
.gui
.webview
);
1366 webkit_web_frame_print (frame
);
1371 search(const Arg
*arg
) {
1372 State
*state
= &client
.state
;
1373 state
->count
= state
->count
? state
->count
: 1;
1374 gboolean success
, direction
= arg
->i
& DirectionPrev
;
1377 if (state
->search_handle
) {
1378 g_free(state
->search_handle
);
1380 state
->search_handle
= g_strdup(arg
->s
);
1382 if (!state
->search_handle
)
1384 if (arg
->i
& DirectionAbsolute
)
1385 state
->search_direction
= direction
;
1387 direction
^= state
->search_direction
;
1389 success
= webkit_web_view_search_text(client
.gui
.webview
, state
->search_handle
, arg
->i
& CaseSensitive
, direction
, FALSE
);
1391 if (arg
->i
& Wrapping
) {
1392 success
= webkit_web_view_search_text(client
.gui
.webview
, state
->search_handle
, arg
->i
& CaseSensitive
, direction
, TRUE
);
1394 echo_message(Warning
, "search hit %s, continuing at %s",
1395 direction
? "BOTTOM" : "TOP",
1396 direction
? "TOP" : "BOTTOM");
1402 } while(--state
->count
);
1404 echo_message(Error
, "Pattern not found: %s", state
->search_handle
);
1410 set(const Arg
*arg
) {
1413 if (client
.state
.search_handle
) {
1414 g_free(client
.state
.search_handle
);
1415 client
.state
.search_handle
= NULL
;
1416 webkit_web_view_unmark_text_matches(client
.gui
.webview
);
1418 gtk_entry_set_text(GTK_ENTRY(client
.gui
.inputbox
), "");
1419 gtk_widget_grab_focus(GTK_WIDGET(client
.gui
.webview
));
1421 case ModePassThrough
:
1422 echo_message(Info
| NoAutoHide
, "-- PASS THROUGH --");
1425 echo_message(Info
| NoAutoHide
, "-- PASS TROUGH (next) --");
1427 case ModeInsert
: /* should not be called manually but automatically */
1428 echo_message(Info
| NoAutoHide
, "-- INSERT --");
1433 client
.state
.mode
= arg
->i
;
1438 jsapi_ref_to_string(JSContextRef context
, JSValueRef ref
) {
1439 JSStringRef string_ref
;
1443 string_ref
= JSValueToStringCopy(context
, ref
, NULL
);
1444 length
= JSStringGetMaximumUTF8CStringSize(string_ref
);
1445 string
= g_new(gchar
, length
);
1446 JSStringGetUTF8CString(string_ref
, string
, length
);
1447 JSStringRelease(string_ref
);
1452 jsapi_evaluate_script(const gchar
*script
, gchar
**value
, gchar
**message
) {
1453 WebKitWebFrame
*frame
= webkit_web_view_get_main_frame(client
.gui
.webview
);
1454 JSGlobalContextRef context
= webkit_web_frame_get_global_context(frame
);
1456 JSValueRef val
, exception
;
1458 str
= JSStringCreateWithUTF8CString(script
);
1459 val
= JSEvaluateScript(context
, str
, JSContextGetGlobalObject(context
), NULL
, 0, &exception
);
1460 JSStringRelease(str
);
1462 *message
= jsapi_ref_to_string(context
, exception
);
1464 *value
= jsapi_ref_to_string(context
, val
);
1468 quickmark(const Arg
*a
) {
1471 char *fn
= g_strdup_printf(QUICKMARK_FILE
);
1473 fp
= fopen(fn
, "r");
1478 if (fp
!= NULL
&& b
< 10) {
1479 for( i
=0; i
< b
; ++i
) {
1483 fgets(buf
, 100, fp
);
1485 char *ptr
= strrchr(buf
, '\n');
1488 Arg x
= { .s
= buf
};
1489 return open_arg(&x
);
1491 echo_message(Error
, "Quickmark %d not defined", b
);
1494 } else { return false; }
1498 script(const Arg
*arg
) {
1499 gchar
*value
= NULL
, *message
= NULL
;
1500 char text
[BUF_SIZE
] = "";
1502 WebKitNetworkRequest
*request
;
1503 WebKitDownload
*download
;
1506 set_error("Missing argument.");
1509 jsapi_evaluate_script(arg
->s
, &value
, &message
);
1517 if (arg
->i
!= Silent
&& value
) {
1518 echo_message(arg
->i
, value
);
1520 /* switch mode according to scripts return value */
1522 if (strncmp(value
, "done;", 5) == 0) {
1525 } else if (strncmp(value
, "insert;", 7) == 0) {
1528 client
.state
.manual_focus
= TRUE
;
1529 } else if (strncmp(value
, "save;", 5) == 0) {
1530 /* forced download */
1533 request
= webkit_network_request_new((value
+ 5));
1534 download
= webkit_download_new(request
);
1535 webview_download_cb(client
.gui
.webview
, download
, (gpointer
*)NULL
);
1536 } else if (strncmp(value
, "yank;", 5) == 0) {
1537 /* yank link URL to clipboard */
1540 a
.i
= ClipboardPrimary
| ClipboardGTK
;
1543 } else if (strncmp(value
, "colon;", 6) == 0) {
1544 /* use link URL for colon command */
1545 strncpy(text
, (char *)gtk_entry_get_text(GTK_ENTRY(client
.gui
.inputbox
)), 1023);
1550 a
.s
= g_strconcat(":open ", (value
+ 6), NULL
);
1553 a
.s
= g_strconcat(":tabopen ", (value
+ 6), NULL
);
1560 } else if (strncmp(value
, "error;", 6) == 0) {
1570 scroll(const Arg
*arg
) {
1571 GtkAdjustment
*adjust
= (arg
->i
& OrientationHoriz
) ? client
.gui
.adjust_h
: client
.gui
.adjust_v
;
1572 int max
= gtk_adjustment_get_upper(adjust
) - gtk_adjustment_get_page_size(adjust
);
1573 float val
= gtk_adjustment_get_value(adjust
) / max
* 100;
1574 int direction
= (arg
->i
& (1 << 2)) ? 1 : -1;
1575 unsigned int count
= client
.state
.count
;
1577 if ((direction
== 1 && val
< 100) || (direction
== -1 && val
> 0)) {
1578 if (arg
->i
& ScrollMove
)
1579 gtk_adjustment_set_value(adjust
, gtk_adjustment_get_value(adjust
) +
1580 direction
* /* direction */
1581 ((arg
->i
& UnitLine
|| (arg
->i
& UnitBuffer
&& count
)) ? (scrollstep
* (count
? count
: 1)) : (
1582 arg
->i
& UnitBuffer
? gtk_adjustment_get_page_size(adjust
) / 2 :
1583 (count
? count
: 1) * (gtk_adjustment_get_page_size(adjust
) -
1584 (gtk_adjustment_get_page_size(adjust
) > pagingkeep
? pagingkeep
: 0)))));
1586 gtk_adjustment_set_value(adjust
,
1587 ((direction
== 1) ? gtk_adjustment_get_upper
: gtk_adjustment_get_lower
)(adjust
));
1594 zoom(const Arg
*arg
) {
1595 unsigned int count
= client
.state
.count
;
1597 webkit_web_view_set_full_content_zoom(client
.gui
.webview
, (arg
->i
& ZoomFullContent
) > 0);
1598 webkit_web_view_set_zoom_level(client
.gui
.webview
, (arg
->i
& ZoomOut
) ?
1599 webkit_web_view_get_zoom_level(client
.gui
.webview
) +
1600 (((float)(count
? count
: 1)) * (arg
->i
& (1 << 1) ? 1.0 : -1.0) * client
.config
.zoomstep
) :
1601 (count
? (float)count
/ 100.0 : 1.0));
1606 fake_key_event(const Arg
*a
) {
1607 if(!client
.state
.embed
) {
1611 if ( (xdpy
= XOpenDisplay(NULL
)) == NULL
) {
1612 echo_message(Error
, "Couldn't find the XDisplay.");
1618 xk
.subwindow
= None
;
1619 xk
.time
= CurrentTime
;
1620 xk
.same_screen
= True
;
1621 xk
.x
= xk
.y
= xk
.x_root
= xk
.y_root
= 1;
1622 xk
.window
= client
.state
.embed
;
1626 echo_message(Error
, "Zero pointer as argument! Check your config.h");
1631 if( (keysym
= XStringToKeysym(a
->s
)) == NoSymbol
) {
1632 echo_message(Error
, "Couldn't translate %s to keysym", a
->s
);
1636 if( (xk
.keycode
= XKeysymToKeycode(xdpy
, keysym
)) == NoSymbol
) {
1637 echo_message(Error
, "Couldn't translate keysym to keycode");
1642 if( !XSendEvent(xdpy
, client
.state
.embed
, True
, KeyPressMask
, (XEvent
*)&xk
) ) {
1643 echo_message(Error
, "XSendEvent failed");
1652 commandhistoryfetch(const Arg
*arg
) {
1653 State
*state
= &client
.state
;
1654 const int length
= g_list_length(client
.state
.commandhistory
);
1655 gchar
*input_message
= NULL
;
1658 if (arg
->i
== DirectionPrev
) {
1659 state
->commandpointer
= (length
+ state
->commandpointer
- 1) % length
;
1661 state
->commandpointer
= (length
+ state
->commandpointer
+ 1) % length
;
1664 const char* command
= (char *)g_list_nth_data(state
->commandhistory
, state
->commandpointer
);
1665 input_message
= g_strconcat(":", command
, NULL
);
1666 gtk_entry_set_text(GTK_ENTRY(client
.gui
.inputbox
), input_message
);
1667 g_free(input_message
);
1668 gtk_editable_set_position(GTK_EDITABLE(client
.gui
.inputbox
), -1);
1676 bookmark(const Arg
*arg
) {
1678 const char *filename
;
1679 const char *uri
= webkit_web_view_get_uri(client
.gui
.webview
);
1680 const char *title
= webkit_web_view_get_title(client
.gui
.webview
);
1681 filename
= g_strdup_printf(BOOKMARKS_STORAGE_FILENAME
);
1682 f
= fopen(filename
, "a");
1683 g_free((gpointer
*)filename
);
1684 if (uri
== NULL
|| strlen(uri
) == 0) {
1685 set_error("No URI found to bookmark.");
1689 fprintf(f
, "%s", uri
);
1690 if (title
!= NULL
) {
1691 fprintf(f
, "%s", " ");
1692 fprintf(f
, "%s", title
);
1694 if (arg
->s
&& strlen(arg
->s
)) {
1695 build_taglist(arg
, f
);
1697 fprintf(f
, "%s", "\n");
1699 echo_message(Info
, "Bookmark saved");
1702 set_error("Bookmarks file not found.");
1710 const char *filename
;
1711 const char *uri
= webkit_web_view_get_uri(client
.gui
.webview
);
1712 const char *title
= webkit_web_view_get_title(client
.gui
.webview
);
1713 char *entry
, buffer
[512], *new;
1715 gboolean finished
= FALSE
;
1717 if (title
!= NULL
) {
1718 entry
= malloc((strlen(uri
) + strlen(title
) + 2) * sizeof(char));
1719 memset(entry
, 0, strlen(uri
) + strlen(title
) + 2);
1721 entry
= malloc((strlen(uri
) + 1) * sizeof(char));
1722 memset(entry
, 0, strlen(uri
) + 1);
1724 if (entry
!= NULL
) {
1725 strncpy(entry
, uri
, strlen(uri
));
1726 if (title
!= NULL
) {
1727 strncat(entry
, " ", 1);
1728 strncat(entry
, title
, strlen(title
));
1731 filename
= g_strdup_printf(HISTORY_STORAGE_FILENAME
);
1732 f
= fopen(filename
, "r");
1734 new = (char *)malloc(HISTORY_MAX_ENTRIES
* 512 * sizeof(char) + 1);
1736 memset(new, 0, HISTORY_MAX_ENTRIES
* 512 * sizeof(char) + 1);
1737 /* newest entries go on top */
1738 strncpy(new, entry
, strlen(entry
));
1739 strncat(new, "\n", 1);
1740 /* retain at most HISTORY_MAX_ENTIRES - 1 old entries */
1741 while (finished
!= TRUE
) {
1742 if ((char *)NULL
== fgets(buffer
, 512, f
)) {
1743 /* check if end of file was reached / error occured */
1747 /* end of file reached */
1751 /* compare line (-1 because of newline character) */
1752 if (n
!= strlen(buffer
) - 1 || strncmp(entry
, buffer
, n
) != 0) {
1753 /* if the URI is already in history; we put it on top and skip it here */
1754 strncat(new, buffer
, 512);
1757 if ((i
+ 1) >= HISTORY_MAX_ENTRIES
) {
1763 f
= fopen(filename
, "w");
1764 g_free((gpointer
*)filename
);
1766 fprintf(f
, "%s", new);
1775 if (entry
!= NULL
) {
1784 view_source(const Arg
* arg
) {
1785 gboolean current_mode
= webkit_web_view_get_view_source_mode(client
.gui
.webview
);
1786 webkit_web_view_set_view_source_mode(client
.gui
.webview
, !current_mode
);
1787 webkit_web_view_reload(client
.gui
.webview
);
1791 /* open an external editor defined by the protocol handler for
1792 vimprobableedit on a text box or similar */
1794 open_editor(const Arg
*arg
) {
1798 gchar
*value
= NULL
, *message
= NULL
, *tag
= NULL
, *edit_url
= NULL
;
1799 gchar
*temp_file_name
= g_strdup_printf("%s/vimprobableeditXXXXXX",
1801 int temp_file_handle
= -1;
1803 /* check if active element is suitable for text editing */
1804 jsapi_evaluate_script("document.activeElement.tagName", &value
, &message
);
1805 if (value
== NULL
) {
1809 tag
= g_strdup(value
);
1810 if (strcmp(tag
, "INPUT") == 0) {
1811 /* extra check: type == text */
1812 jsapi_evaluate_script("document.activeElement.type", &value
, &message
);
1813 if (strcmp(value
, "text") != 0) {
1820 } else if (strcmp(tag
, "TEXTAREA") != 0) {
1825 jsapi_evaluate_script("document.activeElement.value", &value
, &message
);
1826 text
= g_strdup(value
);
1833 /* write text into temporary file */
1834 temp_file_handle
= mkstemp(temp_file_name
);
1835 if (temp_file_handle
== -1) {
1836 message
= g_strdup_printf("Could not create temporary file: %s",
1838 echo_message(Error
, message
);
1844 if (write(temp_file_handle
, text
, strlen(text
)) != strlen(text
)) {
1845 message
= g_strdup_printf("Short write to temporary file: %s",
1847 echo_message(Error
, message
);
1853 close(temp_file_handle
);
1857 edit_url
= g_strdup_printf("vimprobableedit:%s", temp_file_name
);
1858 success
= open_handler_pid(edit_url
, &child_pid
);
1861 echo_message(Error
, "External editor open failed (no handler for"
1862 " vimprobableedit protocol?)");
1863 unlink(temp_file_name
);
1869 /* mark the active text box as "under processing" */
1870 jsapi_evaluate_script(
1871 "document.activeElement.disabled = true;"
1872 "document.activeElement.originalBackground = "
1873 " document.activeElement.style.background;"
1874 "document.activeElement.style.background = '#aaaaaa';"
1877 g_child_watch_add(child_pid
, _resume_from_editor
, temp_file_name
);
1879 /* temp_file_name is freed in _resume_from_editor */
1887 /* pick up from where open_editor left the work to the glib event loop.
1889 This is called when the external editor exits.
1891 The data argument points to allocated memory containing the temporary file
1894 _resume_from_editor(GPid child_pid
, int child_status
, gpointer data
) {
1896 GString
*set_value_js
= g_string_new(
1897 "document.activeElement.value = \"");
1898 g_spawn_close_pid(child_pid
);
1899 gchar
*value
= NULL
, *message
= NULL
;
1900 gchar
*temp_file_name
= data
;
1901 gchar buffer
[BUF_SIZE
] = "";
1902 gchar
*buf_ptr
= buffer
;
1905 jsapi_evaluate_script(
1906 "document.activeElement.disabled = true;"
1907 "document.activeElement.style.background = '#aaaaaa';"
1913 echo_message(Error
, "External editor returned with non-zero status,"
1914 " discarding edits.");
1918 /* re-read the new contents of the file and put it into the HTML element */
1919 if (!access(temp_file_name
, R_OK
) == 0) {
1920 message
= g_strdup_printf("Could not access temporary file: %s",
1924 fp
= fopen(temp_file_name
, "r");
1926 /* this would be too weird to even emit an error message */
1929 jsapi_evaluate_script("document.activeElement.value = '';",
1934 while (EOF
!= (char_read
= fgetc(fp
))) {
1935 if (char_read
== '\n') {
1938 } else if (char_read
== '"') {
1942 *buf_ptr
++ = char_read
;
1944 /* ship out as the buffer when space gets tight. This has
1945 fuzz to save on thinking, plus we have enough space for the
1946 trailing "; in any case. */
1947 if (buf_ptr
-buffer
>=BUF_SIZE
-10) {
1949 g_string_append(set_value_js
, buffer
);
1956 g_string_append(set_value_js
, buffer
);
1959 jsapi_evaluate_script(set_value_js
->str
, &value
, &message
);
1963 /* Fall through, error and normal exit are identical */
1965 jsapi_evaluate_script(
1966 "document.activeElement.disabled = false;"
1967 "document.activeElement.style.background ="
1968 " document.activeElement.originalBackground;"
1971 g_string_free(set_value_js
, TRUE
);
1972 unlink(temp_file_name
);
1973 g_free(temp_file_name
);
1979 focus_input(const Arg
*arg
) {
1982 a
.s
= g_strdup("hints.focusInput();");
1987 client
.state
.manual_focus
= TRUE
;
1995 a
.s
= g_strdup("hints.clearFocus();");
2005 browser_settings(const Arg
*arg
) {
2008 set_error("Missing argument.");
2011 strncpy(line
, arg
->s
, 254);
2012 if (process_set_line(line
))
2015 set_error("Invalid setting.");
2021 search_word(int whichword
) {
2023 static char word
[240];
2024 char *c
= my_pair
.line
;
2026 while (isspace(*c
) && *c
)
2029 switch (whichword
) {
2031 while (*c
&& !isspace (*c
) && *c
!= '=' && k
< 240) {
2036 strncpy(my_pair
.what
, word
, 20);
2039 while (*c
&& k
< 240) {
2044 strncpy(my_pair
.value
, word
, 240);
2052 process_set_line(char *line
) {
2056 WebKitWebSettings
*settings
;
2058 settings
= webkit_web_view_get_settings(client
.gui
.webview
);
2059 my_pair
.line
= line
;
2061 if (!strlen(my_pair
.what
))
2064 while (isspace(*c
) && *c
)
2067 if (*c
== ':' || *c
== '=')
2073 listlen
= LENGTH(browsersettings
);
2074 for (i
= 0; i
< listlen
; i
++) {
2075 if (strlen(browsersettings
[i
].name
) == strlen(my_pair
.what
) && strncmp(browsersettings
[i
].name
, my_pair
.what
, strlen(my_pair
.what
)) == 0) {
2076 /* mandatory argument not provided */
2077 if (strlen(my_pair
.value
) == 0)
2079 /* process qmark? */
2080 if (strlen(my_pair
.what
) == 5 && strncmp("qmark", my_pair
.what
, 5) == 0) {
2081 return (process_save_qmark(my_pair
.value
, client
.gui
.webview
));
2083 /* interpret boolean values */
2084 if (browsersettings
[i
].boolval
) {
2085 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) {
2087 } 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) {
2092 } else if (browsersettings
[i
].colourval
) {
2093 /* interpret as hexadecimal colour */
2094 if (!parse_colour(my_pair
.value
)) {
2098 if (browsersettings
[i
].var
!= NULL
) {
2099 strncpy(browsersettings
[i
].var
, my_pair
.value
, MAX_SETTING_SIZE
);
2100 if (strlen(my_pair
.value
) > MAX_SETTING_SIZE
- 1) {
2101 /* in this case, \0 will not have been copied */
2102 browsersettings
[i
].var
[MAX_SETTING_SIZE
- 1] = '\0';
2103 /* in case this string is also used for a webkit setting, make sure it's consistent */
2104 my_pair
.value
[MAX_SETTING_SIZE
- 1] = '\0';
2105 echo_message(Info
, "String too long; automatically truncated!");
2108 if (strlen(browsersettings
[i
].webkit
) > 0) {
2109 /* activate appropriate webkit setting */
2110 if (browsersettings
[i
].boolval
) {
2111 g_object_set((GObject
*)settings
, browsersettings
[i
].webkit
, boolval
, NULL
);
2112 } else if (browsersettings
[i
].intval
) {
2113 g_object_set((GObject
*)settings
, browsersettings
[i
].webkit
, atoi(my_pair
.value
), NULL
);
2115 g_object_set((GObject
*)settings
, browsersettings
[i
].webkit
, my_pair
.value
, NULL
);
2117 webkit_web_view_set_settings(client
.gui
.webview
, settings
);
2120 if (strlen(my_pair
.what
) == 14) {
2121 if (strncmp("acceptlanguage", my_pair
.what
, 14) == 0) {
2122 g_object_set(G_OBJECT(client
.net
.session
), "accept-language", acceptlanguage
, NULL
);
2123 } else if (strncmp("completioncase", my_pair
.what
, 14) == 0) {
2124 complete_case_sensitive
= boolval
;
2126 } else if (strlen(my_pair
.what
) == 5 && strncmp("proxy", my_pair
.what
, 5) == 0) {
2127 toggle_proxy(boolval
);
2128 } else if (strlen(my_pair
.what
) == 10 && strncmp("scrollbars", my_pair
.what
, 10) == 0) {
2129 toggle_scrollbars(boolval
);
2130 } else if (strlen(my_pair
.what
) == 9 && strncmp("statusbar", my_pair
.what
, 9) == 0) {
2131 gtk_widget_set_visible(GTK_WIDGET(client
.gui
.statusbar
), boolval
);
2132 } else if (strlen(my_pair
.what
) == 8 && strncmp("inputbox", my_pair
.what
, 8) == 0) {
2133 gtk_widget_set_visible(client
.gui
.inputbox
, boolval
);
2134 } else if (strlen(my_pair
.what
) == 11 && strncmp("escapeinput", my_pair
.what
, 11) == 0) {
2135 escape_input_on_load
= boolval
;
2138 /* SSL certificate checking */
2139 if (strlen(my_pair
.what
) == 9 && strncmp("strictssl", my_pair
.what
, 9) == 0) {
2142 g_object_set(G_OBJECT(client
.net
.session
), "ssl-strict", TRUE
, NULL
);
2145 g_object_set(G_OBJECT(client
.net
.session
), "ssl-strict", FALSE
, NULL
);
2148 if (strlen(my_pair
.what
) == 8 && strncmp("cabundle", my_pair
.what
, 8) == 0) {
2149 g_object_set(G_OBJECT(client
.net
.session
), SOUP_SESSION_SSL_CA_FILE
, ca_bundle
, NULL
);
2151 if (strlen(my_pair
.what
) == 10 && strncmp("windowsize", my_pair
.what
, 10) == 0) {
2152 set_default_winsize(my_pair
.value
);
2156 if (browsersettings
[i
].reload
)
2157 webkit_web_view_reload(client
.gui
.webview
);
2165 process_line(char *line
) {
2166 char *c
= line
, *command_hist
;
2168 size_t len
, length
= strlen(line
);
2169 gboolean found
= FALSE
, success
= FALSE
;
2175 /* Ignore blank lines. */
2179 command_hist
= g_strdup(c
);
2181 /* check for colon command aliases first */
2182 for (l
= client
.config
.colon_aliases
; l
; l
= g_list_next(l
)) {
2183 Alias
*alias
= (Alias
*)l
->data
;
2184 if (length
== strlen(alias
->alias
) && strncmp(alias
->alias
, line
, length
) == 0) {
2185 /* reroute to target command */
2187 length
= strlen(alias
->target
);
2192 /* check standard commands */
2193 for (i
= 0; i
< LENGTH(commands
); i
++) {
2194 if (commands
[i
].cmd
== NULL
)
2196 len
= strlen(commands
[i
].cmd
);
2197 if (length
>= len
&& !strncmp(c
, commands
[i
].cmd
, len
) && (c
[len
] == ' ' || !c
[len
])) {
2199 a
.i
= commands
[i
].arg
.i
;
2200 a
.s
= g_strdup(length
> len
+ 1 ? &c
[len
+ 1] : commands
[i
].arg
.s
);
2201 success
= commands
[i
].func(&a
);
2207 save_command_history(command_hist
);
2208 g_free(command_hist
);
2211 echo_message(Error
, "Not a browser command: %s", c
);
2212 } else if (!success
) {
2213 if (client
.state
.error_msg
!= NULL
) {
2214 echo_message(Error
, client
.state
.error_msg
);
2215 g_free(client
.state
.error_msg
);
2216 client
.state
.error_msg
= NULL
;
2218 echo_message(Error
, "Unknown error. Please file a bug report!");
2225 search_tag(const Arg
* a
) {
2227 const char *filename
;
2228 const char *tag
= a
->s
;
2229 char s
[BUFFERSIZE
], foundtag
[40], url
[BUFFERSIZE
];
2233 /* The user must give us something to load up. */
2234 set_error("Bookmark tag required with this option.");
2238 if (strlen(tag
) > MAXTAGSIZE
) {
2239 set_error("Tag too long.");
2243 filename
= g_strdup_printf(BOOKMARKS_STORAGE_FILENAME
);
2244 f
= fopen(filename
, "r");
2245 g_free((gpointer
*)filename
);
2247 set_error("Couldn't open bookmarks file.");
2250 while (fgets(s
, BUFFERSIZE
-1, f
)) {
2253 while (isspace(s
[t
]))
2255 if (s
[t
] != ']') continue;
2268 foundtag
[i
++] = s
[k
++];
2270 /* foundtag now contains the tag */
2271 if (strlen(foundtag
) < MAXTAGSIZE
&& strcmp(tag
, foundtag
) == 0) {
2273 while (isspace(s
[i
])) i
++;
2275 while (s
[i
] && !isspace(s
[i
])) url
[k
++] = s
[i
++];
2277 Arg x
= { .i
= TargetNew
, .s
= url
};
2291 toggle_proxy(gboolean onoff
) {
2293 char *filename
, *new;
2295 if (onoff
== FALSE
) {
2296 g_object_set(client
.net
.session
, "proxy-uri", NULL
, NULL
);
2298 filename
= (char *)g_getenv("http_proxy");
2300 /* Fallthrough to checking HTTP_PROXY as well, since this can also be
2303 if (filename
== NULL
)
2304 filename
= (char *)g_getenv("HTTP_PROXY");
2306 if (filename
!= NULL
&& 0 < strlen(filename
)) {
2307 new = g_strrstr(filename
, "http://") ? g_strdup(filename
) : g_strdup_printf("http://%s", filename
);
2308 proxy_uri
= soup_uri_new(new);
2310 g_object_set(client
.net
.session
, "proxy-uri", proxy_uri
, NULL
);
2312 soup_uri_free(proxy_uri
);
2319 toggle_scrollbars(gboolean onoff
) {
2320 Gui
*gui
= &client
.gui
;
2321 if (onoff
== TRUE
) {
2322 gui
->adjust_h
= gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(gui
->viewport
));
2323 gui
->adjust_v
= gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(gui
->viewport
));
2324 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gui
->viewport
), GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
2326 gui
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(gui
->scroll_v
));
2327 gui
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(gui
->scroll_h
));
2328 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gui
->viewport
), GTK_POLICY_NEVER
, GTK_POLICY_NEVER
);
2330 gtk_widget_set_scroll_adjustments (GTK_WIDGET(gui
->webview
), gui
->adjust_h
, gui
->adjust_v
);
2335 void set_default_winsize(const char * const size
) {
2337 int x
= 640, y
= 480;
2339 x
= strtol(size
, &p
, 10);
2340 if (errno
== ERANGE
|| x
<= 0) {
2345 if (p
== size
|| strlen(size
) == p
- size
)
2348 y
= strtol(p
+ 1, NULL
, 10);
2349 if (errno
== ERANGE
|| y
<= 0)
2353 gtk_window_resize(GTK_WINDOW(client
.gui
.window
), x
, y
);
2357 update_url(const char *uri
) {
2358 Gui
*gui
= &client
.gui
;
2359 gboolean ssl
= g_str_has_prefix(uri
, "https://");
2361 WebKitWebFrame
*frame
;
2362 WebKitWebDataSource
*src
;
2363 WebKitNetworkRequest
*request
;
2366 char *sslactivecolor
;
2368 #ifdef ENABLE_HISTORY_INDICATOR
2369 char before
[] = " [";
2371 gboolean back
= webkit_web_view_can_go_back(gui
->webview
);
2372 gboolean fwd
= webkit_web_view_can_go_forward(gui
->webview
);
2375 before
[0] = after
[0] = '\0';
2377 markup
= g_markup_printf_escaped(
2378 #ifdef ENABLE_HISTORY_INDICATOR
2379 "<span font=\"%s\">%s%s%s%s%s</span>", statusfont
, uri
,
2380 before
, back
? "+" : "", fwd
? "-" : "", after
2382 "<span font=\"%s\">%s</span>", statusfont
, uri
2385 gtk_label_set_markup(GTK_LABEL(gui
->status_url
), markup
);
2388 frame
= webkit_web_view_get_main_frame(gui
->webview
);
2389 src
= webkit_web_frame_get_data_source(frame
);
2390 request
= webkit_web_data_source_get_request(src
);
2391 msg
= webkit_network_request_get_message(request
);
2392 ssl_ok
= soup_message_get_flags(msg
) & SOUP_MESSAGE_CERTIFICATE_TRUSTED
;
2394 sslactivecolor
= sslbgcolor
;
2396 sslactivecolor
= sslinvalidbgcolor
;
2398 gdk_color_parse(ssl
? sslactivecolor
: statusbgcolor
, &color
);
2399 gtk_widget_modify_bg(gui
->eventbox
, GTK_STATE_NORMAL
, &color
);
2400 gdk_color_parse(ssl
? sslcolor
: statuscolor
, &color
);
2401 gtk_widget_modify_fg(GTK_WIDGET(gui
->status_url
), GTK_STATE_NORMAL
, &color
);
2402 gtk_widget_modify_fg(GTK_WIDGET(gui
->status_state
), GTK_STATE_NORMAL
, &color
);
2407 State
* state
= &client
.state
;
2409 int download_count
= g_list_length(state
->activeDownloads
);
2410 GString
*status
= g_string_new("");
2412 /* construct the status line */
2414 /* count, modkey and input buffer */
2415 g_string_append_printf(status
, "%.0d", state
->count
);
2416 if (state
->current_modkey
) g_string_append_c(status
, state
->current_modkey
);
2418 /* the number of active downloads */
2419 if (state
->activeDownloads
) {
2420 g_string_append_printf(status
, " %d active %s", download_count
,
2421 (download_count
== 1) ? "download" : "downloads");
2424 #ifdef ENABLE_WGET_PROGRESS_BAR
2425 /* the progressbar */
2428 char progressbar
[progressbartick
+ 1];
2430 if (state
->activeDownloads
) {
2434 for (ptr
= state
->activeDownloads
; ptr
; ptr
= g_list_next(ptr
)) {
2435 progress
+= 100 * webkit_download_get_progress(ptr
->data
);
2438 progress
/= download_count
;
2440 } else if (webkit_web_view_get_load_status(client
.gui
.webview
) != WEBKIT_LOAD_FINISHED
2441 && webkit_web_view_get_load_status(client
.gui
.webview
) != WEBKIT_LOAD_FAILED
) {
2443 progress
= webkit_web_view_get_progress(client
.gui
.webview
) * 100;
2446 if (progress
>= 0) {
2447 ascii_bar(progressbartick
, progress
* progressbartick
/ 100, progressbar
);
2448 g_string_append_printf(status
, " %c%s%c",
2449 progressborderleft
, progressbar
, progressborderright
);
2454 /* and the current scroll position */
2456 int max
= gtk_adjustment_get_upper(client
.gui
.adjust_v
) - gtk_adjustment_get_page_size(client
.gui
.adjust_v
);
2457 int val
= (int)(gtk_adjustment_get_value(client
.gui
.adjust_v
) / max
* 100);
2460 g_string_append(status
, " All");
2462 g_string_append(status
, " Top");
2463 else if (val
== 100)
2464 g_string_append(status
, " Bot");
2466 g_string_append_printf(status
, " %d%%", val
);
2470 markup
= g_markup_printf_escaped("<span font=\"%s\">%s</span>", statusfont
, status
->str
);
2471 gtk_label_set_markup(GTK_LABEL(client
.gui
.status_state
), markup
);
2474 g_string_free(status
, TRUE
);
2478 setup_client(void) {
2479 State
*state
= &client
.state
;
2480 Config
*config
= &client
.config
;
2482 state
->mode
= ModeNormal
;
2484 state
->rememberedURI
[0] = '\0';
2485 state
->manual_focus
= FALSE
;
2486 state
->commandhistory
= NULL
;
2487 state
->commandpointer
= 0;
2489 config
->colon_aliases
= NULL
;
2490 config
->cookie_timeout
= 4800;
2496 client
.config
.modkeys
= calloc(LENGTH(keys
) + 1, sizeof(char));
2497 char *ptr
= client
.config
.modkeys
;
2499 for (i
= 0; i
< LENGTH(keys
); i
++)
2500 if (keys
[i
].modkey
&& !strchr(client
.config
.modkeys
, keys
[i
].modkey
))
2501 *(ptr
++) = keys
[i
].modkey
;
2502 client
.config
.modkeys
= realloc(client
.config
.modkeys
, &ptr
[0] - &client
.config
.modkeys
[0] + 1);
2507 Gui
*gui
= &client
.gui
;
2508 State
*state
= &client
.state
;
2510 gui
->scroll_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
2511 gui
->scroll_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
2512 gui
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(gui
->scroll_h
));
2513 gui
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(gui
->scroll_v
));
2514 if (client
.state
.embed
) {
2515 gui
->window
= GTK_WINDOW(gtk_plug_new(client
.state
.embed
));
2517 gui
->window
= GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL
));
2518 gtk_window_set_wmclass(GTK_WINDOW(gui
->window
), "vimprobable2", "Vimprobable2");
2520 gtk_window_set_default_size(GTK_WINDOW(gui
->window
), 640, 480);
2521 gui
->box
= GTK_BOX(gtk_vbox_new(FALSE
, 0));
2522 gui
->inputbox
= gtk_entry_new();
2523 gui
->webview
= (WebKitWebView
*)webkit_web_view_new();
2524 gui
->statusbar
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
2525 gui
->eventbox
= gtk_event_box_new();
2526 gui
->status_url
= gtk_label_new(NULL
);
2527 gui
->status_state
= gtk_label_new(NULL
);
2529 PangoFontDescription
*font
;
2530 GdkGeometry hints
= { 1, 1 };
2531 gui
->inspector
= webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(gui
->webview
));
2533 state
->clipboards
[0] = gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2534 state
->clipboards
[1] = gtk_clipboard_get(GDK_NONE
);
2536 gdk_color_parse(statusbgcolor
, &bg
);
2537 gtk_widget_modify_bg(gui
->eventbox
, GTK_STATE_NORMAL
, &bg
);
2538 gtk_widget_set_name(GTK_WIDGET(gui
->window
), "Vimprobable2");
2539 gtk_window_set_geometry_hints(gui
->window
, NULL
, &hints
, GDK_HINT_MIN_SIZE
);
2541 state
->keymap
= gdk_keymap_get_default();
2543 #ifdef DISABLE_SCROLLBAR
2544 gui
->viewport
= gtk_scrolled_window_new(NULL
, NULL
);
2545 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gui
->viewport
), GTK_POLICY_NEVER
, GTK_POLICY_NEVER
);
2547 /* Ensure we still see scrollbars. */
2548 gui
->viewport
= gtk_scrolled_window_new(gui
->adjust_h
, gui
->adjust_v
);
2552 gtk_container_add(GTK_CONTAINER(gui
->viewport
), GTK_WIDGET(gui
->webview
));
2554 /* Ensure we set the scroll adjustments now, so that if we're not drawing
2555 * titlebars, we can still scroll.
2557 gtk_widget_set_scroll_adjustments(GTK_WIDGET(gui
->webview
), gui
->adjust_h
, gui
->adjust_v
);
2559 font
= pango_font_description_from_string(urlboxfont
[0]);
2560 gtk_widget_modify_font(GTK_WIDGET(gui
->inputbox
), font
);
2561 pango_font_description_free(font
);
2562 gtk_entry_set_inner_border(GTK_ENTRY(gui
->inputbox
), NULL
);
2563 gtk_misc_set_alignment(GTK_MISC(gui
->status_url
), 0.0, 0.0);
2564 gtk_misc_set_alignment(GTK_MISC(gui
->status_state
), 1.0, 0.0);
2565 gtk_box_pack_start(gui
->statusbar
, gui
->status_url
, TRUE
, TRUE
, 2);
2566 gtk_box_pack_start(gui
->statusbar
, gui
->status_state
, FALSE
, FALSE
, 2);
2567 gtk_container_add(GTK_CONTAINER(gui
->eventbox
), GTK_WIDGET(gui
->statusbar
));
2568 gtk_box_pack_start(gui
->box
, gui
->viewport
, TRUE
, TRUE
, 0);
2569 gtk_box_pack_start(gui
->box
, gui
->eventbox
, FALSE
, FALSE
, 0);
2570 gtk_entry_set_has_frame(GTK_ENTRY(gui
->inputbox
), FALSE
);
2571 gtk_box_pack_end(gui
->box
, gui
->inputbox
, FALSE
, FALSE
, 0);
2572 gtk_container_add(GTK_CONTAINER(gui
->window
), GTK_WIDGET(gui
->box
));
2573 gtk_widget_grab_focus(GTK_WIDGET(gui
->webview
));
2574 gtk_widget_show_all(GTK_WIDGET(gui
->window
));
2575 set_widget_font_and_color(gui
->inputbox
, urlboxfont
[0], urlboxbgcolor
[0], urlboxcolor
[0]);
2576 g_object_set(gtk_widget_get_settings(gui
->inputbox
), "gtk-entry-select-on-focus", FALSE
, NULL
);
2581 WebKitWebSettings
*settings
= (WebKitWebSettings
*)webkit_web_settings_new();
2582 char *filename
, *file_url
;
2584 client
.net
.session
= webkit_get_default_session();
2585 g_object_set(G_OBJECT(client
.net
.session
), "ssl-ca-file", ca_bundle
, NULL
);
2586 g_object_set(G_OBJECT(client
.net
.session
), "ssl-strict", strict_ssl
, NULL
);
2587 g_object_set(G_OBJECT(settings
), "default-font-size", DEFAULT_FONT_SIZE
, NULL
);
2588 g_object_set(G_OBJECT(settings
), "enable-scripts", enablePlugins
, NULL
);
2589 g_object_set(G_OBJECT(settings
), "enable-plugins", enablePlugins
, NULL
);
2590 g_object_set(G_OBJECT(settings
), "enable-java-applet", enableJava
, NULL
);
2591 g_object_set(G_OBJECT(settings
), "enable-page-cache", enablePagecache
, NULL
);
2592 filename
= g_strdup_printf(USER_STYLESHEET
);
2593 file_url
= g_strdup_printf("file://%s", filename
);
2594 g_object_set(G_OBJECT(settings
), "user-stylesheet-uri", file_url
, NULL
);
2597 g_object_set(G_OBJECT(settings
), "user-agent", useragent
, NULL
);
2598 g_object_get(G_OBJECT(settings
), "zoom-step", &client
.config
.zoomstep
, NULL
);
2599 webkit_web_view_set_settings(client
.gui
.webview
, settings
);
2602 toggle_proxy(use_proxy
);
2607 WebKitWebFrame
*frame
= webkit_web_view_get_main_frame(client
.gui
.webview
);
2608 #ifdef ENABLE_COOKIE_SUPPORT
2610 g_signal_connect_after(G_OBJECT(client
.net
.session
), "request-started", G_CALLBACK(new_generic_request
), NULL
);
2612 /* Accept-language header */
2613 g_object_set(G_OBJECT(client
.net
.session
), "accept-language", acceptlanguage
, NULL
);
2615 g_object_connect(G_OBJECT(client
.gui
.window
),
2616 "signal::destroy", G_CALLBACK(window_destroyed_cb
), NULL
,
2619 g_signal_connect(G_OBJECT(frame
),
2620 "scrollbars-policy-changed", G_CALLBACK(blank_cb
), NULL
);
2622 g_object_connect(G_OBJECT(client
.gui
.webview
),
2623 "signal::title-changed", G_CALLBACK(webview_title_changed_cb
), NULL
,
2624 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), NULL
,
2625 "signal::load-committed", G_CALLBACK(webview_load_committed_cb
), NULL
,
2626 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), NULL
,
2627 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_navigation_cb
), NULL
,
2628 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_new_window_cb
), NULL
,
2629 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), NULL
,
2630 "signal::download-requested", G_CALLBACK(webview_download_cb
), NULL
,
2631 "signal::key-press-event", G_CALLBACK(webview_keypress_cb
), NULL
,
2632 "signal::hovering-over-link", G_CALLBACK(webview_hoverlink_cb
), NULL
,
2633 "signal::console-message", G_CALLBACK(webview_console_cb
), NULL
,
2634 "signal::create-web-view", G_CALLBACK(webview_open_in_new_window_cb
), NULL
,
2635 "signal::event", G_CALLBACK(notify_event_cb
), NULL
,
2637 /* webview adjustment */
2638 g_object_connect(G_OBJECT(client
.gui
.adjust_v
),
2639 "signal::value-changed", G_CALLBACK(webview_scroll_cb
), NULL
,
2642 g_object_connect(G_OBJECT(client
.gui
.inputbox
),
2643 "signal::activate", G_CALLBACK(inputbox_activate_cb
), NULL
,
2644 "signal::key-press-event", G_CALLBACK(inputbox_keypress_cb
), NULL
,
2645 "signal::key-release-event", G_CALLBACK(inputbox_keyrelease_cb
), NULL
,
2646 "signal::changed", G_CALLBACK(inputbox_changed_cb
), NULL
,
2649 g_signal_connect(G_OBJECT(client
.gui
.inspector
),
2650 "inspect-web-view", G_CALLBACK(inspector_inspect_web_view_cb
), NULL
);
2653 #ifdef ENABLE_USER_SCRIPTFILE
2655 scripts_run_user_file() {
2656 gchar
*js
= NULL
, *user_scriptfile
= NULL
;
2657 GError
*error
= NULL
;
2659 user_scriptfile
= g_strdup_printf(USER_SCRIPTFILE
);
2661 /* run the users script file */
2662 if (g_file_test(user_scriptfile
, G_FILE_TEST_IS_REGULAR
)
2663 && g_file_get_contents(user_scriptfile
, &js
, NULL
, &error
)) {
2665 gchar
*value
= NULL
, *message
= NULL
;
2667 jsapi_evaluate_script(js
, &value
, &message
);
2670 fprintf(stderr
, "%s", message
);
2675 fprintf(stderr
, "Cannot open %s: %s\n", user_scriptfile
, error
? error
->message
: "file not found");
2678 g_free(user_scriptfile
);
2682 #ifdef ENABLE_COOKIE_SUPPORT
2686 Network
*net
= &client
.net
;
2687 if (net
->file_cookie_jar
)
2688 g_object_unref(net
->file_cookie_jar
);
2690 if (net
->session_cookie_jar
)
2691 g_object_unref(net
->session_cookie_jar
);
2693 net
->session_cookie_jar
= soup_cookie_jar_new();
2695 net
->cookie_store
= g_strdup_printf(COOKIES_STORAGE_FILENAME
);
2699 g_signal_connect(G_OBJECT(net
->file_cookie_jar
), "changed",
2700 G_CALLBACK(update_cookie_jar
), NULL
);
2703 /* TA: XXX - we should be using this callback for any header-requests we
2704 * receive (hence the name "new_generic_request" -- but for now, its use
2705 * is limited to handling cookies.
2708 new_generic_request(SoupSession
*session
, SoupMessage
*soup_msg
, gpointer unused
)
2710 SoupMessageHeaders
*soup_msg_h
;
2714 soup_msg_h
= soup_msg
->request_headers
;
2715 soup_message_headers_remove(soup_msg_h
, "Cookie");
2716 uri
= soup_message_get_uri(soup_msg
);
2717 if ((cookie_str
= get_cookies(uri
))) {
2718 soup_message_headers_append(soup_msg_h
, "Cookie", cookie_str
);
2722 g_signal_connect_after(G_OBJECT(soup_msg
), "got-headers", G_CALLBACK(handle_cookie_request
), NULL
);
2728 get_cookies(SoupURI
*soup_uri
) {
2731 cookie_str
= soup_cookie_jar_get_cookies(client
.net
.file_cookie_jar
, soup_uri
, TRUE
);
2737 handle_cookie_request(SoupMessage
*soup_msg
, gpointer unused
)
2739 GSList
*resp_cookie
= NULL
, *cookie_list
;
2742 cookie_list
= soup_cookies_from_response(soup_msg
);
2743 for(resp_cookie
= cookie_list
; resp_cookie
; resp_cookie
= g_slist_next(resp_cookie
))
2745 SoupDate
*soup_date
;
2746 cookie
= soup_cookie_copy((SoupCookie
*)resp_cookie
->data
);
2748 if (client
.config
.cookie_timeout
&& cookie
->expires
== NULL
) {
2749 soup_date
= soup_date_new_from_time_t(time(NULL
) + client
.config
.cookie_timeout
* 10);
2750 soup_cookie_set_expires(cookie
, soup_date
);
2751 soup_date_free(soup_date
);
2753 soup_cookie_jar_add_cookie(client
.net
.file_cookie_jar
, cookie
);
2756 soup_cookies_free(cookie_list
);
2762 update_cookie_jar(SoupCookieJar
*jar
, SoupCookie
*old
, SoupCookie
*new)
2765 /* Nothing to do. */
2770 copy
= soup_cookie_copy(new);
2772 soup_cookie_jar_add_cookie(client
.net
.session_cookie_jar
, copy
);
2778 load_all_cookies(void)
2780 Network
*net
= &client
.net
;
2781 GSList
*cookie_list
;
2782 net
->file_cookie_jar
= soup_cookie_jar_text_new(net
->cookie_store
, COOKIES_STORAGE_READONLY
);
2784 /* Put them back in the session store. */
2785 GSList
*cookies_from_file
= soup_cookie_jar_all_cookies(net
->file_cookie_jar
);
2786 cookie_list
= cookies_from_file
;
2788 for (; cookies_from_file
;
2789 cookies_from_file
= cookies_from_file
->next
)
2791 soup_cookie_jar_add_cookie(net
->session_cookie_jar
, cookies_from_file
->data
);
2794 soup_cookies_free(cookies_from_file
);
2795 g_slist_free(cookie_list
);
2804 /* Free up any nasty globals before exiting. */
2805 #ifdef ENABLE_COOKIE_SUPPORT
2806 if (client
.net
.cookie_store
)
2807 g_free(client
.net
.cookie_store
);
2813 main(int argc
, char *argv
[]) {
2815 static char url
[256] = "";
2816 static gboolean ver
= false;
2817 static gboolean configfile_exists
= FALSE
;
2818 static const char *cfile
= NULL
;
2819 static gchar
*winid
= NULL
;
2820 static GOptionEntry opts
[] = {
2821 { "version", 'v', 0, G_OPTION_ARG_NONE
, &ver
, "print version", NULL
},
2822 { "embed", 'e', 0, G_OPTION_ARG_STRING
, &winid
, "embedded", NULL
},
2823 { "configfile", 'c', 0, G_OPTION_ARG_STRING
, &cfile
, "config file", NULL
},
2828 Config
*config
= &client
.config
;
2830 /* command line argument: version */
2831 if (!gtk_init_with_args(&argc
, &argv
, "[<uri>]", opts
, NULL
, &err
)) {
2832 g_printerr("can't init gtk: %s\n", err
->message
);
2834 return EXIT_FAILURE
;
2838 printf("%s\n", INTERNAL_VERSION
);
2839 return EXIT_SUCCESS
;
2844 if (getenv("TMPDIR")) {
2845 strncpy(temp_dir
, getenv("TMPDIR"), MAX_SETTING_SIZE
);
2846 temp_dir
[MAX_SETTING_SIZE
-1] = 0;
2849 if( getenv("XDG_CONFIG_HOME") )
2850 config
->config_base
= g_strdup_printf("%s", getenv("XDG_CONFIG_HOME"));
2852 config
->config_base
= g_strdup_printf("%s/.config/", getenv("HOME"));
2855 config
->configfile
= g_strdup(cfile
);
2857 config
->configfile
= g_strdup_printf(RCFILE
);
2859 if (!g_thread_supported())
2860 g_thread_init(NULL
);
2863 if (strncmp(winid
, "0x", 2) == 0) {
2864 client
.state
.embed
= strtol(winid
, NULL
, 16);
2866 client
.state
.embed
= atoi(winid
);
2873 #ifdef ENABLE_COOKIE_SUPPORT
2877 make_searchengines_list(searchengines
, LENGTH(searchengines
));
2878 make_uri_handlers_list(uri_handlers
, LENGTH(uri_handlers
));
2880 /* Check if the specified file exists. */
2881 /* And only warn the user, if they explicitly asked for a config on the
2884 if (!(access(config
->configfile
, F_OK
) == 0) && cfile
) {
2885 echo_message(Info
, "Config file '%s' doesn't exist", cfile
);
2886 } else if ((access(config
->configfile
, F_OK
) == 0))
2887 configfile_exists
= true;
2889 /* read config file */
2890 /* But only report errors if we failed, and the file existed. */
2891 if ((SUCCESS
!= read_rcfile(config
->configfile
)) && configfile_exists
) {
2892 echo_message(Error
, "Error in config file '%s'", config
->configfile
);
2893 g_free(config
->configfile
);
2896 /* command line argument: URL */
2898 strncpy(url
, argv
[argc
- 1], 255);
2900 strncpy(url
, startpage
, 255);
2903 a
.i
= TargetCurrent
;
2910 return EXIT_SUCCESS
;