aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--AUTHORS1
-rw-r--r--docs/formfiller-data-format35
-rw-r--r--examples/config/config4
-rw-r--r--examples/data/scripts/formfiller.js67
-rwxr-xr-xexamples/data/scripts/formfiller.sh324
-rw-r--r--src/callbacks.c32
-rw-r--r--src/io.c7
-rw-r--r--src/util.c176
-rw-r--r--src/util.h6
-rw-r--r--src/uzbl-core.c25
-rw-r--r--src/uzbl-core.h1
-rw-r--r--tests/test-command.c8
12 files changed, 377 insertions, 309 deletions
diff --git a/AUTHORS b/AUTHORS
index 5e7ef07..ff31818 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -75,6 +75,7 @@ In alphabetical order:
Simon Lipp (sloonz) - various patches, EM contributions
Sylvester Johansson (scj) - form filler script & different take on link follower
Tassilo Horn (tsdh) - $VISUAL patch
+ Taylan Ulrich Bayırlı (taylanub) - updated form filler
Thorsten Wilms - logo design
Tom Adams (holizz) - few patches, cookies.py, gtkplug/socket & proof of concept uzbl_tabbed.py, scheme_handler
Uli Schlachter (psychon) - basic mime_policy_cb & Makefile patch
diff --git a/docs/formfiller-data-format b/docs/formfiller-data-format
new file mode 100644
index 0000000..f42114c
--- /dev/null
+++ b/docs/formfiller-data-format
@@ -0,0 +1,35 @@
+FORMFILLER FILE FORMAT
+
+lines starting with '>' are ignored
+
+a file consists of profile definitions
+lines between profile definitions are ignored
+
+a line starting with '!profile=' introduces a profile definition
+the rest of that line is taken as the profile name
+profile names must match the RE /^[a-zA-Z0-9_-]*$/
+a line starting with '!' terminates the profile definition
+the rest of that line is ignored
+
+a profile definition consists of field definitions
+lines between field definitions are ignored
+
+a line starting with '%' introduces a field definition
+(details depend on the field type)
+the rest of that line shall be in the format ...
+ 'name(type){value}:checked' for checkbox/radio
+ 'name(type):value' for text/password/search
+ 'name(type):' for textarea
+where name/type/value/checked are the input field's HTML attribute values,
+ 'name' always being percent encoded, 'checked' being a JS boolean,
+ and 'value' being percent encoded if type is checkbox/radio
+for all types but textarea, the field definition ends with that single line
+for textarea, all lines up to (but excluding) one starting with '%' make up the 'value'
+the rest of that line starting with '%' is ignored
+
+for all lines, a leading '\', if present, is removed
+
+--
+
+a once-edit temporary file consists only of field definitions,
+ and lines starting with '!' or '>' are not special
diff --git a/examples/config/config b/examples/config/config
index d5555e0..ab690e0 100644
--- a/examples/config/config
+++ b/examples/config/config
@@ -69,6 +69,10 @@ set download_handler = sync_spawn @scripts_dir/download.sh
# Load commit handlers
@on_event LOAD_COMMIT @set_status <span foreground="green">recv</span>
+ # add some javascript to the page for other 'js' and 'script' commands to access later.
+@on_event LOAD_COMMIT js uzbl = {};
+@on_event LOAD_COMMIT script @scripts_dir/formfiller.js
+
# Userscripts/per-site-settings. See the script and the example configuration for details
#@on_event LOAD_COMMIT spawn @scripts_dir/per-site-settings.py @data_home/uzbl/per-site-settings
diff --git a/examples/data/scripts/formfiller.js b/examples/data/scripts/formfiller.js
new file mode 100644
index 0000000..abf0162
--- /dev/null
+++ b/examples/data/scripts/formfiller.js
@@ -0,0 +1,67 @@
+uzbl.formfiller = {
+
+ dump: function() {
+ var rv = '';
+ var allFrames = new Array(window);
+ for ( f=0; f<window.frames.length; ++f ) {
+ allFrames.push(window.frames[f]);
+ }
+ for ( j=0; j<allFrames.length; ++j ) {
+ try {
+ var xp_res = allFrames[j].document.evaluate(
+ '//input', allFrames[j].document.documentElement, null, XPathResult.ANY_TYPE,null
+ );
+ var input;
+ while ( input = xp_res.iterateNext() ) {
+ var type = (input.type?input.type:text);
+ if ( type == 'text' || type == 'password' || type == 'search' ) {
+ rv += '%' + escape(input.name) + '(' + type + '):' + input.value + '\n';
+ }
+ else if ( type == 'checkbox' || type == 'radio' ) {
+ rv += '%' + escape(input.name) + '(' + type + '){' + escape(input.value) + '}:' + (input.checked?'1':'0') + '\n';
+ }
+ }
+ xp_res = allFrames[j].document.evaluate(
+ '//textarea', allFrames[j].document.documentElement, null, XPathResult.ANY_TYPE,null
+ );
+ var input;
+ while ( input = xp_res.iterateNext() ) {
+ rv += '%' + escape(input.name) + '(textarea):\n' + input.value.replace(/\n%/g,"\n\\%") + '\n%\n';
+ }
+ }
+ catch (err) { }
+ }
+ return 'formfillerstart\n' + rv + '%!end';
+ }
+
+ ,
+
+ insert: function(fname, ftype, fvalue, fchecked) {
+ fname = unescape(fname);
+ var allFrames = new Array(window);
+ for ( f=0; f<window.frames.length; ++f ) {
+ allFrames.push(window.frames[f]);
+ }
+ for ( j=0; j<allFrames.length; ++j ) {
+ try {
+ if ( ftype == 'text' || ftype == 'password' || ftype == 'search' || ftype == 'textarea' ) {
+ allFrames[j].document.getElementsByName(fname)[0].value = fvalue;
+ }
+ else if ( ftype == 'checkbox' ) {
+ allFrames[j].document.getElementsByName(fname)[0].checked = fchecked;
+ }
+ else if ( ftype == 'radio' ) {
+ fvalue = unescape(fvalue);
+ var radios = allFrames[j].document.getElementsByName(fname);
+ for ( r=0; r<radios.length; ++r ) {
+ if ( radios[r].value == fvalue ) {
+ radios[r].checked = fchecked;
+ }
+ }
+ }
+ }
+ catch (err) { }
+ }
+ }
+
+}
diff --git a/examples/data/scripts/formfiller.sh b/examples/data/scripts/formfiller.sh
index c6822e6..3dc9dc4 100755
--- a/examples/data/scripts/formfiller.sh
+++ b/examples/data/scripts/formfiller.sh
@@ -1,202 +1,146 @@
#!/bin/sh
#
-# Enhanced html form (eg for logins) filler (and manager) for uzbl.
-#
-# uses settings files like: $UZBL_FORMS_DIR/<domain>
-# files contain lines like: !profile=<profile_name>
-# <fieldname>(fieldtype): <value>
-# profile_name should be replaced with a name that will tell sth about that
-# profile
-# fieldtype can be checkbox, text or password, textarea - only for information
-# pupropse (auto-generated) - don't change that
-#
-# Texteares: for textareas edited text can be now splitted into more lines.
-# If there will be text, that doesn't match key line:
-# <fieldname>(fieldtype):<value>
-# then it will be considered as a multiline for the first field above it
-# Keep in mind, that if you make more than one line for fileds like input
-# text fields, then all lines will be inserted into as one line
-#
-# Checkboxes/radio-buttons: to uncheck it type on of the following after the
-# colon:
-# no
-# off
-# 0
-# unchecked
-# false
-# or leave it blank, even without spaces
-# otherwise it will be considered as checked
-#
-# user arg 1:
-# edit: force editing the file (falls back to new if not found)
-# new: start with a new file.
-# load: try to load from file into form
-# add: try to add another profile to an existing file
-# once: edit form using external editor
-#
-# something else (or empty): if file not available: new, otherwise load.
+# action
+# new: add new profile template (creates file if not found), then edit
+# edit: edit file (fall back to 'new' if file not found)
+# load: load from file
+# once: use temporary file to edit form once
+# (empty): if file not available, new; otherwise, load
#
-DMENU_ARGS="-i"
-DMENU_SCHEMA="formfiller"
-DMENU_LINES="3"
-DMENU_PROMPT="Choose profile"
-DMENU_OPTIONS="vertical resize"
+action=$1
-. $UZBL_UTIL_DIR/dmenu.sh
-. $UZBL_UTIL_DIR/editor.sh
-. $UZBL_UTIL_DIR/uzbl-dir.sh
+. "$UZBL_UTIL_DIR/uzbl-dir.sh"
+. "$UZBL_UTIL_DIR/editor.sh"
-RAND=$(dd if=/dev/urandom count=1 2> /dev/null | cksum | cut -c 1-5)
-MODELINE="> vim:ft=formfiller"
+mkdir -p "$UZBL_FORMS_DIR" || exit
-[ -d "$(dirname $UZBL_FORMS_DIR)" ] || exit 1
-[ -d $UZBL_FORMS_DIR ] || mkdir $UZBL_FORMS_DIR || exit 1
+domain=${UZBL_URI#*://}
+domain=${domain%%/*}
-action=$1
+test "$domain" || exit
-domain=$(echo $UZBL_URI | sed 's/\(http\|https\):\/\/\([^\/]\+\)\/.*/\2/')
-
-if [ "$action" != 'edit' -a "$action" != 'new' -a "$action" != 'load' -a "$action" != 'add' -a "$action" != 'once' ]; then
- action="new"
- [ -e "$UZBL_FORMS_DIR/$domain" ] && action="load"
-elif [ "$action" = 'edit' ] && [ ! -e "$UZBL_FORMS_DIR/$domain" ]; then
- action="new"
-fi
-
-dumpFunction="function dump() { \
- var rv=''; \
- var allFrames = new Array(window); \
- for(f=0;f<window.frames.length;f=f+1) { \
- allFrames.push(window.frames[f]); \
- } \
- for(j=0;j<allFrames.length;j=j+1) { \
- try { \
- var xp_res=allFrames[j].document.evaluate('//input', allFrames[j].document.documentElement, null, XPathResult.ANY_TYPE,null); \
- var input; \
- while(input=xp_res.iterateNext()) { \
- var type=(input.type?input.type:text); \
- if(type == 'text' || type == 'password' || type == 'search') { \
- rv += input.name + '(' + type + '):' + input.value + '\\\\n'; \
- } \
- else if(type == 'checkbox' || type == 'radio') { \
- rv += input.name + '{' + input.value + '}(' + type + '):' + (input.checked?'ON':'OFF') + '\\\\n'; \
- } \
- } \
- xp_res=allFrames[j].document.evaluate('//textarea', allFrames[j].document.documentElement, null, XPathResult.ANY_TYPE,null); \
- var input; \
- while(input=xp_res.iterateNext()) { \
- rv += input.name + '(textarea):' + input.value + '\\\\n'; \
- } \
- } \
- catch(err) { } \
- } \
- return rv; \
-}; "
-
-insertFunction="function insert(fname, ftype, fvalue, fchecked) { \
- var allFrames = new Array(window); \
- for(f=0;f<window.frames.length;f=f+1) { \
- allFrames.push(window.frames[f]); \
- } \
- for(j=0;j<allFrames.length;j=j+1) { \
- try { \
- if(ftype == 'text' || ftype == 'password' || ftype == 'search' || ftype == 'textarea') { \
- allFrames[j].document.getElementsByName(fname)[0].value = fvalue; \
- } \
- else if(ftype == 'checkbox') { \
- allFrames[j].document.getElementsByName(fname)[0].checked = fchecked;\
- } \
- else if(ftype == 'radio') { \
- var radios = allFrames[j].document.getElementsByName(fname); \
- for(r=0;r<radios.length;r+=1) { \
- if(radios[r].value == fvalue) { \
- radios[r].checked = fchecked; \
- } \
- } \
- } \
- } \
- catch(err) { } \
- } \
-}; "
-
-if [ "$action" = 'load' ]; then
- [ -e $UZBL_FORMS_DIR/$domain ] || exit 2
- if [ $(cat $UZBL_FORMS_DIR/$domain | grep "!profile" | wc -l) -gt 1 ]; then
- menu=$(cat $UZBL_FORMS_DIR/$domain | \
- sed -n 's/^!profile=\([^[:blank:]]\+\)/\1/p')
- option=$(printf "$menu" | $DMENU)
- fi
+file=$UZBL_FORMS_DIR/$domain
+
+GenForm ()
+{
+ echo 'js uzbl.formfiller.dump();' \
+ | socat - unix-connect:"$UZBL_SOCKET" \
+ | awk '
+ /^formfillerstart$/ {
+ while (getline) {
+ if ( /^%!end/ ) exit
+ print
+ }
+ }
+ '
+}
+
+GetOption ()
+{
+ DMENU_SCHEME=formfiller
+ DMENU_PROMPT="choose profile"
+ DMENU_LINES=4
- # Remove comments
- sed '/^>/d' -i $tmpfile
-
- sed 's/^\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):\(off\|no\|false\|unchecked\|0\|$\)/\1{\2}(\3):0/I;s/^\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):[^0]\+/\1{\2}(\3):1/I' -i $UZBL_FORMS_DIR/$domain
- fields=$(cat $UZBL_FORMS_DIR/$domain | \
- sed -n "/^!profile=${option}/,/^!profile=/p" | \
- sed '/^!profile=/d' | \
- sed 's/^\([^(]\+(\)\(radio\|checkbox\|text\|search\|textarea\|password\)):/%{>\1\2):<}%/' | \
- sed 's/^\(.\+\)$/<{br}>\1/' | \
- tr -d '\n' | \
- sed 's/<{br}>%{>\([^(]\+(\)\(radio\|checkbox\|text\|search\|textarea\|password\)):<}%/\\n\1\2):/g')
- printf '%s\n' "${fields}" | \
- sed -n -e "s/\([^(]\+\)(\(password\|text\|search\|textarea\)\+):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\2', '\3', 0);/p" | \
- sed -e 's/@/\\@/g;s/<{br}>/\\\\n/g' | socat - unix-connect:$UZBL_SOCKET
- printf '%s\n' "${fields}" | \
- sed -n -e "s/\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\3', '\2', \4);/p" | \
- sed -e 's/@/\\@/g' | socat - unix-connect:$UZBL_SOCKET
-elif [ "$action" = "once" ]; then
- tmpfile=$(mktemp)
- printf 'js %s dump(); \n' "$dumpFunction" | \
- socat - unix-connect:$UZBL_SOCKET | \
- sed -n '/^[^(]\+([^)]\+):/p' > $tmpfile
- echo "$MODELINE" >> $tmpfile
- $UZBL_EDITOR $tmpfile
-
- [ -e $tmpfile ] || exit 2
-
- # Remove comments
- sed '/^>/d' -i $tmpfile
-
- sed 's/^\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):\(off\|no\|false\|unchecked\|0\|$\)/\1{\2}(\3):0/I;s/^\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):[^0]\+/\1{\2}(\3):1/I' -i $tmpfile
- fields=$(cat $tmpfile | \
- sed 's/^\([^(]\+(\)\(radio\|checkbox\|text\|search\|textarea\|password\)):/%{>\1\2):<}%/' | \
- sed 's/^\(.\+\)$/<{br}>\1/' | \
- tr -d '\n' | \
- sed 's/<{br}>%{>\([^(]\+(\)\(radio\|checkbox\|text\|search\|textarea\|password\)):<}%/\\n\1\2):/g')
- printf '%s\n' "${fields}" | \
- sed -n -e "s/\([^(]\+\)(\(password\|text\|search\|textarea\)\+):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\2', '\3', 0);/p" | \
- sed -e 's/@/\\@/g;s/<{br}>/\\\\n/g' | socat - unix-connect:$UZBL_SOCKET
- printf '%s\n' "${fields}" | \
- sed -n -e "s/\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\3', '\2', \4);/p" | \
- sed -e 's/@/\\@/g' | socat - unix-connect:$UZBL_SOCKET
- rm -f $tmpfile
-else
- if [ "$action" = 'new' -o "$action" = 'add' ]; then
- [ "$action" = 'new' ] && echo "$MODELINE" > $UZBL_FORMS_DIR/$domain
- echo "!profile=NAME_THIS_PROFILE$RAND" >> $UZBL_FORMS_DIR/$domain
- #
- # 2. and 3. line (tr -d and sed) are because, on gmail login for example,
- # <input > tag is splited into lines
- # ex:
- # <input name="Email"
- # type="text"
- # value="">
- # So, tr removes all new lines, and sed inserts new line after each >
- # Next sed selects only <input> tags and only with type = "text" or = "password"
- # If type is first and name is second, then another sed will change their order
- # so the last sed will make output
- # text_from_the_name_attr(text or password):
- #
- # login(text):
- # passwd(password):
- #
- printf 'js %s dump(); \n' "$dumpFunction" | \
- socat - unix-connect:$UZBL_SOCKET | \
- sed -n '/^[^(]\+([^)]\+):/p' >> $UZBL_FORMS_DIR/$domain
+ . "$UZBL_UTIL_DIR/dmenu.sh"
+
+ if [ $(grep -c '^!profile' "$1") -gt 1 ]
+ then sed -n 's/^!profile=//p' "$1" | $DMENU
+ else sed -n 's/^!profile=//p' "$1"
+ fi
+}
+
+ParseProfile ()
+{
+ sed "/^>/d; /^!profile=$1$/,/^!/!d; /^!/d"
+}
+
+ParseFields ()
+{
+ awk '/^%/ {
+
+ sub ( /%/, "" )
+
+ split( $0, parts, /\(|\)|\{|\}/ )
+
+ field = $0
+ sub ( /[^:]*:/, "", field )
+
+ if ( parts[2] ~ /(text|password|search)/ )
+ printf( "js uzbl.formfiller.insert(\"%s\",\"%s\",\"%s\",0);\n",
+ parts[1], parts[2], field )
+
+ else if ( parts[2] ~ /(checkbox|radio)/ )
+ printf( "js uzbl.formfiller.insert(\"%s\",\"%s\",\"%s\",%s);\n",
+ parts[1], parts[2], parts[3], field )
+
+ else if ( parts[2] == "textarea" ) {
+ field = ""
+ while (getline) {
+ if ( /^%/ ) break
+ sub ( /^\\/, "" )
+ gsub ( /"/, "\\\"" )
+ gsub ( /\\/, "\\\\" )
+ field = field $0 "\\n"
+ }
+ printf( "js uzbl.formfiller.insert(\"%s\",\"%s\",\"%s\",0);\n",
+ parts[1], parts[2], field )
+ }
+
+ }'
+}
+
+New ()
+{
+ { echo '!profile=NAME_THIS_PROFILE'
+ GenForm | sed 's/^!/\\!/'
+ echo '!'
+ } >> "$file"
+ chmod 600 "$file"
+ $UZBL_EDITOR "$file"
+}
+
+Edit ()
+ if [ -e "$file" ]
+ then $UZBL_EDITOR "$file"
+ else New
fi
- [ -e "$UZBL_FORMS_DIR/$domain" ] || exit 3 #this should never happen, but you never know.
- $UZBL_EDITOR "$UZBL_FORMS_DIR/$domain" #TODO: if user aborts save in editor, the file is already overwritten
-fi
-# vim:fileencoding=utf-8:sw=4
+Load ()
+{
+ test -e "$file" || exit
+
+ option=$(GetOption "$file")
+
+ case $option in *[!a-zA-Z0-9_-]*) exit 1; esac
+
+ ParseProfile $option < "$file" \
+ | ParseFields \
+ | sed 's/@/\\@/' \
+ > "$UZBL_FIFO"
+}
+
+Once ()
+{
+ tmpfile=/tmp/${0##*/}-$$-tmpfile
+ trap 'rm -f "$tmpfile"' EXIT
+
+ GenForm > "$tmpfile"
+ chmod 600 "$tmpfile"
+
+ $UZBL_EDITOR "$tmpfile"
+
+ test -e "$tmpfile" &&
+ ParseFields < "$tmpfile" \
+ | sed 's/@/\\@' \
+ > "$UZBL_FIFO"
+}
+
+case $action in
+ new) New; Load ;;
+ edit) Edit; Load ;;
+ load) Load ;;
+ once) Once ;;
+ '') if [ -e "$file" ]; then Load; else New; Load; fi ;;
+ *) exit 1
+esac
diff --git a/src/callbacks.c b/src/callbacks.c
index a40057c..d315a9e 100644
--- a/src/callbacks.c
+++ b/src/callbacks.c
@@ -957,16 +957,28 @@ scroll_horiz_cb(GtkAdjustment *adjust, void *w)
}
void
-run_menu_command(GtkWidget *menu, const char *line) {
+run_menu_command(GtkWidget *menu, MenuItem *mi) {
(void) menu;
- parse_cmd_line(line, NULL);
+ if (mi->context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE) {
+ gchar* uri;
+ g_object_get(mi->hittest, "image-uri", &uri, NULL);
+ gchar* cmd = g_strdup_printf("%s %s", mi->cmd, uri);
+
+ parse_cmd_line(cmd, NULL);
+
+ g_free(cmd);
+ g_free(uri);
+ g_object_unref(mi->hittest);
+ }
+ else {
+ parse_cmd_line(mi->cmd, NULL);
+ }
}
void
populate_popup_cb(WebKitWebView *v, GtkMenu *m, void *c) {
- (void) v;
(void) c;
GUI *g = &uzbl.gui;
GtkWidget *item;
@@ -981,11 +993,19 @@ populate_popup_cb(WebKitWebView *v, GtkMenu *m, void *c) {
if((context = get_click_context(NULL)) == -1)
return;
-
for(i=0; i < uzbl.gui.menu_items->len; i++) {
hit = 0;
mi = g_ptr_array_index(uzbl.gui.menu_items, i);
+ if (mi->context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE) {
+ GdkEventButton ev;
+ gint x, y;
+ gdk_window_get_pointer(gtk_widget_get_window(v), &x, &y, NULL);
+ ev.x = x;
+ ev.y = y;
+ mi->hittest = webkit_web_view_get_hit_test_result(v, &ev);
+ }
+
if((mi->context > WEBKIT_HIT_TEST_RESULT_CONTEXT_DOCUMENT) &&
(context & mi->context)) {
if(mi->issep) {
@@ -996,7 +1016,7 @@ populate_popup_cb(WebKitWebView *v, GtkMenu *m, void *c) {
else {
item = gtk_menu_item_new_with_label(mi->name);
g_signal_connect(item, "activate",
- G_CALLBACK(run_menu_command), mi->cmd);
+ G_CALLBACK(run_menu_command), mi);
gtk_menu_shell_append(GTK_MENU_SHELL(m), item);
gtk_widget_show(item);
}
@@ -1014,7 +1034,7 @@ populate_popup_cb(WebKitWebView *v, GtkMenu *m, void *c) {
else {
item = gtk_menu_item_new_with_label(mi->name);
g_signal_connect(item, "activate",
- G_CALLBACK(run_menu_command), mi->cmd);
+ G_CALLBACK(run_menu_command), mi);
gtk_menu_shell_append(GTK_MENU_SHELL(m), item);
gtk_widget_show(item);
}
diff --git a/src/io.c b/src/io.c
index 1e85237..b84780e 100644
--- a/src/io.c
+++ b/src/io.c
@@ -123,9 +123,14 @@ control_stdin(GIOChannel *gio, GIOCondition condition) {
if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
return FALSE;
- parse_cmd_line(ctl_line, NULL);
+ GString *result = g_string_new("");
+
+ parse_cmd_line(ctl_line, result);
g_free(ctl_line);
+ puts(result->str);
+ g_string_free(result, TRUE);
+
return TRUE;
}
diff --git a/src/util.c b/src/util.c
index 345bf2f..8f6c349 100644
--- a/src/util.c
+++ b/src/util.c
@@ -6,6 +6,8 @@
#include "util.h"
+gchar* find_existing_file2(gchar *, const gchar *);
+
const XDG_Var XDG[] = {
{ "XDG_CONFIG_HOME", "~/.config" },
{ "XDG_DATA_HOME", "~/.local/share" },
@@ -16,56 +18,41 @@ const XDG_Var XDG[] = {
/*@null@*/ gchar*
get_xdg_var (XDG_Var xdg) {
- const gchar* actual_value = getenv (xdg.environmental);
- const gchar* home = getenv ("HOME");
- gchar* return_value;
-
- if (! actual_value || strcmp (actual_value, "") == 0) {
- if (xdg.default_value) {
- return_value = str_replace ("~", home, xdg.default_value);
- } else {
- return_value = NULL;
- }
- } else {
- return_value = str_replace("~", home, actual_value);
- }
+ const gchar *actual_value = getenv(xdg.environmental);
+ const gchar *home = getenv("HOME");
+
+ if (!actual_value || !actual_value[0])
+ actual_value = xdg.default_value;
- return return_value;
+ if (!actual_value)
+ return NULL;
+
+ return str_replace("~", home, actual_value);
}
/*@null@*/ gchar*
-find_xdg_file (int xdg_type, const char* filename) {
+find_xdg_file (int xdg_type, const char* basename) {
/* xdg_type = 0 => config
xdg_type = 1 => data
- xdg_type = 2 => cache*/
+ xdg_type = 2 => cache */
- gchar* xdgv = get_xdg_var (XDG[xdg_type]);
- gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
+ gchar *xdgv = get_xdg_var(XDG[xdg_type]);
+ gchar *path = g_strconcat (xdgv, basename, NULL);
g_free (xdgv);
- gchar* temporary_string;
- char* saveptr;
- char* buf;
-
- if (! file_exists (temporary_file) && xdg_type != 2) {
- buf = get_xdg_var (XDG[3 + xdg_type]);
- temporary_string = (char *) strtok_r (buf, ":", &saveptr);
- g_free(buf);
+ if (file_exists(path))
+ return path;
- while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
- g_free (temporary_file);
- temporary_file = g_strconcat (temporary_string, filename, NULL);
- }
- }
+ if (xdg_type == 2)
+ return NULL;
- //g_free (temporary_string); - segfaults.
+ /* the file doesn't exist in the expected directory.
+ * check if it exists in one of the system-wide directories. */
+ char *system_dirs = get_xdg_var(XDG[3 + xdg_type]);
+ path = find_existing_file2(system_dirs, basename);
+ g_free(system_dirs);
- if (file_exists (temporary_file)) {
- return temporary_file;
- } else {
- g_free(temporary_file);
- return NULL;
- }
+ return path;
}
gboolean
@@ -95,85 +82,66 @@ for_each_line_in_file(const gchar *path, void (*callback)(const gchar *l, void *
GIOChannel *chan = g_io_channel_new_file(path, "r", NULL);
- if (chan) {
- while (g_io_channel_read_line(chan, &line, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
- callback(line, user_data);
- g_free(line);
- }
- g_io_channel_unref (chan);
+ if (!chan)
+ return FALSE;
- return TRUE;
+ while (g_io_channel_read_line(chan, &line, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
+ callback(line, user_data);
+ g_free(line);
}
- return FALSE;
-}
+ g_io_channel_unref (chan);
-enum exp_type
-get_exp_type(const gchar *s) {
- /* variables */
- if(*(s+1) == '(')
- return EXP_EXPR;
- else if(*(s+1) == '{')
- return EXP_BRACED_VAR;
- else if(*(s+1) == '<')
- return EXP_JS;
- else if(*(s+1) == '[')
- return EXP_ESCAPE;
- else
- return EXP_SIMPLE_VAR;
-
- /*@notreached@*/
-return EXP_ERR;
+ return TRUE;
}
-
-/* search a PATH style string for an existing file+path combination */
+/* This function searches the directories in the : separated ($PATH style)
+ * string 'dirs' for a file named 'basename'. It returns the path of the first
+ * file found, or NULL if none could be found.
+ * NOTE: this function modifies the 'dirs' argument. */
gchar*
-find_existing_file(gchar* path_list) {
- int i=0;
- int cnt;
- gchar **split;
- gchar *tmp = NULL;
- gchar *executable;
+find_existing_file2(gchar *dirs, const gchar *basename) {
+ char *saveptr = NULL;
+
+ /* iterate through the : separated elements until we find our file. */
+ char *tok = strtok_r(dirs, ":", &saveptr);
+ char *path = g_strconcat (tok, "/", basename, NULL);
+
+ while (!file_exists(path)) {
+ g_free(path);
+
+ tok = strtok_r(NULL, ":", &saveptr);
+ if (!tok)
+ return NULL; /* we've hit the end of the string */
+ path = g_strconcat (tok, "/", basename, NULL);
+ }
+
+ return path;
+}
+
+/* search a PATH style string for an existing file+path combination.
+ * everything after the last ':' is assumed to be the name of the file.
+ * e.g. "/tmp:/home:a/file" will look for /tmp/a/file and /home/a/file.
+ *
+ * if there are no :s then the entire thing is taken to be the path. */
+gchar*
+find_existing_file(const gchar* path_list) {
if(!path_list)
return NULL;
- split = g_strsplit(path_list, ":", 0);
- while(split[i])
- i++;
+ char *path_list_dup = g_strdup(path_list);
- if(i<=1) {
- tmp = g_strdup(split[0]);
- g_strfreev(split);
- return tmp;
- }
- else
- cnt = i-1;
-
- i=0;
- tmp = g_strdup(split[cnt]);
- g_strstrip(tmp);
- if(tmp[0] == '/')
- executable = g_strdup_printf("%s", tmp+1);
- else
- executable = g_strdup(tmp);
- g_free(tmp);
-
- while(i<cnt) {
- tmp = g_strconcat(g_strstrip(split[i]), "/", executable, NULL);
- if(g_file_test(tmp, G_FILE_TEST_EXISTS)) {
- g_strfreev(split);
- return tmp;
- }
- else
- g_free(tmp);
- i++;
- }
+ char *basename = strrchr(path_list_dup, ':');
+ if(!basename)
+ return path_list_dup;
+
+ basename[0] = '\0';
+ basename++;
- g_free(executable);
- g_strfreev(split);
- return NULL;
+ char *result = find_existing_file2(path_list_dup, basename);
+ g_free(path_list_dup);
+ return result;
}
gchar*
diff --git a/src/util.h b/src/util.h
index db19930..75a614b 100644
--- a/src/util.h
+++ b/src/util.h
@@ -6,16 +6,12 @@ typedef struct {
gchar* default_value;
} XDG_Var;
-enum exp_type {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
-
-
gchar* get_xdg_var(XDG_Var xdg);
gchar* find_xdg_file(int xdg_type, const char* filename);
gboolean file_exists(const char* filename);
char* str_replace(const char* search, const char* replace, const char* string);
gboolean for_each_line_in_file(const gchar *path, void (*callback)(const gchar *l, void *c), void *user_data);
-enum exp_type get_exp_type(const gchar*);
-gchar* find_existing_file(gchar*);
+gchar* find_existing_file(const gchar*);
gchar* argv_idx(const GArray*, const guint);
/**
* appends `src' to `dest' with backslash, single-quotes and newlines in
diff --git a/src/uzbl-core.c b/src/uzbl-core.c
index 26b3dba..638dd1f 100644
--- a/src/uzbl-core.c
+++ b/src/uzbl-core.c
@@ -165,6 +165,20 @@ create_var_to_name_hash() {
/* --- UTILITY FUNCTIONS --- */
+enum exp_type {
+ EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE
+};
+
+enum exp_type
+get_exp_type(const gchar *s) {
+ switch(*(s+1)) {
+ case '(': return EXP_EXPR;
+ case '{': return EXP_BRACED_VAR;
+ case '<': return EXP_JS;
+ case '[': return EXP_ESCAPE;
+ default: return EXP_SIMPLE_VAR;
+ }
+}
/*
* recurse == 1: don't expand '@(command)@'
@@ -608,6 +622,9 @@ print(WebKitWebView *page, GArray *argv, GString *result) {
(void) page; (void) result;
gchar* buf;
+ if(!result)
+ return;
+
buf = expand(argv_idx(argv, 0), 0);
g_string_assign(result, buf);
g_free(buf);
@@ -720,7 +737,8 @@ act_dump_config_as_events() {
void
load_uri(WebKitWebView *web_view, GArray *argv, GString *result) {
(void) web_view; (void) result;
- set_var_value("uri", argv_idx(argv, 0));
+ gchar * uri = argv_idx(argv, 0);
+ set_var_value("uri", uri ? uri : "");
}
/* Javascript*/
@@ -1212,7 +1230,8 @@ move_statusbar() {
}
g_object_unref(uzbl.gui.scrolled_win);
g_object_unref(uzbl.gui.mainbar);
- gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
+ if (!uzbl.state.plug_mode)
+ gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
return;
}
@@ -1258,6 +1277,8 @@ set_var_value(const gchar *name, gchar *val) {
char *endp = NULL;
char *buf = NULL;
+ g_assert(val != NULL);
+
if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
if(!c->writeable) return FALSE;
diff --git a/src/uzbl-core.h b/src/uzbl-core.h
index 3240fc6..1285e3a 100644
--- a/src/uzbl-core.h
+++ b/src/uzbl-core.h
@@ -362,6 +362,7 @@ typedef struct {
gchar* cmd;
gboolean issep;
guint context;
+ WebKitHitTestResult* hittest;
} MenuItem;
#endif
diff --git a/tests/test-command.c b/tests/test-command.c
index 55bf316..6b55fb3 100644
--- a/tests/test-command.c
+++ b/tests/test-command.c
@@ -282,6 +282,11 @@ test_js (void) {
g_string_free(result, TRUE);
}
+void test_uri(void) {
+ /* Testing for a crash, not crashing is a pass */
+ parse_cmd_line("uri", NULL);
+}
+
void
test_last_result (void) {
GString *result = g_string_new("");
@@ -313,6 +318,7 @@ main (int argc, char *argv[]) {
g_test_add("/test-command/event", struct EventFixture, NULL, event_fixture_setup, test_event, event_fixture_teardown);
g_test_add_func("/test-command/print", test_print);
+ g_test_add_func("/test-command/uri", test_uri);
g_test_add_func("/test-command/scroll", test_scroll);
g_test_add_func("/test-command/toggle-status", test_toggle_status);
g_test_add_func("/test-command/sync-sh", test_sync_sh);
@@ -329,7 +335,7 @@ main (int argc, char *argv[]) {
uzbl.state.config_file = "/tmp/uzbl-config";
uzbl.comm.fifo_path = "/tmp/some-nonexistant-fifo";
uzbl.comm.socket_path = "/tmp/some-nonexistant-socket";
- uzbl.state.uri = "http://example.org/";
+ uzbl.state.uri = g_strdup("http://example.org/");
uzbl.gui.main_title = "Example.Org";
uzbl.state.instance_name = INSTANCE_NAME;