aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Brendan Taylor <whateley@gmail.com>2010-12-30 15:46:39 -0700
committerGravatar Brendan Taylor <whateley@gmail.com>2010-12-30 15:46:39 -0700
commit71dfda767ab2e9bfa2be2f77f1619edf52cc09a4 (patch)
tree97f44b63a53bc1cb3238f478c8e49a9d5edcf97a
parent4fbba2d523ba65b2dad15cf264eb168157cf0f15 (diff)
parent99252a853e9dd9e0e706d6793e836fb85f65ab5a (diff)
Merge branch 'experimental' into dev/script-args-in-env
Conflicts: src/uzbl-core.c
-rw-r--r--.gitignore1
-rw-r--r--AUTHORS3
-rw-r--r--Makefile79
-rw-r--r--README49
-rw-r--r--docs/COMMUNITY10
-rw-r--r--docs/INSTALL1
-rw-r--r--docs/README.uzbl-event-manager99
-rw-r--r--examples/config/config31
-rw-r--r--examples/data/per-site-settings3
-rw-r--r--examples/data/plugins/cookies.py176
-rw-r--r--examples/data/plugins/downloads.py69
-rwxr-xr-xexamples/data/scripts/download.sh34
-rwxr-xr-xexamples/data/scripts/load_cookies.sh20
-rwxr-xr-xexamples/data/scripts/load_url_from_history.sh2
-rwxr-xr-xexamples/data/scripts/per-site-settings.py117
-rw-r--r--examples/data/scripts/pipermail.js71
-rwxr-xr-xexamples/data/scripts/session.sh28
-rwxr-xr-xexamples/data/scripts/userscript.sh38
-rwxr-xr-xexamples/data/scripts/userscripts.sh8
-rw-r--r--examples/data/scripts/util/dmenu.sh2
-rwxr-xr-xexamples/data/scripts/uzbl-tabbed5
-rwxr-xr-xmisc/env.sh4
-rw-r--r--src/callbacks.c157
-rw-r--r--src/callbacks.h5
-rw-r--r--src/cookie-jar.c26
-rw-r--r--src/cookie-jar.h1
-rw-r--r--src/events.c22
-rw-r--r--src/events.h4
-rwxr-xr-xsrc/uzbl-browser4
-rw-r--r--src/uzbl-core.c354
-rw-r--r--src/uzbl-core.h20
-rw-r--r--tests/Makefile38
32 files changed, 1143 insertions, 338 deletions
diff --git a/.gitignore b/.gitignore
index 078164f..2ddbb60 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,4 @@ uzbl-core
*.pyc
*~
tags
+uzbl-cookie-manager
diff --git a/AUTHORS b/AUTHORS
index 830df44..f7e4a16 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -35,7 +35,7 @@ In alphabetical order:
Daiki Ueno (ueno) - fix for crash when opening image in new window
Damien Leon - misc
Daniel M. Hackney - documentation cleanups
- David Keijser - various C and python patches.
+ David Keijser (keis) - the add_cookie/delete_cookie + distributor system, various C and python patches.
Devon Jones <devon.jones@gmail.com> - uzbl_tabbed: bring_to_front
Dieter Plaetinck (Dieter@be) <dieter AT plaetinck.be> - several contributions
Dmytro Milinevskyy - uzbl-tabbed useability patches
@@ -51,6 +51,7 @@ In alphabetical order:
James S Wheaton (uranther) - zoom level, test framework
Jan Kolkmeier (jouz) - scrolling, link following
Jason Woofenden (JasonWoof) - geometry=maximized, link following
+ Jochen Sprickerhof - session.sh enhancements
Lars-Dominik Braun (PromyLOPh) - added ability to enable/disable the webkit page cache
Laurence Withers (lwithers) - talk_to_socket
Luca Bruno <lucab@debian.org> - bashims fixes
diff --git a/Makefile b/Makefile
index 04f35d0..7724303 100644
--- a/Makefile
+++ b/Makefile
@@ -1,50 +1,71 @@
# first entries are for gnu make, 2nd for BSD make. see http://lists.uzbl.org/pipermail/uzbl-dev-uzbl.org/2009-July/000177.html
-CFLAGS:=-std=c99 $(shell pkg-config --cflags gtk+-2.0 webkit-1.0 libsoup-2.4 gthread-2.0 glib-2.0) -ggdb -Wall -W -DARCH="\"$(shell uname -m)\"" -lgthread-2.0 -DCOMMIT="\"$(shell ./misc/hash.sh)\"" $(CPPFLAGS) -fPIC -W -Wall -Wextra -pedantic
-CFLAGS!=echo -std=c99 `pkg-config --cflags gtk+-2.0 webkit-1.0 libsoup-2.4 gthread-2.0 glib-2.0` -ggdb -Wall -W -DARCH='"\""'`uname -m`'"\""' -lgthread-2.0 -DCOMMIT='"\""'`./misc/hash.sh`'"\""' $(CPPFLAGS) -fPIC -W -Wall -Wextra -pedantic
+# packagers, set DESTDIR to your "package directory" and PREFIX to the prefix you want to have on the end-user system
+# end-users who build from source: don't care about DESTDIR, update PREFIX if you want to
+# RUN_PREFIX : what the prefix is when the software is run. usually the same as PREFIX
+PREFIX?=/usr/local
+INSTALLDIR?=$(DESTDIR)$(PREFIX)
+DOCDIR?=$(INSTALLDIR)/share/uzbl/docs
+RUN_PREFIX?=$(PREFIX)
+
+# gtk2
+REQ_PKGS = gtk+-2.0 webkit-1.0
+CPPFLAGS =
+
+# gtk3
+#REQ_PKGS = gtk+-3.0 webkitgtk-3.0
+#CPPFLAGS = -DGTK3
+
+# --- configuration ends here ---
+
+REQ_PKGS += libsoup-2.4 gthread-2.0 glib-2.0
+
+ARCH:=$(shell uname -m)
+ARCH!=echo `uname -m`
+
+COMMIT_HASH:=$(shell ./misc/hash.sh)
+COMMIT_HASH!=echo `./misc/hash.sh`
-UZBL_LDFLAGS:=$(shell pkg-config --libs gtk+-2.0 webkit-1.0 libsoup-2.4 gthread-2.0 x11) -pthread $(LDFLAGS)
-UZBL_LDFLAGS!=echo `pkg-config --libs gtk+-2.0 webkit-1.0 libsoup-2.4 gthread-2.0 x11` -pthread $(LDFLAGS)
+CPPFLAGS += -DARCH=\"$(ARCH)\" -DCOMMIT=\"$(COMMIT_HASH)\"
+
+PKG_CFLAGS:=$(shell pkg-config --cflags $(REQ_PKGS))
+PKG_CFLAGS!=echo pkg-config --cflags $(REQ_PKGS)
+
+LDLIBS:=$(shell pkg-config --libs $(REQ_PKGS) x11)
+LDLIBS!=echo pkg-config --libs $(REQ_PKGS) x11
+
+CFLAGS += -std=c99 $(PKG_CFLAGS) -ggdb -W -Wall -Wextra -pedantic -pthread
SRC = $(wildcard src/*.c)
HEAD = $(wildcard src/*.h)
-OBJ = $(foreach obj, $(SRC:.c=.o), $(notdir $(obj)))
+OBJ = $(foreach obj, $(SRC:.c=.o), $(notdir $(obj)))
+LOBJ = $(foreach obj, $(SRC:.c=.lo), $(notdir $(obj)))
all: uzbl-browser uzbl-cookie-manager
VPATH:=src
-.c.o:
- @echo -e "${CC} -c ${CFLAGS} $<"
- @${CC} -c ${CFLAGS} $<
-
${OBJ}: ${HEAD}
uzbl-core: ${OBJ}
- @echo -e "\n${CC} -o $@ ${OBJ} ${UZBL_LDFLAGS}"
- @${CC} -o $@ ${OBJ} ${UZBL_LDFLAGS}
-uzbl-cookie-manager: examples/uzbl-cookie-manager.o src/util.o
- @echo -e "\n${CC} -o $@ uzbl-cookie-manager.o util.o ${LDFLAGS} ${shell pkg-config --libs glib-2.0 libsoup-2.4}"
- @${CC} -o $@ uzbl-cookie-manager.o util.o ${LDFLAGS} $(shell pkg-config --libs glib-2.0 libsoup-2.4)
+uzbl-cookie-manager: examples/uzbl-cookie-manager.o util.o
+ @echo -e "\n${CC} -o $@ examples/uzbl-cookie-manager.o util.o ${shell pkg-config --libs glib-2.0 libsoup-2.4}"
+ @${CC} -o $@ examples/uzbl-cookie-manager.o util.o $(shell pkg-config --libs glib-2.0 libsoup-2.4)
uzbl-browser: uzbl-core uzbl-cookie-manager
-# packagers, set DESTDIR to your "package directory" and PREFIX to the prefix you want to have on the end-user system
-# end-users who build from source: don't care about DESTDIR, update PREFIX if you want to
-# RUN_PREFIX : what the prefix is when the software is run. usually the same as PREFIX
-PREFIX?=/usr/local
-INSTALLDIR?=$(DESTDIR)$(PREFIX)
-DOCDIR?=$(INSTALLDIR)/share/uzbl/docs
-RUN_PREFIX?=$(PREFIX)
-
# the 'tests' target can never be up to date
.PHONY: tests
force:
+# this is here because the .so needs to be compiled with -fPIC on x86_64
+${LOBJ}: ${SRC} ${HEAD}
+ $(CC) $(CPPFLAGS) $(CFLAGS) -fPIC -c src/$(@:.lo=.c) -o $@
+
# When compiling unit tests, compile uzbl as a library first
-tests: ${OBJ} force
- $(CC) -shared -Wl ${OBJ} -o ./tests/libuzbl-core.so
+tests: ${LOBJ} force
+ $(CC) -shared -Wl ${LOBJ} -o ./tests/libuzbl-core.so
cd ./tests/; $(MAKE)
test-uzbl-core: uzbl-core
@@ -90,12 +111,7 @@ test-uzbl-tabbed-sandbox: uzbl-browser
clean:
rm -f uzbl-core
rm -f uzbl-cookie-manager
- rm -f uzbl-core.o
- rm -f events.o
- rm -f callbacks.o
- rm -f inspector.o
- rm -f cookie-jar.o
- rm -f util.o
+ rm -f *.o *.lo
find ./examples/ -name "*.pyc" -delete
cd ./tests/; $(MAKE) clean
rm -rf ./sandbox/
@@ -119,9 +135,6 @@ install-uzbl-core: all install-dirs
install -m644 AUTHORS $(DOCDIR)/
cp -r examples $(INSTALLDIR)/share/uzbl/
chmod 755 $(INSTALLDIR)/share/uzbl/examples/data/scripts/*
- mv $(INSTALLDIR)/share/uzbl/examples/config/config $(INSTALLDIR)/share/uzbl/examples/config/config.bak
- sed 's#^set prefix.*=.*#set prefix = $(RUN_PREFIX)#' < $(INSTALLDIR)/share/uzbl/examples/config/config.bak > $(INSTALLDIR)/share/uzbl/examples/config/config
- rm $(INSTALLDIR)/share/uzbl/examples/config/config.bak
install -m755 uzbl-core $(INSTALLDIR)/bin/uzbl-core
install-uzbl-browser: uzbl-cookie-manager install-dirs
diff --git a/README b/README
index 22c1a99..35492da 100644
--- a/README
+++ b/README
@@ -260,6 +260,11 @@ The following commands are recognized:
- Read contents of `<file>` and interpret as a set of `uzbl` commands.
* `show_inspector`
- Show the WebInspector
+* `add_cookie <domain> <path> <name> <value> <scheme> <expires>`
+ - Adds a new cookie to the cookie jar
+* 'delete_cookie <domain> <path> <name> <value> [<scheme> <expires>]`
+ - Deletes a matching cookie from the cookie jar. scheme and expire time
+ is currently not considered when matching.
### VARIABLES AND CONSTANTS
@@ -305,6 +310,9 @@ file).
- `data`: The cookie data. Only included for "PUT" requests.
* `scheme_handler`: handler to execute for each URI navigated to - the
navigation request will be ignored if handler prints "USED\n"
+* `download_handler`: executed when a download is started. the handler script
+ should print a path that the download should be saved to, or print nothing
+ to cancel the download.
* `fifo_dir`: location to store FIFOs.
* `socket_dir`: location to store sockets.
* `http_debug`: HTTP debug mode (value 0-3).
@@ -494,9 +502,16 @@ access to the following environment variables:
These variables are also available as positional arguments `$1` through `$7`,
but this is deprecated and will be removed.
-Handler scripts (`cookie_handler`, `scheme_handler` and
+Handler scripts (`download_handler`, `cookie_handler`, `scheme_handler` and
`authentication_handler`) are called with special arguments:
+* download handler
+
+ - `$8 url`: The URL of the item to be downloaded.
+ - `$9 suggested_filename`: A filename suggested by the server or based on the URL.
+ - `$10 content_type`: The mimetype of the file to be downloaded.
+ - `$11 total_size`: The size of the file to be downloaded in bytes. This may be inaccurate.
+
* cookie handler
- `$8 GET/PUT`: Whether a cookie should be sent to the server (`GET`) or
@@ -622,11 +637,10 @@ The EM allows:
* Many fine-grained events (`hover_over_link`, `key_press`, `key_release`,..)
* See example `uzbl-event-manager`.
-**Note**: Cookie events are not sent to an event handler but handled internally
- through the cookie handler because of their synchronous nature. Cookie events
- are really something completely different from all other events. Maybe someday
- we'll use HTTP proxies or synchronous events (which also have other nice use
- cases), but for now we still use the handler code.
+**Note**: Cookie events are sent in addition to (optionally) being handled by
+ the cookie handler (set by the cookie_handler var). If using a handler it will
+ take precedence before the internal state configured by (add|delete)_cookie
+ commands.
Events have this format:
@@ -664,8 +678,13 @@ Events have this format:
* `EVENT [uzbl_instance_name] TITLE_CHANGED title_name`: When the title of the
page (and hence maybe, the window title) changed. `title_name` is the new
title.
-* `EVENT [uzbl_instance_name] DOWNLOAD_REQUEST download_uri`: When content needs
- to be downloaded, `download_uri` is the URI to get.
+* `EVENT [uzbl_instance_name] DOWNLOAD_STARTED destination_path`: A download
+ has been started, the file will be saved to `destination_path`.
+* `EVENT [uzbl_instance_name] DOWNLOAD_PROGRESS destination_path progress`:
+ While a download is active this event notifies you of the progress.
+ `progress` is a decimal between 0 and 1.
+* `EVENT [uzbl_instance_name] DOWNLOAD_COMPLETE destination_path`: The
+ download being saved to `destination_path` is now complete.
* `EVENT [uzbl_instance_name] LINK_HOVER uri`: The mouse hovers over the link
`uri`.
* `EVENT [uzbl_instance_name] LINK_UNHOVER uri`: The mouse leaves the link
@@ -695,6 +714,11 @@ Events have this format:
Xembed mode, `plug_id` is the Xembed ID used.
* `EVENT [uzbl_instance_name] BUILTINS command_list`: Shows a list of all `uzbl`
commands, whitespace separated, on startup.
+* `EVENT [uzbl_instance_name] ADD_COOKIE domain path name value scheme expire`:
+ When a cookie was added or replaced. scheme is 'http' or 'https', expire will
+ be a unix-timestamp or empty
+* `EVENT [uzbl_instance_name] DELETE_COOKIE domain path name value scheme expire`:
+ When a cookie was deleted. arguments as ADD_COOKIE
Events/requests which the EM and its plugins listens for
@@ -769,6 +793,15 @@ Events/requests which the EM and its plugins listens for
`<index>` is `+`, advance the cursor by one character, and if it is `-`,
move the cursor back by one character.
* `START_COMPLETION`: TODO explain completion
+* `BLACKLIST_COOKIE`: add a rule for blacklisting cookies
+ - `request BLACKLIST_COOKIE <component> <regexp>`: Blacklist cookies where
+ `<component>` matches `<regexp>`. `<component>` is one of `domain`,
+ `path`, `name`, `value`, `scheme` or `expires`.
+* `WHITELIST_COOKIE`: add a rule for whitelisting cookies (if any whitelist is
+ set then only cookies that are whitelisted cookies will be used)
+ - `request WHITELIST_COOKIE <component> <regexp>`: Whitelist cookies where
+ `<component>` matches `<regexp>`. `<component>` is one of `domain`,
+ `path`, `name`, `value`, `scheme` or `expires`.
### COMMAND LINE ARGUMENTS
diff --git a/docs/COMMUNITY b/docs/COMMUNITY
index 2817ee9..cbf3b48 100644
--- a/docs/COMMUNITY
+++ b/docs/COMMUNITY
@@ -4,12 +4,12 @@ COMMUNITY
### Mailing list
* Address: [uzbl-dev@lists.uzbl.org](mailto:uzbl-dev@lists.uzbl.org)
-* [Page](http://lists.uzbl.org/listinfo.cgi/uzbl-dev-uzbl.org)
-* [Archives](http://lists.uzbl.org/pipermail/uzbl-dev-uzbl.org/)
+* [Page](http://lists.uzbl.org/listinfo/uzbl-dev)
+* [Archives](http://lists.uzbl.org/pipermail/uzbl-dev/)
### IRC
-* `#uzbl` on irc.freenode.net
+* `#uzbl` on irc.freenode.net ([webchat](http://webchat.freenode.net/?channels=uzbl))
* [Archive](http://www.uzbl.org/irc-log)
### Website
@@ -23,10 +23,6 @@ COMMUNITY
### Code repositories
* [github.com/Dieterbe/uzbl](http://github.com/Dieterbe/uzbl/)
-* [github.com/anydot/uzbl](http://github.com/anydot/uzbl/)
-* [github.com/Barrucadu/uzbl](http://github.com/Barrucadu/uzbl/)
-* [github.com/dusanx/uzbl](http://github.com/dusanx/uzbl/)
-* [github.com/robm/uzbl](http://github.com/robm/uzbl/)
There are more contributors who have forks. See:
diff --git a/docs/INSTALL b/docs/INSTALL
index accd383..0f6a662 100644
--- a/docs/INSTALL
+++ b/docs/INSTALL
@@ -13,6 +13,7 @@ You can pull the code from git or get a tagged tarball.
[ $ git checkout origin/experimental ] # optional. see below
$ make
$ sudo make install
+
If you want to remove uzbl again, you can issue:
$ make uninstall
diff --git a/docs/README.uzbl-event-manager b/docs/README.uzbl-event-manager
new file mode 100644
index 0000000..074811e
--- /dev/null
+++ b/docs/README.uzbl-event-manager
@@ -0,0 +1,99 @@
+# The uzbl event manager #
+
+## Core ##
+
+## Plugins ##
+
+### mode.py ###
+- Named modes with different settings
+- Connects To: (MODE_CONFIG, MODE_CONFIRM)
+- Watches: mode, default_mode
+- Emits: MODE_CHANGED, MODE_CONFIRM
+
+Changes between modes configured with MODE_CONFIG when the mode variable changes.
+
+MODE_CONFIG <mode> <var> = <value>
+ configures `mode` to have `var` set to `value`
+
+MODE_CONFIRM <mode>
+ Emitted when the mode has changed with a round trip to uzbl-core to allow
+ the settings to take effect.
+ emits MODE_CHANGE if `mode` matches the current mode
+
+MODE_CHANGE <mode>
+ Emitted when the mode has changed
+
+
+### keycmd.py ###
+- Tracks the currently entered command
+- Connects To: FOCUS_GAINED, FOCUS_LOST, KEY_PRESS, KEY_RELEASE, (APPEND_KEYCMD,
+ IGNORE_KEY, INJECT_KEYCMD, KEYCMD_BACKSPACE, KEYCMD_DELETE,
+ KEYCMD_EXEC_CURRENT, KEYCMD_STRIP_WORD, MODKEY_ADDITION, MODMAP,
+ SET_CURSOR_POS, SET_KEYCMD)
+- Emits: KEYCMD_UPDATE, KEYCMD_EXEC, MODCMD_UPDATE, MODCMD_EXEC
+
+Maintains a command line that is manipulated by simple keypresses and a number
+of events.
+
+
+### bind.py ###
+- Provides support for key bindings
+- Connects To: (BIND, MODE_BIND, MODE_CHANGED, KEYCMD_UPDATE, KEYCMD_EXEC,
+ MODCMD_UPDATE, MODCMD_EXEC)
+- Emits: EXEC_BIND
+
+Listens for changes in keycmd and modcmd and executes bindings configured by
+BIND and MODE_BIND.
+
+BIND <bind> = <command>
+ short hand for MODE_BIND global <bind> = <command>
+
+MODE_BIND <mode> <bind> = <command>
+ Makes <bind> execute <command> while the current mode is matched by `mode`.
+ `mode` is a comma separated list of modes in which this binding should
+ apply. The special mode 'global' will match all modes except any modes
+ excluded by prefixing them with '-'.
+
+ e.g
+ MODE_BIND global,-insert <Up> = scroll vertical -20
+ will make the Up-key scroll up in all modes except insert
+
+EXEC_BIND <bind> <args> <kwargs>
+ Emitted before executing `bind` with `args` as arguments and `kwargs` as
+ keyword arguments. `bind` is a Bind instance, <args> a sequence and <kwargs>
+ a dictionary.
+
+
+### cookies.py ###
+- Cookie synchronization and persistence
+- Connects To: ADD_COOKIE, DELETE_COOKIE, (BLACKLIST_COOKIE, WHITELIST_COOKIE)
+
+This plugin acts on the (ADD|DELETE)_COOKIE events by issuing add_cookie or
+delete_cookie commands as appropriate to other connected uzbl instances.
+However if the cookie is blacklisted (see below) the cookie will not be
+forwarded and instead delete_cookie will be sent to the source so that the
+cookie will not be included in future HTTP requests.
+
+This plugin also maintains a mozilla cookies.txt compatible file with all your
+persistent cookies in $XDG_DATA_HOME/uzbl/cookies.txt and all your session
+cookies in $XDG_DATA_HOME/uzbl/session-cookies.txt.
+
+The blacklist is configured using the BLACKLIST_COOKIE and WHITELIST_COOKIE
+events. If any whitelist is set, then any cookie that is not whitelisted will
+be rejected. Otherwise, only cookies that have been blacklisted will be
+rejected.
+
+BLACKLIST_COOKIE <part> <re>
+ Adds a new blacklist filter. cookies where the component specified by
+ `part` matches the regular expression `re` will be filtered. part can be
+ either 0-5 or any of the symbolic names domain, path, name, value, scheme,
+ expires
+
+ for example to block all cookies which name is "__utm" followed by a single
+ character (google analytics cookies) do.
+ request BLACKLIST_COOKIE name '^__utm.$'
+
+WHITELIST_COOKIE <part> <re>
+ Adds a new whitelist filter. cookies where the component specified by
+ `part` matches the regular expression `re` will be allowed. part can be any
+ of the parts allowed for the BLACKLIST_COOKIE event
diff --git a/examples/config/config b/examples/config/config
index 3c1a2fe..a5b6edb 100644
--- a/examples/config/config
+++ b/examples/config/config
@@ -3,8 +3,10 @@
# === Core settings ==========================================================
-# Install location prefix.
-set prefix = /usr/local
+# common directory locations
+set prefix = @(echo $PREFIX)@
+set data_home = @(echo $XDG_DATA_HOME)@
+set cache_home = @(echo $XDG_CACHE_HOME)@
# Interface paths.
set fifo_dir = /tmp
@@ -34,15 +36,15 @@ set set_mode = set mode =
set set_status = set status_message =
# Spawn path shortcuts. In spawn the first dir+path match is used in "dir1:dir2:dir3:executable"
-set scripts_dir = $XDG_DATA_HOME/uzbl:@prefix/share/uzbl/examples/data:scripts
+set scripts_dir = @data_home/uzbl:@prefix/share/uzbl/examples/data:scripts
# === Hardcoded handlers =====================================================
# These handlers can't be moved to the new event system yet as we don't
# support events that can wait for a response from a script.
-set cookie_handler = talk_to_socket $XDG_CACHE_HOME/uzbl/cookie_daemon_socket
set scheme_handler = sync_spawn @scripts_dir/scheme.py
set authentication_handler = sync_spawn @scripts_dir/auth.py
+set download_handler = sync_spawn @scripts_dir/download.sh
# === Dynamic event handlers =================================================
@@ -53,9 +55,6 @@ set authentication_handler = sync_spawn @scripts_dir/auth.py
# Open in new tab
#@on_event NEW_WINDOW event NEW_TAB %s
-# Download handler
-@on_event DOWNLOAD_REQUEST spawn @scripts_dir/download.sh %s \@proxy_url
-
# Load start handler
@on_event LOAD_START @set_status <span foreground="khaki">wait</span>
# Reset the keycmd on navigation
@@ -64,8 +63,8 @@ set authentication_handler = sync_spawn @scripts_dir/auth.py
# Load commit handlers
@on_event LOAD_COMMIT @set_status <span foreground="green">recv</span>
-# Userscript support. Add all scripts to $XDG_DATA_HOME/uzbl/userscripts
-#@on_event LOAD_COMMIT spawn @scripts_dir/userscripts.sh
+# 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
# Load finish handlers
@on_event LOAD_FINISH @set_status <span foreground="gold">done</span>
@@ -104,7 +103,9 @@ set name_section = <span foreground="khaki">\@[\@NAME]\@</span>
set status_section = <span foreground="orange">\@status_message</span>
set selected_section = <span foreground="#606060">\@[\@SELECTED_URI]\@</span>
-set status_format = <span font_family="monospace">@mode_section @keycmd_section @progress_section @uri_section @name_section @status_section @scroll_section @selected_section</span>
+set download_section = <span foreground="white">\@downloads</span>
+
+set status_format = <span font_family="monospace">@mode_section @keycmd_section @progress_section @uri_section @name_section @status_section @scroll_section @selected_section @download_section</span>
set title_format_long = \@keycmd_prompt \@raw_modcmd \@raw_keycmd \@TITLE - Uzbl browser <\@NAME> \@SELECTED_URI
@@ -120,6 +121,10 @@ set progress.pending =
set useragent = Uzbl (Webkit @{WEBKIT_MAJOR}.@{WEBKIT_MINOR}.@{WEBKIT_MICRO}) (@(+uname -sm)@ [@ARCH_UZBL]) (Commit @COMMIT)
+# === Configure cookie blacklist ========================================================
+# Drop google analytics tracking cookies
+#request BLACKLIST_COOKIE name '^__utm.$'
+
# === Key binding configuration ==============================================
# --- Internal modmapping and ignoring ---------------------------------------
@@ -251,8 +256,8 @@ set ebind = @mode_bind global,-insert
# Use socat to directly inject commands into uzbl-core and view events
# raised by uzbl-core:
-@cbind <Ctrl><Alt>t = sh "xterm -e 'socat unix-connect:\"$UZBL_SOCKET\" -'"
-#@cbind <Ctrl><Alt>t = sh "urxvt -e 'socat unix-connect:\"$UZBL_SOCKET\" -'"
+@cbind <Ctrl><Alt>t = sh 'xterm -e "socat unix-connect:\"$UZBL_SOCKET\" -"'
+#@cbind <Ctrl><Alt>t = sh 'urxvt -e socat unix-connect:"$UZBL_SOCKET" -'
# Uri opening prompts
@cbind o<uri:>_ = uri %s
@@ -389,6 +394,8 @@ set stack = @mode_config stack
set default_mode = command
# === Post-load misc commands ================================================
+sync_spawn_exec @scripts_dir/load_cookies.sh
+sync_spawn_exec @scripts_dir/load_cookies.sh @data_home/uzbl/session-cookies.txt
# Set the "home" page.
set uri = uzbl.org/doesitwork/@COMMIT
diff --git a/examples/data/per-site-settings b/examples/data/per-site-settings
new file mode 100644
index 0000000..78bade4
--- /dev/null
+++ b/examples/data/per-site-settings
@@ -0,0 +1,3 @@
+.*
+ .*/\d+-\w+/(thread|subject|author|date).html
+ script @data_home/uzbl/scripts/pipermail.js
diff --git a/examples/data/plugins/cookies.py b/examples/data/plugins/cookies.py
new file mode 100644
index 0000000..c9fe2c3
--- /dev/null
+++ b/examples/data/plugins/cookies.py
@@ -0,0 +1,176 @@
+""" Basic cookie manager
+ forwards cookies to all other instances connected to the event manager"""
+
+from collections import defaultdict
+import os, re
+
+# these are symbolic names for the components of the cookie tuple
+symbolic = {'domain': 0, 'path':1, 'name':2, 'value':3, 'scheme':4, 'expires':5}
+
+_splitquoted = re.compile("( |\\\".*?\\\"|'.*?')")
+def splitquoted(text):
+ return [str(p.strip('\'"')) for p in _splitquoted.split(text) if p.strip()]
+
+# allows for partial cookies
+# ? allow wildcard in key
+def match(key, cookie):
+ for k,c in zip(key,cookie):
+ if k != c:
+ return False
+ return True
+
+class NullStore(object):
+ def add_cookie(self, rawcookie, cookie):
+ pass
+
+ def delete_cookie(self, rkey, key):
+ pass
+
+class ListStore(list):
+ def add_cookie(self, rawcookie, cookie):
+ self.append(rawcookie)
+
+ def delete_cookie(self, rkey, key):
+ self[:] = [x for x in self if not match(key, splitquoted(x))]
+
+class TextStore(object):
+ def __init__(self, filename):
+ self.filename = filename
+
+ def as_event(self, cookie):
+ if cookie[0].startswith("#HttpOnly_"):
+ domain = cookie[0][len("#HttpOnly_"):]
+ elif cookie[0].startswith('#'):
+ return None
+ else:
+ domain = cookie[0]
+ return (domain,
+ cookie[2],
+ cookie[5],
+ cookie[6],
+ 'https' if cookie[3] == 'TRUE' else 'http',
+ cookie[4])
+
+ def as_file(self, cookie):
+ return (cookie[0],
+ 'TRUE' if cookie[0].startswith('.') else 'FALSE',
+ cookie[1],
+ 'TRUE' if cookie[4] == 'https' else 'FALSE',
+ cookie[5],
+ cookie[2],
+ cookie[3])
+
+ def add_cookie(self, rawcookie, cookie):
+ assert len(cookie) == 6
+
+ # delete equal cookies (ignoring expire time, value and secure flag)
+ self.delete_cookie(None, cookie[:-3])
+
+ first = not os.path.exists(self.filename)
+ with open(self.filename, 'a') as f:
+ if first:
+ print >> f, "# HTTP Cookie File"
+ print >> f, '\t'.join(self.as_file(cookie))
+
+ def delete_cookie(self, rkey, key):
+ if not os.path.exists(self.filename):
+ return
+
+ # read all cookies
+ with open(self.filename, 'r') as f:
+ cookies = f.readlines()
+
+ # write those that don't match the cookie to delete
+ with open(self.filename, 'w') as f:
+ for l in cookies:
+ c = self.as_event(l.split('\t'))
+ if c is None or not match(key, c):
+ print >> f, l,
+
+xdg_data_home = os.environ.get('XDG_DATA_HOME', os.path.join(os.environ['HOME'], '.local/share'))
+DefaultStore = TextStore(os.path.join(xdg_data_home, 'uzbl/cookies.txt'))
+SessionStore = TextStore(os.path.join(xdg_data_home, 'uzbl/session-cookies.txt'))
+
+def match_list(_list, cookie):
+ for component, match in _list:
+ if match(cookie[component]) is not None:
+ return True
+ return False
+
+# accept a cookie only when:
+# a. there is no whitelist and the cookie is in the blacklist
+# b. the cookie is in the whitelist and not in the blacklist
+def accept_cookie(uzbl, cookie):
+ if uzbl.cookie_whitelist:
+ if match_list(uzbl.cookie_whitelist, cookie):
+ return not match_list(uzbl.cookie_blacklist, cookie)
+ return False
+
+ return not match_list(uzbl.cookie_blacklist, cookie)
+
+def expires_with_session(uzbl, cookie):
+ return cookie[5] == ''
+
+def get_recipents(uzbl):
+ """ get a list of Uzbl instances to send the cookie too. """
+ # This could be a lot more interesting
+ return [u for u in uzbl.parent.uzbls.values() if u is not uzbl]
+
+def get_store(uzbl, session=False):
+ if session:
+ return SessionStore
+ return DefaultStore
+
+def add_cookie(uzbl, cookie):
+ splitted = splitquoted(cookie)
+ if accept_cookie(uzbl, splitted):
+ for u in get_recipents(uzbl):
+ u.send('add_cookie %s' % cookie)
+
+ get_store(uzbl, expires_with_session(uzbl, splitted)).add_cookie(cookie, splitted)
+ else:
+ logger.debug('cookie %r is blacklisted' % splitted)
+ uzbl.send('delete_cookie %s' % cookie)
+
+def delete_cookie(uzbl, cookie):
+ for u in get_recipents(uzbl):
+ u.send('delete_cookie %s' % cookie)
+
+ splitted = splitquoted(cookie)
+ if len(splitted) == 6:
+ get_store(uzbl, expires_with_session(uzbl, splitted)).delete_cookie(cookie, splitted)
+ else:
+ for store in set([get_store(uzbl, session) for session in (True, False)]):
+ store.delete_cookie(cookie, splitted)
+
+# add a cookie matcher to a whitelist or a blacklist.
+# a matcher is a (component, re) tuple that matches a cookie when the
+# "component" part of the cookie matches the regular expression "re".
+# "component" is one of the keys defined in the variable "symbolic" above,
+# or the index of a component of a cookie tuple.
+def add_cookie_matcher(_list, arg):
+ component, regexp = splitquoted(arg)
+ try:
+ component = symbolic[component]
+ except KeyError:
+ component = int(component)
+ assert component <= 5
+ _list.append((component, re.compile(regexp).search))
+
+def blacklist(uzbl, arg):
+ add_cookie_matcher(uzbl.cookie_blacklist, arg)
+
+def whitelist(uzbl, arg):
+ add_cookie_matcher(uzbl.cookie_whitelist, arg)
+
+def init(uzbl):
+ connect_dict(uzbl, {
+ 'ADD_COOKIE': add_cookie,
+ 'DELETE_COOKIE': delete_cookie,
+ 'BLACKLIST_COOKIE': blacklist,
+ 'WHITELIST_COOKIE': whitelist
+ })
+ export_dict(uzbl, {
+ 'cookie_blacklist' : [],
+ 'cookie_whitelist' : []
+ })
diff --git a/examples/data/plugins/downloads.py b/examples/data/plugins/downloads.py
new file mode 100644
index 0000000..7bf32d7
--- /dev/null
+++ b/examples/data/plugins/downloads.py
@@ -0,0 +1,69 @@
+# this plugin does a very simple display of download progress. to use it, add
+# @downloads to your status_format.
+
+import os
+ACTIVE_DOWNLOADS = {}
+
+# after a download's status has changed this is called to update the status bar
+def update_download_section(uzbl):
+ global ACTIVE_DOWNLOADS
+
+ if len(ACTIVE_DOWNLOADS):
+ # add a newline before we list downloads
+ result = '&#10;downloads:'
+ for path in ACTIVE_DOWNLOADS:
+ # add each download
+ fn = os.path.basename(path)
+ progress, = ACTIVE_DOWNLOADS[path]
+
+ dl = " %s (%d%%)" % (fn, progress * 100)
+
+ # replace entities to make sure we don't break our markup
+ # (this could be done with an @[]@ expansion in uzbl, but then we
+ # can't use the &#10; above to make a new line)
+ dl = dl.replace("&", "&amp;").replace("<", "&lt;")
+ result += dl
+ else:
+ result = ''
+
+ # and the result gets saved to an uzbl variable that can be used in
+ # status_format
+ if uzbl.config.get('downloads', '') != result:
+ uzbl.config['downloads'] = result
+
+def download_started(uzbl, destination_path):
+ # add to the list of active downloads
+ global ACTIVE_DOWNLOADS
+ ACTIVE_DOWNLOADS[destination_path] = (0.0,)
+
+ # update the progress
+ update_download_section(uzbl)
+
+def download_progress(uzbl, args):
+ # parse the arguments
+ s = args.rindex(' ')
+ destination_path = args[:s]
+ progress = float(args[s+1:])
+
+ # update the progress
+ global ACTIVE_DOWNLOADS
+ ACTIVE_DOWNLOADS[destination_path] = (progress,)
+
+ # update the status bar variable
+ update_download_section(uzbl)
+
+def download_complete(uzbl, destination_path):
+ # remove from the list of active downloads
+ global ACTIVE_DOWNLOADS
+ del ACTIVE_DOWNLOADS[destination_path]
+
+ # update the status bar variable
+ update_download_section(uzbl)
+
+# plugin init hook
+def init(uzbl):
+ connect_dict(uzbl, {
+ 'DOWNLOAD_STARTED': download_started,
+ 'DOWNLOAD_PROGRESS': download_progress,
+ 'DOWNLOAD_COMPLETE': download_complete,
+ })
diff --git a/examples/data/scripts/download.sh b/examples/data/scripts/download.sh
index de03b1f..df7a571 100755
--- a/examples/data/scripts/download.sh
+++ b/examples/data/scripts/download.sh
@@ -1,26 +1,26 @@
#!/bin/sh
-# just an example of how you could handle your downloads
-# try some pattern matching on the uri to determine what we should do
+#
+# uzbl's example configuration sets this script up as its download_handler.
+# when uzbl starts a download it runs this script.
+# if the script prints a file path to stdout, uzbl will save the download to
+# that path.
+# if nothing is printed to stdout, the download will be cancelled.
shift 7
. $UZBL_UTIL_DIR/uzbl-dir.sh
-# Some sites block the default wget --user-agent..
-GET="wget --user-agent=Firefox --content-disposition --load-cookies=$UZBL_COOKIE_JAR"
+# the URL that is being downloaded
+uri=$1
-url="$1"
+# a filename suggested by the server or based on the URL
+suggested_filename=${2:-$(echo "$uri" | sed 's/\W/-/g')}
-http_proxy="$2"
-export http_proxy
+# the mimetype of the file being downloaded
+content_type=$3
-if [ -z "$url" ]; then
- echo "you must supply a url! ($url)"
- exit 1
-fi
+# the size of the downloaded file in bytes. this is not always accurate, since
+# the server might not have sent a size with its response headers.
+total_size=$4
-# only changes the dir for the $get sub process
-if echo "$url" | grep -E '.*\.torrent' >/dev/null; then
- ( cd "$UZBL_DOWNLOAD_DIR"; $GET "$url")
-else
- ( cd "$UZBL_DOWNLOAD_DIR"; $GET "$url")
-fi
+# just save the file to the default directory with the suggested name
+echo $UZBL_DOWNLOAD_DIR/$suggested_filename
diff --git a/examples/data/scripts/load_cookies.sh b/examples/data/scripts/load_cookies.sh
new file mode 100755
index 0000000..f4c6886
--- /dev/null
+++ b/examples/data/scripts/load_cookies.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+if [ "$8" != "" ]; then
+ cookie_file=$8
+else
+ cookie_file=${XDG_DATA_HOME:-$HOME/.local/share}/uzbl/cookies.txt
+fi
+
+awk -F \\t '
+BEGIN {
+ scheme["TRUE"] = "https";
+ scheme["FALSE"] = "http";
+}
+$0 ~ /^#HttpOnly_/ {
+printf("add_cookie \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"\n", substr($1,length("#HttpOnly_"),length($1)), $3, $6, $7, scheme[$4], $5)
+}
+$0 !~ /^#/ {
+printf("add_cookie \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"\n", $1, $3, $6, $7, scheme[$4], $5)
+}
+' $cookie_file
diff --git a/examples/data/scripts/load_url_from_history.sh b/examples/data/scripts/load_url_from_history.sh
index 4782cca..59ad492 100755
--- a/examples/data/scripts/load_url_from_history.sh
+++ b/examples/data/scripts/load_url_from_history.sh
@@ -12,7 +12,7 @@ DMENU_OPTIONS="xmms vertical resize"
# goto=$(awk '{print $3}' $history_file | sort -u | dmenu -i)
if [ -z "$DMENU_HAS_VERTICAL" ]; then
current=$(tail -n 1 "$UZBL_HISTORY_FILE" | awk '{print $3}');
- goto=$((echo $current; awk '{print $3}' "$UZBL_HISTORY_FILE" | grep -v "^$current\$" | sort -u) | $DMENU)
+ goto=$( (echo $current; awk '{print $3}' "$UZBL_HISTORY_FILE" | grep -v "^$current\$" | sort -u) | $DMENU)
else
# choose an item in reverse order, showing also the date and page titles
# pick the last field from the first 3 fields. this way you can pick a url (prefixed with date & time) or type just a new url.
diff --git a/examples/data/scripts/per-site-settings.py b/examples/data/scripts/per-site-settings.py
new file mode 100755
index 0000000..febfd0f
--- /dev/null
+++ b/examples/data/scripts/per-site-settings.py
@@ -0,0 +1,117 @@
+#!/usr/bin/env python
+# Per-site settings plugin
+
+# Example configuration usage:
+#
+# @on_event LOAD_COMMIT spawn @scripts_dir/per-site-settings.py @data_home/uzbl/per-site-settings
+
+# Format of the settings file:
+#
+# <url>
+# <path>
+# <command>
+#
+# - url
+# May either be a regex, or literal. If literal, it will block any
+# subdomains as well.
+# - path
+# May either be a regex, or literal. If literal, it will block any
+# decendent paths as well.
+# - command
+# Given to uzbl verbatim.
+#
+# Matches are attempted on a literal match first.
+#
+# Any of the specifications can be repeated and acts as a fall-through to the
+# next level. Make sure indentation lines up locally. Any indentation addition
+# is considered as a fall through to the next level and any decrease is
+# considered a pop back (zero is always urls). This works because it's only 3
+# deep. Four and we'd have to keep track of things.
+
+import os
+import re
+import socket
+import stat
+import subprocess
+import tempfile
+import urlparse
+import sys
+
+
+def match_url(url, patt):
+ return url.endswith(patt) or re.match(patt, url)
+
+
+def match_path(path, patt):
+ return path.startswith(patt) or re.match(patt, path)
+
+
+def grep_url(url, path, fin):
+ entries = []
+ cur_indent = 0
+ passing = [False, False]
+ # 0 == url
+ # 1 == path
+ # 2 == command
+ state = 0
+ for line in fin:
+ raw = line.strip()
+
+ indent = len(line) - len(raw) - 1
+ if not indent:
+ # Reset state
+ passing = [False, False]
+ state = 0
+ else:
+ # previous level
+ if indent < cur_indent:
+ if state == 1:
+ passing[0] = False
+ elif state == 2:
+ passing[1] = False
+ state -= 1
+ # next level
+ elif cur_indent < indent:
+ state += 1
+
+ # parse the line
+ if state == 0:
+ if not passing[0] and match_url(url, raw):
+ passing[0] = True
+ elif state == 1 and passing[0]:
+ if not passing[1] and match_path(path, raw):
+ passing[1] = True
+ elif state == 2 and passing[1]:
+ entries.append(raw)
+
+ cur_indent = indent
+
+ return entries
+
+
+def write_to_socket(commands, sockpath):
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ sock.connect(sockpath)
+ for command in commands:
+ sock.send(command)
+ sock.close()
+
+
+if __name__ == '__main__':
+ sockpath = os.environ['UZBL_SOCKET']
+ url = urlparse.urlparse(os.environ['UZBL_URI'])
+ filepath = sys.argv[8]
+
+ mode = os.stat(filepath)[stat.ST_MODE]
+
+ if mode & stat.S_IEXEC:
+ fin = tempfile.TemporaryFile()
+ subprocess.Popen([filepath], stdout=fin).wait()
+ else:
+ fin = open(filepath, 'r')
+
+ commands = grep_url(url.hostname, url.path, fin)
+
+ fin.close()
+
+ write_to_socket(commands, sockpath)
diff --git a/examples/data/scripts/pipermail.js b/examples/data/scripts/pipermail.js
new file mode 100644
index 0000000..5ec4aa4
--- /dev/null
+++ b/examples/data/scripts/pipermail.js
@@ -0,0 +1,71 @@
+// this is a userscript inspired by "Pipermail Navigation Links" by Michael
+// Stone <http://userscripts.org/scripts/show/3174>.
+
+// it adds previous month/next month navigation links in pipermail mailing
+// list archives.
+
+// we wrap the whole thing in a function (that gets called immediately) so
+// that this script doesn't interfere with any javascript in the page.
+(function() {
+
+// figure out what page we're looking at right now
+var urlparts = document.location.toString().split("/");
+var currView = urlparts[urlparts.length-1].split("#")[0];
+var currDate = urlparts[urlparts.length-2].split("-");
+
+// figure out the URLs to the next month and previous month
+var months = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July',
+ 'August', 'September', 'October', 'November', 'December' ];
+
+var thisMonth = currDate[1];
+var prevMonth;
+var nextMonth;
+
+var thisYear = currDate[0];
+var prevYear = thisYear;
+var nextYear = thisYear;
+
+if(thisMonth == 'January') {
+ prevMonth = "December";
+ nextMonth = "February";
+ prevYear = parseInt(thisYear) - 1;
+} else if(thisMonth == 'December') {
+ prevMonth = "November";
+ nextMonth = "January";
+ nextYear = parseInt(thisYear) + 1;
+} else {
+ var monthNum = months.indexOf(thisMonth);
+ prevMonth = months[monthNum - 1];
+ nextMonth = months[monthNum + 1];
+}
+
+var prevHref = "../" + prevYear + "-" + prevMonth + "/" + currView;
+var nextHref = "../" + nextYear + "-" + nextMonth + "/" + currView;
+
+// find the navigation header and footer
+var selector = "a[href='date.html#start']";
+
+// if we're on a "date" page then the date link isn't displayed
+if(currView == "date.html")
+ selector = "a[href='author.html#start']";
+
+var navLinks = document.querySelectorAll(selector);
+
+// append the prev/next links to the navigation header and footer
+for(var i = 0; i < navLinks.length; i++) {
+ var victim = navLinks[i].parentNode;
+
+ var prevEl = document.createElement("a");
+ prevEl.textContent = "[ prev month ]";
+ prevEl.href = prevHref;
+
+ var nextEl = document.createElement("a");
+ nextEl.textContent = "[ next month ]";
+ nextEl.href = nextHref;
+
+ victim.appendChild(prevEl);
+ victim.appendChild(document.createTextNode(" "));
+ victim.appendChild(nextEl);
+}
+
+})();
diff --git a/examples/data/scripts/session.sh b/examples/data/scripts/session.sh
index c5c4172..36e0c19 100755
--- a/examples/data/scripts/session.sh
+++ b/examples/data/scripts/session.sh
@@ -1,14 +1,17 @@
#!/bin/sh
#
# Very simple session manager for uzbl-browser.
-# To use, add a line like 'bind quit = spawn @scripts_dir/session.sh endsession'
-# to your config.
-# To restore the session, run this script with the argument "launch". An
-# instance of uzbl-browser will be launched for each stored url.
+# To use, add a line like 'bind quit = spawn @scripts_dir/session.sh' to your
+# config. This binding will exit every instance of uzbl and store the URLs they
+# had open in $UZBL_SESSION_FILE.
#
-# When called with "endsession" as the argument, it will backup
-# $UZBL_SESSION_FILE, look for fifos in $UZBL_FIFO_DIR and instruct each of them
-# to store its current url in $UZBL_SESSION_FILE and terminate.
+# When a session file exists this script can be run with no arguments (or the
+# argument "launch") to start an instance of uzbl-browser for every stored url.
+#
+# If no session file exists (or if called with "endsession" as the first
+# argument), this script looks for instances of uzbl that have fifos in
+# $UZBL_FIFO_DIR and instructs each of them to store its current url in
+# $UZBL_SESSION_FILE and terminate.
#
# "endinstance" is used internally and doesn't need to be called manually.
@@ -33,9 +36,13 @@ if [ $# -gt 1 ]; then
shift 7
fi
-scriptfile=$0 # this script
+scriptfile=$(readlink -f $0) # this script
act="$1"
+if [ -z "$act" ]; then
+ [ -f "$UZBL_SESSION_FILE" ] && act="launch" || act="endsession"
+fi
+
case $act in
"launch" )
urls=$(cat "$UZBL_SESSION_FILE")
@@ -44,8 +51,8 @@ case $act in
else
for url in $urls; do
$UZBL --uri "$url" &
- disown
done
+ mv "$UZBL_SESSION_FILE" "$UZBL_SESSION_FILE~"
fi
;;
@@ -59,13 +66,12 @@ case $act in
;;
"endsession" )
- mv "$UZBL_SESSION_FILE" "$UZBL_SESSION_FILE~"
for fifo in "$UZBL_FIFO_DIR"/uzbl_fifo_*; do
if [ "$fifo" != "$UZBL_FIFO" ]; then
echo "spawn $scriptfile endinstance" > "$fifo"
fi
done
- echo "spawn $scriptfile endinstance" > "$UZBL_FIFO"
+ [ -z "$UZBL_FIFO" ] || echo "spawn $scriptfile endinstance" > "$UZBL_FIFO"
;;
* )
diff --git a/examples/data/scripts/userscript.sh b/examples/data/scripts/userscript.sh
deleted file mode 100755
index fd95fdc..0000000
--- a/examples/data/scripts/userscript.sh
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/bin/sh
-
-if [ $# = "3" ]
-then
- UZBL_FIFO=$1
- UZBL_URI=$2
- SCRIPT=$3
-else
- SCRIPT=$8
-fi
-
-# Extract metadata chunk
-META="`sed -ne '/^\s*\/\/\s*==UserScript==\s*$/,/^\s*\/\/\s*==\/UserScript==\s*$/p' "$SCRIPT"`"
-SHOULD_RUN=false # Assume this script will not be included
-# Loop over all include rules
-for INCLUDE in `echo "$META" | grep "^\s*\/\/\s*@include"`; do
- # Munge into grep pattern
- INCLUDE="`echo "$INCLUDE" | sed -e 's/^\s*\/\/\s*@include\s*//' -e 's/\./\\\\./g' -e 's/\*/.*/g' -e 's/[\r\n]//g'`"
- if echo "$UZBL_URI" | grep -x "$INCLUDE"; then
- SHOULD_RUN=true
- break
- fi
-done
-
-# Loop over all exclude rules
-for EXCLUDE in `echo "$META" | grep "^\s*\/\/\s*@exclude"`; do
- # Munge into grep pattern
- EXCLUDE="`echo "$EXCLUDE" | sed -e 's/^\s*\/\/\s*@exclude\s*//' -e 's/\./\\\\./g' -e 's/\*/.*/g' -e 's/[\r\n]//g'`"
- if echo "$UZBL_URI" | grep -x "$EXCLUDE"; then
- SHOULD_RUN=false
- break
- fi
-done
-
-# Run the script
-if [ $SHOULD_RUN = true ]; then
- echo "script '$SCRIPT'" > "$UZBL_FIFO"
-fi
diff --git a/examples/data/scripts/userscripts.sh b/examples/data/scripts/userscripts.sh
deleted file mode 100755
index 4f76c90..0000000
--- a/examples/data/scripts/userscripts.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/sh
-
-scripts_dir="$XDG_DATA_HOME/uzbl/userscripts"
-
-for SCRIPT in $(grep -rlx "\s*//\s*==UserScript==\s*" "$scripts_dir")
-do
- $XDG_DATA_HOME/uzbl/scripts/userscript.sh "$UZBL_FIFO" "$UZBL_URI" "$SCRIPT"
-done
diff --git a/examples/data/scripts/util/dmenu.sh b/examples/data/scripts/util/dmenu.sh
index f789178..da61cae 100644
--- a/examples/data/scripts/util/dmenu.sh
+++ b/examples/data/scripts/util/dmenu.sh
@@ -67,7 +67,7 @@ if dmenu --help 2>&1 | grep -q '\[-xs\]'; then
fi
# Detect the vertical patch
-if dmenu --help 2>&1 | grep -q '\[-l <lines>\]'; then
+if dmenu --help 2>&1 | grep -q '\[-l <\?lines>\?\]'; then
# Default to 10 lines
if [ -z "$DMENU_LINES" ]; then
DMENU_LINES=10
diff --git a/examples/data/scripts/uzbl-tabbed b/examples/data/scripts/uzbl-tabbed
index 9e5d715..0086c04 100755
--- a/examples/data/scripts/uzbl-tabbed
+++ b/examples/data/scripts/uzbl-tabbed
@@ -471,8 +471,9 @@ class UzblInstance:
elif var == "gtk_tab_pos":
self.parent.update_gtk_tab_pos()
elif var == "status_background":
- col = gtk.gdk.color_parse(config['status_background'])
- self.parent.ebox.modify_bg(gtk.STATE_NORMAL, col)
+ if config['status_background'].strip():
+ col = gtk.gdk.color_parse(config['status_background'])
+ self.parent.ebox.modify_bg(gtk.STATE_NORMAL, col)
elif var == "tab_titles" or var == "tab_indexes":
for tab in self.parent.notebook:
self.parent.tabs[tab].title_changed(True)
diff --git a/misc/env.sh b/misc/env.sh
index 0dfedfa..68377f2 100755
--- a/misc/env.sh
+++ b/misc/env.sh
@@ -10,7 +10,7 @@
# Maybe we should spawn processes from here with an 'exec' at the end?
# Re-define our home location inside the sandbox dir.
-export HOME=./sandbox/home
+export HOME=$(pwd)/sandbox/home
# Export default XDG_{DATA,CACHE,..}_HOME locations inside the sandbox
# directory according to defaults in the xdg specification.
@@ -20,4 +20,4 @@ export XDG_CACHE_HOME=$HOME/.cache
export XDG_CONFIG_HOME=$HOME/.config
# Needed to run uzbl-browser etc from here.
-export PATH="./sandbox/usr/local/bin:$PATH"
+export PATH="$(pwd)/sandbox/usr/local/bin:$PATH"
diff --git a/src/callbacks.c b/src/callbacks.c
index 4f0e4ac..673979e 100644
--- a/src/callbacks.c
+++ b/src/callbacks.c
@@ -324,7 +324,8 @@ cmd_scrollbars_visibility() {
uzbl.gui.bar_v = gtk_range_get_adjustment (GTK_RANGE (uzbl.gui.scbar_v));
uzbl.gui.bar_h = gtk_range_get_adjustment (GTK_RANGE (uzbl.gui.scbar_h));
}
- gtk_widget_set_scroll_adjustments (GTK_WIDGET (uzbl.gui.web_view), uzbl.gui.bar_h, uzbl.gui.bar_v);
+
+ set_webview_scroll_adjustments();
}
/* requires webkit >=1.1.14 */
@@ -745,41 +746,139 @@ create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer us
(void) web_view;
(void) frame;
(void) user_data;
- if (uzbl.state.selected_url != NULL) {
- if (uzbl.state.verbose)
- printf("\nNew web view -> %s\n", uzbl.state.selected_url);
- if (strncmp(uzbl.state.selected_url, "javascript:", strlen("javascript:")) == 0) {
- WebKitWebView* new_view = WEBKIT_WEB_VIEW(webkit_web_view_new());
+ if (uzbl.state.verbose)
+ printf("New web view -> javascript link...\n");
- g_signal_connect (new_view, "web-view-ready",
- G_CALLBACK(create_web_view_js_cb), NULL);
+ WebKitWebView* new_view = WEBKIT_WEB_VIEW(webkit_web_view_new());
- return new_view;
- }
- else
- send_event(NEW_WINDOW, uzbl.state.selected_url, NULL);
+ g_signal_connect (new_view, "web-view-ready",
+ G_CALLBACK(create_web_view_js_cb), NULL);
+ return new_view;
+}
- } else {
- if (uzbl.state.verbose)
- printf("New web view -> javascript link...\n");
+void
+download_progress_cb(WebKitDownload *download, GParamSpec *pspec, gpointer user_data) {
+ (void) pspec; (void) user_data;
+
+ gdouble progress;
+ g_object_get(download, "progress", &progress, NULL);
- WebKitWebView* new_view = WEBKIT_WEB_VIEW(webkit_web_view_new());
+ const gchar *dest_uri = webkit_download_get_destination_uri(download);
+ const gchar *dest_path = dest_uri + strlen("file://");
- g_signal_connect (new_view, "web-view-ready",
- G_CALLBACK(create_web_view_js_cb), NULL);
- return new_view;
+ gchar *details = g_strdup_printf("%s %.2lf", dest_path, progress);
+ send_event(DOWNLOAD_PROGRESS, details, NULL);
+ g_free(details);
+}
+
+void
+download_status_cb(WebKitDownload *download, GParamSpec *pspec, gpointer user_data) {
+ (void) pspec; (void) user_data;
+
+ WebKitDownloadStatus status;
+ g_object_get(download, "status", &status, NULL);
+
+ switch(status) {
+ case WEBKIT_DOWNLOAD_STATUS_CREATED:
+ case WEBKIT_DOWNLOAD_STATUS_STARTED:
+ case WEBKIT_DOWNLOAD_STATUS_ERROR:
+ case WEBKIT_DOWNLOAD_STATUS_CANCELLED:
+ return; /* these are irrelevant */
+ case WEBKIT_DOWNLOAD_STATUS_FINISHED:
+ {
+ const gchar *dest_uri = webkit_download_get_destination_uri(download);
+ const gchar *dest_path = dest_uri + strlen("file://");
+ send_event(DOWNLOAD_COMPLETE, dest_path, NULL);
+ }
}
- return NULL;
}
gboolean
-download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
- (void) web_view;
- (void) user_data;
+download_cb(WebKitWebView *web_view, WebKitDownload *download, gpointer user_data) {
+ (void) web_view; (void) user_data;
- send_event(DOWNLOAD_REQ, webkit_download_get_uri ((WebKitDownload*)download), NULL);
- return (FALSE);
+ /* get the URI being downloaded */
+ const gchar *uri = webkit_download_get_uri(download);
+
+ if (uzbl.state.verbose)
+ printf("Download requested -> %s\n", uri);
+
+ if (!uzbl.behave.download_handler) {
+ webkit_download_cancel(download);
+ return FALSE; /* reject downloads when there's no download handler */
+ }
+
+ /* get a reasonable suggestion for a filename */
+ const gchar *suggested_filename;
+ g_object_get(download, "suggested-filename", &suggested_filename, NULL);
+
+ /* get the mimetype of the download */
+ const gchar *content_type;
+ WebKitNetworkResponse *r = webkit_download_get_network_response(download);
+ /* downloads can be initiated from the context menu, in that case there is
+ no network response yet and trying to get one would crash. */
+ if(WEBKIT_IS_NETWORK_RESPONSE(r)) {
+ SoupMessage *m = webkit_network_response_get_message(r);
+ SoupMessageHeaders *h;
+ g_object_get(m, "response-headers", &h, NULL);
+ content_type = soup_message_headers_get_one(h, "Content-Type");
+ } else
+ content_type = "application/octet-stream";
+
+ /* get the filesize of the download, as given by the server.
+ (this may be inaccurate, there's nothing we can do about that.) */
+ unsigned int total_size = webkit_download_get_total_size(download);
+
+ gchar *ev = g_strdup_printf("'%s' '%s' '%s' %d", uri, suggested_filename,
+ content_type, total_size);
+ run_handler(uzbl.behave.download_handler, ev);
+ g_free(ev);
+
+ /* no response, cancel the download */
+ if(!uzbl.comm.sync_stdout) {
+ webkit_download_cancel(download);
+ return FALSE;
+ }
+
+ /* no response, cancel the download */
+ if(uzbl.comm.sync_stdout[0] == 0) {
+ webkit_download_cancel(download);
+ uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
+ return FALSE;
+ }
+
+ /* we got a response, it's the path we should download the file to */
+ gchar *destination_path = uzbl.comm.sync_stdout;
+ uzbl.comm.sync_stdout = NULL;
+
+ /* presumably people don't need newlines in their filenames. */
+ char *p = strchr(destination_path, '\n');
+ if ( p != NULL ) *p = '\0';
+
+ /* set up progress callbacks */
+ g_signal_connect(download, "notify::status", G_CALLBACK(download_status_cb), NULL);
+ g_signal_connect(download, "notify::progress", G_CALLBACK(download_progress_cb), NULL);
+
+ /* convert relative path to absolute path */
+ if(destination_path[0] != '/') {
+ gchar *rel_path = destination_path;
+ gchar *cwd = g_get_current_dir();
+ destination_path = g_strconcat(cwd, "/", destination_path, NULL);
+ g_free(cwd);
+ g_free(rel_path);
+ }
+
+ send_event(DOWNLOAD_STARTED, destination_path, NULL);
+
+ /* convert absolute path to file:// URI */
+ gchar *destination_uri = g_strconcat("file://", destination_path, NULL);
+ g_free(destination_path);
+
+ webkit_download_set_destination_uri(download, destination_uri);
+ g_free(destination_uri);
+
+ return TRUE;
}
gboolean
@@ -854,14 +953,14 @@ populate_popup_cb(WebKitWebView *v, GtkMenu *m, void *c) {
(context & mi->context)) {
if(mi->issep) {
item = gtk_separator_menu_item_new();
- gtk_menu_append(GTK_MENU(m), item);
+ gtk_menu_shell_append(GTK_MENU_SHELL(m), item);
gtk_widget_show(item);
}
else {
item = gtk_menu_item_new_with_label(mi->name);
g_signal_connect(item, "activate",
G_CALLBACK(run_menu_command), mi->cmd);
- gtk_menu_append(GTK_MENU(m), item);
+ gtk_menu_shell_append(GTK_MENU_SHELL(m), item);
gtk_widget_show(item);
}
hit++;
@@ -872,14 +971,14 @@ populate_popup_cb(WebKitWebView *v, GtkMenu *m, void *c) {
!hit) {
if(mi->issep) {
item = gtk_separator_menu_item_new();
- gtk_menu_append(GTK_MENU(m), item);
+ gtk_menu_shell_append(GTK_MENU_SHELL(m), item);
gtk_widget_show(item);
}
else {
item = gtk_menu_item_new_with_label(mi->name);
g_signal_connect(item, "activate",
G_CALLBACK(run_menu_command), mi->cmd);
- gtk_menu_append(GTK_MENU(m), item);
+ gtk_menu_shell_append(GTK_MENU_SHELL(m), item);
gtk_widget_show(item);
}
}
diff --git a/src/callbacks.h b/src/callbacks.h
index a4258f2..40fa80d 100644
--- a/src/callbacks.h
+++ b/src/callbacks.h
@@ -133,9 +133,6 @@ cmd_load_start();
WebKitWebSettings*
view_settings();
-gboolean
-download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data);
-
void
toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result);
@@ -197,7 +194,7 @@ request_starting_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitWebRes
create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data);
gboolean
-download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data);
+download_cb (WebKitWebView *web_view, WebKitDownload *download, gpointer user_data);
void
populate_popup_cb(WebKitWebView *v, GtkMenu *m, void *c);
diff --git a/src/cookie-jar.c b/src/cookie-jar.c
index f2e340b..626e454 100644
--- a/src/cookie-jar.c
+++ b/src/cookie-jar.c
@@ -12,6 +12,7 @@
#include "cookie-jar.h"
#include "uzbl-core.h"
+#include "events.h"
static void
uzbl_cookie_jar_session_feature_init(SoupSessionFeatureInterface *iface, gpointer user_data);
@@ -38,6 +39,8 @@ soup_cookie_jar_socket_init(UzblCookieJar *jar) {
jar->handler = NULL;
jar->socket_path = NULL;
jar->connection_fd = -1;
+ jar->in_get_callback = 0;
+ jar->in_manual_add = 0;
}
static void
@@ -141,7 +144,7 @@ request_started(SoupSessionFeature *feature, SoupSession *session,
static void
changed(SoupCookieJar *jar, SoupCookie *old_cookie, SoupCookie *new_cookie) {
- (void) old_cookie;
+ SoupCookie * cookie = new_cookie ? new_cookie : old_cookie;
UzblCookieJar *uzbl_jar = UZBL_COOKIE_JAR(jar);
@@ -155,6 +158,25 @@ changed(SoupCookieJar *jar, SoupCookie *old_cookie, SoupCookie *new_cookie) {
if(uzbl_jar->in_get_callback)
return;
+ gchar *scheme = cookie->secure ? "https" : "http";
+
+ /* send a ADD or DELETE -_COOKIE event depending on what have changed */
+ if(!uzbl_jar->in_manual_add) {
+ gchar *expires = NULL;
+ if(cookie->expires)
+ expires = g_strdup_printf ("%d", soup_date_to_time_t (cookie->expires));
+
+ gchar * eventstr = g_strdup_printf ("'%s' '%s' '%s' '%s' '%s' '%s'",
+ cookie->domain, cookie->path, cookie->name, cookie->value, scheme, expires?expires:"");
+ if(new_cookie)
+ send_event(ADD_COOKIE, eventstr, NULL);
+ else
+ send_event(DELETE_COOKIE, eventstr, NULL);
+ g_free(eventstr);
+ if(expires)
+ g_free(expires);
+ }
+
/* the cookie daemon is only interested in new cookies and changed
ones, it can take care of deleting expired cookies on its own. */
if(!new_cookie)
@@ -162,8 +184,6 @@ changed(SoupCookieJar *jar, SoupCookie *old_cookie, SoupCookie *new_cookie) {
GString *s = g_string_new ("PUT");
- gchar *scheme = new_cookie->secure ? "https" : "http";
-
if(has_socket_handler(uzbl_jar)) {
g_string_append_c(s, 0); /* null-terminate the PUT */
g_string_append_len(s, scheme, strlen(scheme)+1);
diff --git a/src/cookie-jar.h b/src/cookie-jar.h
index 80af00e..f3e3733 100644
--- a/src/cookie-jar.h
+++ b/src/cookie-jar.h
@@ -16,6 +16,7 @@ typedef struct {
int connection_fd;
gboolean in_get_callback;
+ gboolean in_manual_add;
} UzblCookieJar;
typedef struct {
diff --git a/src/events.c b/src/events.c
index 20e3675..baaf8f3 100644
--- a/src/events.c
+++ b/src/events.c
@@ -22,7 +22,6 @@ const char *event_table[LAST_EVENT] = {
"REQUEST_STARTING" ,
"KEY_PRESS" ,
"KEY_RELEASE" ,
- "DOWNLOAD_REQUEST" ,
"COMMAND_EXECUTED" ,
"LINK_HOVER" ,
"TITLE_CHANGED" ,
@@ -45,10 +44,14 @@ const char *event_table[LAST_EVENT] = {
"PLUG_CREATED" ,
"COMMAND_ERROR" ,
"BUILTINS" ,
- "PTR_MOVE"
"PTR_MOVE" ,
"SCROLL_VERT" ,
- "SCROLL_HORIZ"
+ "SCROLL_HORIZ" ,
+ "DOWNLOAD_STARTED" ,
+ "DOWNLOAD_PROGRESS",
+ "DOWNLOAD_COMPLETE",
+ "ADD_COOKIE" ,
+ "DELETE_COOKIE"
};
void
@@ -136,24 +139,16 @@ send_event_stdout(GString *msg) {
void
send_event(int type, const gchar *details, const gchar *custom_event) {
GString *event_message = g_string_new("");
- gchar *buf, *p_val = NULL;
-
- /* expand shell vars */
- if(details) {
- buf = g_strdup(details);
- p_val = parseenv(buf ? g_strchug(buf) : " ");
- g_free(buf);
- }
/* check for custom events */
if(custom_event) {
g_string_printf(event_message, "EVENT [%s] %s %s\n",
- uzbl.state.instance_name, custom_event, p_val);
+ uzbl.state.instance_name, custom_event, details);
}
/* check wether we support the internal event */
else if(type < LAST_EVENT) {
g_string_printf(event_message, "EVENT [%s] %s %s\n",
- uzbl.state.instance_name, event_table[type], p_val);
+ uzbl.state.instance_name, event_table[type], details);
}
if(event_message->str) {
@@ -163,7 +158,6 @@ send_event(int type, const gchar *details, const gchar *custom_event) {
g_string_free(event_message, TRUE);
}
- g_free(p_val);
}
/* Transform gdk key events to our own events */
diff --git a/src/events.h b/src/events.h
index bc7960d..3c7b933 100644
--- a/src/events.h
+++ b/src/events.h
@@ -7,7 +7,7 @@
enum event_type {
LOAD_START, LOAD_COMMIT, LOAD_FINISH, LOAD_ERROR,
REQUEST_STARTING,
- KEY_PRESS, KEY_RELEASE, DOWNLOAD_REQ, COMMAND_EXECUTED,
+ KEY_PRESS, KEY_RELEASE, COMMAND_EXECUTED,
LINK_HOVER, TITLE_CHANGED, GEOMETRY_CHANGED,
WEBINSPECTOR, NEW_WINDOW, SELECTION_CHANGED,
VARIABLE_SET, FIFO_SET, SOCKET_SET,
@@ -16,6 +16,8 @@ enum event_type {
FOCUS_LOST, FOCUS_GAINED, FILE_INCLUDED,
PLUG_CREATED, COMMAND_ERROR, BUILTINS,
PTR_MOVE, SCROLL_VERT, SCROLL_HORIZ,
+ DOWNLOAD_STARTED, DOWNLOAD_PROGRESS, DOWNLOAD_COMPLETE,
+ ADD_COOKIE, DELETE_COOKIE,
/* must be last entry */
LAST_EVENT
diff --git a/src/uzbl-browser b/src/uzbl-browser
index 88d3742..faa2829 100755
--- a/src/uzbl-browser
+++ b/src/uzbl-browser
@@ -8,6 +8,8 @@
# to your $XDG_DATA_HOME/uzbl/scripts/ and edit them
PREFIX=/usr/local
+export PREFIX
+
EXAMPLES=$PREFIX/share/uzbl/examples
XDG_DATA_HOME=${XDG_DATA_HOME:-$HOME/.local/share}
@@ -66,7 +68,7 @@ fi
# we could also check if its pid file exists to avoid having to spawn it.
#if [ ! -f "$XDG_CACHE_HOME"/uzbl/cookie_daemon_socket.pid ]
#then
- ${UZBL_COOKIE_DAEMON:-uzbl-cookie-manager}
+# ${UZBL_COOKIE_DAEMON:-uzbl-cookie-manager}
#fi
# uzbl-event-manager will exit if one is already running.
diff --git a/src/uzbl-core.c b/src/uzbl-core.c
index dcdd4c7..592a8dd 100644
--- a/src/uzbl-core.c
+++ b/src/uzbl-core.c
@@ -93,6 +93,7 @@ const struct var_name_to_ptr_t {
{ "cookie_handler", PTR_V_STR(uzbl.behave.cookie_handler, 1, cmd_set_cookie_handler)},
{ "authentication_handler", PTR_V_STR(uzbl.behave.authentication_handler, 1, set_authentication_handler)},
{ "scheme_handler", PTR_V_STR(uzbl.behave.scheme_handler, 1, NULL)},
+ { "download_handler", PTR_V_STR(uzbl.behave.download_handler, 1, NULL)},
{ "fifo_dir", PTR_V_STR(uzbl.behave.fifo_dir, 1, cmd_fifo_dir)},
{ "socket_dir", PTR_V_STR(uzbl.behave.socket_dir, 1, cmd_socket_dir)},
{ "http_debug", PTR_V_INT(uzbl.behave.http_debug, 1, cmd_http_debug)},
@@ -414,36 +415,6 @@ find_existing_file(gchar* path_list) {
return NULL;
}
-
-/* Returns a new string with environment $variables expanded */
-gchar*
-parseenv (gchar* string) {
- extern char** environ;
- gchar* tmpstr = NULL, * out;
- int i = 0;
-
- if(!string)
- return NULL;
-
- out = g_strdup(string);
- while (environ[i] != NULL) {
- gchar** env = g_strsplit (environ[i], "=", 2);
- gchar* envname = g_strconcat ("$", env[0], NULL);
-
- if (g_strrstr (string, envname) != NULL) {
- tmpstr = out;
- out = str_replace(envname, env[1], out);
- g_free (tmpstr);
- }
-
- g_free (envname);
- g_strfreev (env); // somebody said this breaks uzbl
- i++;
- }
-
- return out;
-}
-
void
clean_up(void) {
if(uzbl.info.pid_str) {
@@ -489,15 +460,14 @@ get_click_context() {
if(!uzbl.state.last_button)
return -1;
- ht = webkit_web_view_get_hit_test_result(g->web_view, uzbl.state.last_button);
- g_object_get(ht, "context", &context, NULL);
+ ht = webkit_web_view_get_hit_test_result (g->web_view, uzbl.state.last_button);
+ g_object_get (ht, "context", &context, NULL);
+ g_object_unref (ht);
return (gint)context;
}
/* --- SIGNALS --- */
-int sigs[] = {SIGTERM, SIGINT, SIGSEGV, SIGILL, SIGFPE, SIGQUIT, SIGALRM, 0};
-
sigfunc*
setup_signal(int signr, sigfunc *shandler) {
struct sigaction nh, oh;
@@ -513,21 +483,9 @@ setup_signal(int signr, sigfunc *shandler) {
}
void
-catch_signal(int s) {
- if(s == SIGTERM ||
- s == SIGINT ||
- s == SIGILL ||
- s == SIGFPE ||
- s == SIGQUIT) {
- clean_up();
- exit(EXIT_SUCCESS);
- }
- else if(s == SIGSEGV) {
- clean_up();
- fprintf(stderr, "Program aborted, segmentation fault!\nAttempting to clean up...\n");
- exit(EXIT_FAILURE);
- }
- else if(s == SIGALRM && uzbl.state.event_buffer) {
+empty_event_buffer(int s) {
+ (void) s;
+ if(uzbl.state.event_buffer) {
g_ptr_array_free(uzbl.state.event_buffer, TRUE);
uzbl.state.event_buffer = NULL;
}
@@ -626,9 +584,10 @@ struct {const char *key; CommandInfo value;} cmdlist[] =
{ "js", {run_js, TRUE} },
{ "script", {run_external_js, 0} },
{ "toggle_status", {toggle_status_cb, 0} },
- { "spawn", {spawn, 0} },
+ { "spawn", {spawn_async, 0} },
{ "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
- { "sh", {spawn_sh, 0} },
+ { "sync_spawn_exec", {spawn_sync_exec, 0} }, // needed for load_cookies.sh :(
+ { "sh", {spawn_sh_async, 0} },
{ "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
{ "exit", {close_uzbl, 0} },
{ "search", {search_forward_text, TRUE} },
@@ -656,7 +615,9 @@ struct {const char *key; CommandInfo value;} cmdlist[] =
{ "menu_editable_remove", {menu_remove_edit, TRUE} },
{ "hardcopy", {hardcopy, TRUE} },
{ "include", {include, TRUE} },
- { "show_inspector", {show_inspector, 0} }
+ { "show_inspector", {show_inspector, 0} },
+ { "add_cookie", {add_cookie, 0} },
+ { "delete_cookie", {delete_cookie, 0} }
};
void
@@ -695,9 +656,8 @@ set_var(WebKitWebView *page, GArray *argv, GString *result) {
gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
if (split[0] != NULL) {
- gchar *value = parseenv(split[1] ? g_strchug(split[1]) : " ");
+ gchar *value = split[1] ? g_strchug(split[1]) : " ";
set_var_value(g_strstrip(split[0]), value);
- g_free(value);
}
g_strfreev(split);
}
@@ -716,7 +676,7 @@ add_to_menu(GArray *argv, guint context) {
g->menu_items = g_ptr_array_new();
if(split[1])
- item_cmd = g_strdup(split[1]);
+ item_cmd = split[1];
if(split[0]) {
m = malloc(sizeof(MenuItem));
@@ -726,8 +686,6 @@ add_to_menu(GArray *argv, guint context) {
m->issep = FALSE;
g_ptr_array_add(g->menu_items, m);
}
- else
- g_free(item_cmd);
g_strfreev(split);
}
@@ -927,14 +885,12 @@ void
include(WebKitWebView *page, GArray *argv, GString *result) {
(void) page;
(void) result;
- gchar *pe = NULL,
- *path = NULL;
+ gchar *path = argv_idx(argv, 0);
- if(!argv_idx(argv, 0))
+ if(!path)
return;
- pe = parseenv(argv_idx(argv, 0));
- if((path = find_existing_file(pe))) {
+ if((path = find_existing_file(path))) {
if(!for_each_line_in_file(path, parse_cmd_line_cb, NULL)) {
gchar *tmp = g_strdup_printf("File %s can not be read.", path);
send_event(COMMAND_ERROR, tmp, NULL);
@@ -944,7 +900,6 @@ include(WebKitWebView *page, GArray *argv, GString *result) {
send_event(FILE_INCLUDED, path, NULL);
g_free(path);
}
- g_free(pe);
}
void
@@ -955,6 +910,57 @@ show_inspector(WebKitWebView *page, GArray *argv, GString *result) {
}
void
+add_cookie(WebKitWebView *page, GArray *argv, GString *result) {
+ (void) page; (void) result;
+ gchar *host, *path, *name, *value;
+ gboolean secure = 0;
+ SoupDate *expires = NULL;
+
+ if(argv->len != 6)
+ return;
+
+ // Parse with same syntax as ADD_COOKIE event
+ host = argv_idx (argv, 0);
+ path = argv_idx (argv, 1);
+ name = argv_idx (argv, 2);
+ value = argv_idx (argv, 3);
+ secure = strcmp (argv_idx (argv, 4), "https") == 0;
+ if (strlen (argv_idx (argv, 5)) != 0)
+ expires = soup_date_new_from_time_t (
+ strtoul (argv_idx (argv, 5), NULL, 10));
+
+ // Create new cookie
+ SoupCookie * cookie = soup_cookie_new (name, value, host, path, -1);
+ soup_cookie_set_secure (cookie, secure);
+ if (expires)
+ soup_cookie_set_expires (cookie, expires);
+
+ // Add cookie to jar
+ uzbl.net.soup_cookie_jar->in_manual_add = 1;
+ soup_cookie_jar_add_cookie (SOUP_COOKIE_JAR (uzbl.net.soup_cookie_jar), cookie);
+ uzbl.net.soup_cookie_jar->in_manual_add = 0;
+}
+
+void
+delete_cookie(WebKitWebView *page, GArray *argv, GString *result) {
+ (void) page; (void) result;
+
+ if(argv->len < 4)
+ return;
+
+ SoupCookie * cookie = soup_cookie_new (
+ argv_idx (argv, 2),
+ argv_idx (argv, 3),
+ argv_idx (argv, 0),
+ argv_idx (argv, 1),
+ 0);
+
+ uzbl.net.soup_cookie_jar->in_manual_add = 1;
+ soup_cookie_jar_delete_cookie (SOUP_COOKIE_JAR (uzbl.net.soup_cookie_jar), cookie);
+ uzbl.net.soup_cookie_jar->in_manual_add = 0;
+}
+
+void
act_dump_config() {
dump_config();
}
@@ -1258,7 +1264,7 @@ run_command (const gchar *command, const gchar **args, const gboolean sync,
/*@null@*/ gchar**
split_quoted(const gchar* src, const gboolean unquote) {
- /* split on unquoted space, return array of strings;
+ /* split on unquoted space or tab, return array of strings;
remove a layer of quotes and backslashes if unquote */
if (!src) return NULL;
@@ -1279,7 +1285,7 @@ split_quoted(const gchar* src, const gboolean unquote) {
else if ((*p == '\'') && unquote && !dq) sq = !sq;
else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
sq = ! sq; }
- else if ((*p == ' ') && !dq && !sq) {
+ else if ((*p == ' ' || *p == '\t') && !dq && !sq) {
dup = g_strdup(s->str);
g_array_append_val(a, dup);
g_string_truncate(s, 0);
@@ -1294,33 +1300,49 @@ split_quoted(const gchar* src, const gboolean unquote) {
}
void
-_spawn(GArray *argv, char **output_stdout) {
+spawn(GArray *argv, gboolean sync, gboolean exec) {
gchar *path = NULL;
gchar *arg_car = argv_idx(argv, 0);
const gchar **arg_cdr = &g_array_index(argv, const gchar *, 1);
if (arg_car && (path = find_existing_file(arg_car))) {
- run_command(path, arg_cdr, (output_stdout != NULL), output_stdout);
+ if (uzbl.comm.sync_stdout)
+ uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
+ run_command(path, arg_cdr, sync, sync?&uzbl.comm.sync_stdout:NULL);
+ // run each line of output from the program as a command
+ if (sync && exec && uzbl.comm.sync_stdout) {
+ gchar *head = uzbl.comm.sync_stdout;
+ gchar *tail;
+ while ((tail = strchr (head, '\n'))) {
+ *tail = '\0';
+ parse_cmd_line(head, NULL);
+ head = tail + 1;
+ }
+ }
g_free(path);
}
}
void
-spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
+spawn_async(WebKitWebView *web_view, GArray *argv, GString *result) {
(void)web_view; (void)result;
-
- _spawn(argv, NULL);
+ spawn(argv, FALSE, FALSE);
}
void
spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
(void)web_view; (void)result;
+ spawn(argv, TRUE, FALSE);
+}
- _spawn(argv, &uzbl.comm.sync_stdout);
+void
+spawn_sync_exec(WebKitWebView *web_view, GArray *argv, GString *result) {
+ (void)web_view; (void)result;
+ spawn(argv, TRUE, TRUE);
}
void
-_spawn_sh(GArray *argv, char **output_stdout) {
+spawn_sh(GArray *argv, gboolean sync) {
if (!uzbl.behave.shell_cmd) {
g_printerr ("spawn_sh: shell_cmd is not set!\n");
return;
@@ -1334,24 +1356,27 @@ _spawn_sh(GArray *argv, char **output_stdout) {
for (i = 1; i < g_strv_length(cmd); i++)
g_array_prepend_val(argv, cmd[i]);
- if (cmd) run_command(cmd[0], (const gchar **) argv->data,
- (output_stdout != NULL), output_stdout);
+ if (cmd) {
+ if (uzbl.comm.sync_stdout)
+ uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
+
+ run_command(cmd[0], (const gchar **) argv->data,
+ sync, sync?&uzbl.comm.sync_stdout:NULL);
+ }
g_free (cmdname);
g_strfreev (cmd);
}
void
-spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
+spawn_sh_async(WebKitWebView *web_view, GArray *argv, GString *result) {
(void)web_view; (void)result;
-
- _spawn_sh(argv, NULL);
+ spawn_sh(argv, FALSE);
}
void
spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
(void)web_view; (void)result;
-
- _spawn_sh(argv, &uzbl.comm.sync_stdout);
+ spawn_sh(argv, TRUE);
}
void
@@ -1390,14 +1415,13 @@ parse_command(const char *cmd, const char *param, GString *result) {
strcmp("request", cmd)) {
g_string_printf(tmp, "%s %s", cmd, param?param:"");
send_event(COMMAND_EXECUTED, tmp->str, NULL);
- g_string_free(tmp, TRUE);
}
}
else {
- gchar *tmp = g_strdup_printf("%s %s", cmd, param?param:"");
- send_event(COMMAND_ERROR, tmp, NULL);
- g_free(tmp);
+ g_string_printf (tmp, "%s %s", cmd, param?param:"");
+ send_event(COMMAND_ERROR, tmp->str, NULL);
}
+ g_string_free(tmp, TRUE);
}
@@ -1568,6 +1592,28 @@ control_fifo(GIOChannel *gio, GIOCondition condition) {
return TRUE;
}
+gboolean
+attach_fifo(gchar *path) {
+ GError *error = NULL;
+ /* we don't really need to write to the file, but if we open the
+ * file as 'r' we will block here, waiting for a writer to open
+ * the file. */
+ GIOChannel *chan = g_io_channel_new_file(path, "r+", &error);
+ if (chan) {
+ if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
+ if (uzbl.state.verbose)
+ printf ("attach_fifo: created successfully as %s\n", path);
+ send_event(FIFO_SET, path, NULL);
+ uzbl.comm.fifo_path = path;
+ g_setenv("UZBL_FIFO", uzbl.comm.fifo_path, TRUE);
+ return TRUE;
+ } else g_warning ("attach_fifo: could not add watch on %s\n", path);
+ } else g_warning ("attach_fifo: can't open: %s\n", error->message);
+
+ if (error) g_error_free (error);
+ return FALSE;
+}
+
/*@null@*/ gchar*
init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
@@ -1577,29 +1623,31 @@ init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
uzbl.comm.fifo_path = NULL;
}
- GIOChannel *chan = NULL;
- GError *error = NULL;
gchar *path = build_stream_name(FIFO, dir);
if (!file_exists(path)) {
- if (mkfifo (path, 0666) == 0) {
- // we don't really need to write to the file, but if we open the file as 'r' we will block here, waiting for a writer to open the file.
- chan = g_io_channel_new_file(path, "r+", &error);
- if (chan) {
- if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
- if (uzbl.state.verbose)
- printf ("init_fifo: created successfully as %s\n", path);
- send_event(FIFO_SET, path, NULL);
- uzbl.comm.fifo_path = path;
- g_setenv("UZBL_FIFO", uzbl.comm.fifo_path, TRUE);
- return dir;
- } else g_warning ("init_fifo: could not add watch on %s\n", path);
- } else g_warning ("init_fifo: can't open: %s\n", error->message);
+ if (mkfifo (path, 0666) == 0 && attach_fifo(path)) {
+ return dir;
} else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
- } else g_warning ("init_fifo: can't create %s: file exists\n", path);
+ } else {
+ /* the fifo exists. but is anybody home? */
+ int fd = open(path, O_WRONLY|O_NONBLOCK);
+ if(fd < 0) {
+ /* some error occurred, presumably nobody's on the read end.
+ * we can attach ourselves to it. */
+ if(attach_fifo(path))
+ return dir;
+ else
+ g_warning("init_fifo: can't attach to %s: %s\n", path, strerror(errno));
+ } else {
+ /* somebody's there, we can't use that fifo. */
+ close(fd);
+ /* whatever, this instance can live without a fifo. */
+ g_warning ("init_fifo: can't create %s: file exists and is occupied\n", path);
+ }
+ }
/* if we got this far, there was an error; cleanup */
- if (error) g_error_free (error);
g_free(dir);
g_free(path);
return NULL;
@@ -1750,6 +1798,32 @@ control_client_socket(GIOChannel *clientchan) {
return TRUE;
}
+
+gboolean
+attach_socket(gchar *path, struct sockaddr_un *local) {
+ GIOChannel *chan = NULL;
+ int sock = socket (AF_UNIX, SOCK_STREAM, 0);
+
+ if (bind (sock, (struct sockaddr *) local, sizeof(*local)) != -1) {
+ if (uzbl.state.verbose)
+ printf ("init_socket: opened in %s\n", path);
+
+ if(listen (sock, 5) < 0)
+ g_warning ("attach_socket: could not listen on %s: %s\n", path, strerror(errno));
+
+ if( (chan = g_io_channel_unix_new(sock)) ) {
+ g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
+ uzbl.comm.socket_path = path;
+ send_event(SOCKET_SET, path, NULL);
+ g_setenv("UZBL_SOCKET", uzbl.comm.socket_path, TRUE);
+ return TRUE;
+ }
+ } else g_warning ("attach_socket: could not bind to %s: %s\n", path, strerror(errno));
+
+ return FALSE;
+}
+
+
/*@null@*/ gchar*
init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
@@ -1764,31 +1838,33 @@ init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL *
return NULL;
}
- GIOChannel *chan = NULL;
- int sock, len;
struct sockaddr_un local;
gchar *path = build_stream_name(SOCKET, dir);
- sock = socket (AF_UNIX, SOCK_STREAM, 0);
-
local.sun_family = AF_UNIX;
strcpy (local.sun_path, path);
- unlink (local.sun_path);
- len = strlen (local.sun_path) + sizeof (local.sun_family);
- if (bind (sock, (struct sockaddr *) &local, len) != -1) {
- if (uzbl.state.verbose)
- printf ("init_socket: opened in %s\n", path);
- listen (sock, 5);
-
- if( (chan = g_io_channel_unix_new(sock)) ) {
- g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
- uzbl.comm.socket_path = path;
- send_event(SOCKET_SET, path, NULL);
- g_setenv("UZBL_SOCKET", uzbl.comm.socket_path, TRUE);
- return dir;
+ if(!file_exists(path) && attach_socket(path, &local)) {
+ /* it's free for the taking. */
+ return dir;
+ } else {
+ /* see if anybody's listening on the socket path we want. */
+ int sock = socket (AF_UNIX, SOCK_STREAM, 0);
+ if(connect(sock, (struct sockaddr *) &local, sizeof(local)) < 0) {
+ /* some error occurred, presumably nobody's listening.
+ * we can attach ourselves to it. */
+ unlink(path);
+ if(attach_socket(path, &local))
+ return dir;
+ else
+ g_warning("init_socket: can't attach to existing socket %s: %s\n", path, strerror(errno));
+ } else {
+ /* somebody's there, we can't use that socket path. */
+ close(sock);
+ /* whatever, this instance can live without a socket. */
+ g_warning ("init_socket: can't create %s: socket exists and is occupied\n", path);
}
- } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
+ }
/* if we got this far, there was an error; cleanup */
g_free(path);
@@ -2183,6 +2259,27 @@ retrieve_geometry() {
uzbl.gui.geometry = g_string_free(buf, FALSE);
}
+void
+set_webview_scroll_adjustments() {
+#ifdef GTK3
+ gtk_scrollable_set_hadjustment (GTK_SCROLLABLE(uzbl.gui.web_view), uzbl.gui.bar_h);
+ gtk_scrollable_set_vadjustment (GTK_SCROLLABLE(uzbl.gui.web_view), uzbl.gui.bar_v);
+#else
+ gtk_widget_set_scroll_adjustments (GTK_WIDGET (uzbl.gui.web_view),
+ uzbl.gui.bar_h, uzbl.gui.bar_v);
+#endif
+
+ g_object_connect((GObject*)uzbl.gui.bar_v,
+ "signal::value-changed", (GCallback)scroll_vert_cb, NULL,
+ "signal::changed", (GCallback)scroll_vert_cb, NULL,
+ NULL);
+
+ g_object_connect((GObject*)uzbl.gui.bar_h,
+ "signal::value-changed", (GCallback)scroll_horiz_cb, NULL,
+ "signal::changed", (GCallback)scroll_horiz_cb, NULL,
+ NULL);
+}
+
/* set up gtk, gobject, variable defaults and other things that tests and other
* external applications need to do anyhow */
void
@@ -2220,10 +2317,10 @@ initialize(int argc, char *argv[]) {
uzbl.net.soup_cookie_jar = uzbl_cookie_jar_new();
soup_session_add_feature(uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_cookie_jar));
- for(i=0; sigs[i]; i++) {
- if(setup_signal(sigs[i], catch_signal) == SIG_ERR)
- fprintf(stderr, "uzbl: error hooking %d: %s\n", sigs[i], strerror(errno));
- }
+ /* TODO: move the handler setup to event_buffer_timeout and disarm the
+ * handler in empty_event_buffer? */
+ if(setup_signal(SIGALRM, empty_event_buffer) == SIG_ERR)
+ fprintf(stderr, "uzbl: error hooking %d: %s\n", SIGALRM, strerror(errno));
event_buffer_timeout(10);
uzbl.info.webkit_major = webkit_major_version();
@@ -2317,24 +2414,15 @@ main (int argc, char* argv[]) {
uzbl.gui.main_window = create_window ();
gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
gtk_widget_show_all (uzbl.gui.main_window);
- uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
+ uzbl.xwin = GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (uzbl.gui.main_window)));
}
uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
- gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
- g_object_connect((GObject*)uzbl.gui.bar_v,
- "signal::value-changed", (GCallback)scroll_vert_cb, NULL,
- "signal::changed", (GCallback)scroll_vert_cb, NULL,
- NULL);
-
- g_object_connect((GObject*)uzbl.gui.bar_h,
- "signal::value-changed", (GCallback)scroll_horiz_cb, NULL,
- "signal::changed", (GCallback)scroll_horiz_cb, NULL,
- NULL);
+ set_webview_scroll_adjustments();
gchar *xwin = g_strdup_printf("%d", (int)uzbl.xwin);
g_setenv("UZBL_XID", xwin, TRUE);
diff --git a/src/uzbl-core.h b/src/uzbl-core.h
index 412d9fc..129c6a5 100644
--- a/src/uzbl-core.h
+++ b/src/uzbl-core.h
@@ -133,6 +133,7 @@ typedef struct {
gchar* fantasy_font_family;
gchar* cursive_font_family;
gchar* scheme_handler;
+ gchar* download_handler;
gboolean show_status;
gboolean forward_keys;
gboolean status_top;
@@ -228,9 +229,6 @@ itos(int val);
gchar*
strfree(gchar *str);
-gchar*
-parseenv (gchar* string);
-
void
clean_up(void);
@@ -266,10 +264,10 @@ run_command(const gchar *command, const gchar **args, const gboolean sync,
char **output_stdout);
void
-spawn(WebKitWebView *web_view, GArray *argv, GString *result);
+spawn_async(WebKitWebView *web_view, GArray *argv, GString *result);
void
-spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result);
+spawn_sh_async(WebKitWebView *web_view, GArray *argv, GString *result);
void
spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result);
@@ -278,6 +276,9 @@ void
spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result);
void
+spawn_sync_exec(WebKitWebView *web_view, GArray *argv, GString *result);
+
+void
parse_command(const char *cmd, const char *param, GString *result);
void
@@ -397,6 +398,9 @@ void
retrieve_geometry();
void
+set_webview_scroll_adjustments();
+
+void
event(WebKitWebView *page, GArray *argv, GString *result);
void
@@ -454,6 +458,12 @@ void
show_inspector(WebKitWebView *page, GArray *argv, GString *result);
void
+add_cookie(WebKitWebView *page, GArray *argv, GString *result);
+
+void
+delete_cookie(WebKitWebView *page, GArray *argv, GString *result);
+
+void
builtins();
typedef void (*Command)(WebKitWebView*, GArray *argv, GString *result);
diff --git a/tests/Makefile b/tests/Makefile
index 3f63adf..bfe74c5 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -1,17 +1,41 @@
-CFLAGS:=-std=c99 -I$(shell pwd)/../ -L$(shell pwd) -luzbl-core $(shell pkg-config --cflags gtk+-2.0 webkit-1.0 libsoup-2.4 gthread-2.0) -ggdb -Wall -W -DARCH="\"$(shell uname -m)\"" -lgthread-2.0 -DG_ERRORCHECK_MUTEXES -DCOMMIT="\"$(shell git log | head -n1 | sed "s/.* //")\"" $(CPPFLAGS)
-CFLAGS!=echo -std=c99 `pkg-config --cflags gtk+-2.0 webkit-1.0 libsoup-2.4 gthread-2.0` -ggdb -Wall -W -DARCH='"\""'`uname -m`'"\""' -lgthread-2.0 -DG_ERRORCHECK_MUTEXES -DCOMMIT='"\""'`git log | head -n1 | sed "s/.* //"`'"\""' $(CPPFLAGS)
+# gtk2
+REQ_PKGS = gtk+-2.0 webkit-1.0
+CPPFLAGS = -DERRORCHECK_MUTEXES -I ../
-LDFLAGS:=$(shell pkg-config --libs gtk+-2.0 webkit-1.0 libsoup-2.4 gthread-2.0) -pthread $(LDFLAGS)
-LDFLAGS!=echo `pkg-config --libs gtk+-2.0 webkit-1.0 libsoup-2.4 gthread-2.0` -pthread $(LDFLAGS)
+# gtk3
+#REQ_PKGS = gtk+-3.0 webkitgtk-3.0
+#CPPFLAGS = -DERRORCHECK_MUTEXES -DGTK3 -I ../
-GTESTER:=gtester
-GTESTER_REPORT:=gtester-report
+# --- configuration ends here ---
-TEST_PROGS:=test-expand test-command
+REQ_PKGS += libsoup-2.4 gthread-2.0 glib-2.0
+
+ARCH:=$(shell uname -m)
+ARCH!=echo `uname -m`
+
+COMMIT_HASH:=$(shell cd .. && ./misc/hash.sh)
+COMMIT_HASH!=echo `cd .. && ./misc/hash.sh`
+
+CPPFLAGS += -DARCH=\"$(ARCH)\" -DCOMMIT=\"$(COMMIT_HASH)\"
+
+PKG_CFLAGS:=$(shell pkg-config --cflags $(REQ_PKGS))
+PKG_CFLAGS!=echo pkg-config --cflags $(REQ_PKGS)
+
+LDLIBS:=$(shell pkg-config --libs $(REQ_PKGS) x11)
+LDLIBS!=echo pkg-config --libs $(REQ_PKGS) x11
+
+CFLAGS += -std=c99 $(PKG_CFLAGS) -ggdb -fPIC -W -Wall -Wextra -pedantic -pthread
+
+GTESTER = gtester
+GTESTER_REPORT = gtester-report
+
+TEST_PROGS = test-expand test-command
all: $(TEST_PROGS)
LD_LIBRARY_PATH="$(LD_LIBRARY_PATH):." $(GTESTER) --verbose $(TEST_PROGS)
+${TEST_PROGS}: libuzbl-core.so
+
clean:
rm -f $(TEST_PROGS)
rm -f libuzbl-core.so