summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS2
-rw-r--r--Makefile12
-rw-r--r--config.mk27
-rw-r--r--contrib/bash-completion27
-rw-r--r--doc/dwb.119
-rw-r--r--doc/dwb.1.txt4
-rw-r--r--doc/dwbem.113
-rw-r--r--doc/dwbem.1.txt4
-rw-r--r--dwbem/Makefile (renamed from tools/Makefile)14
-rw-r--r--dwbem/dwbem.c (renamed from tools/dwbem.c)237
-rw-r--r--exar/Makefile34
-rw-r--r--exar/exar.c677
-rw-r--r--exar/exar.h165
-rw-r--r--exar/main.c155
-rw-r--r--m4/keys.m41
-rw-r--r--scripts/base.js37
-rw-r--r--scripts/lib/dwb.js27
-rw-r--r--scripts/lib/extensions.js23
-rw-r--r--scripts/lib/net.js79
-rw-r--r--scripts/lib/system.js27
-rw-r--r--src/Makefile12
-rw-r--r--src/adblock.c34
-rw-r--r--src/adblock.h13
-rw-r--r--src/application.c20
-rw-r--r--src/application.h4
-rw-r--r--src/callback.c5
-rw-r--r--src/callback.h4
-rw-r--r--src/commands.c22
-rw-r--r--src/commands.h5
-rw-r--r--src/completion.h4
-rw-r--r--src/config.h6
-rw-r--r--src/config.mk3
-rw-r--r--src/dom.h4
-rw-r--r--src/domain.h4
-rw-r--r--src/download.h4
-rw-r--r--src/dwb.c87
-rw-r--r--src/dwb.h5
-rw-r--r--src/editor.h4
-rw-r--r--src/entry.h4
-rw-r--r--src/hsts.h6
-rw-r--r--src/html.h4
-rw-r--r--src/js.c8
-rw-r--r--src/js.h5
-rw-r--r--src/local.h4
-rw-r--r--src/plugins.h4
-rw-r--r--src/scripts.c568
-rw-r--r--src/scripts.h8
-rw-r--r--src/session.h4
-rw-r--r--src/soup.h4
-rw-r--r--src/util.c45
-rw-r--r--src/util.h8
-rw-r--r--src/util/Makefile4
-rw-r--r--src/view.c32
-rw-r--r--src/view.h4
-rwxr-xr-xtools/backtrace.sh2
-rwxr-xr-xtools/check_header.sh12
56 files changed, 2197 insertions, 353 deletions
diff --git a/AUTHORS b/AUTHORS
index 8c5e6349..58ca2a53 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -6,8 +6,10 @@ Uli Armbruster 2012
Peter Bex 2011
Florian Bruhin 2012
Bastien Dejean 2012
+Ian Denhardt 2013
Sean DuBois 2012
Jonas Haag 2010
+Christopher Jeffrey 2013
Daniel Martí 2013
Robin Martinjak 2013
Elias Norberg <xyzzy@kudzu.se> 2013
diff --git a/Makefile b/Makefile
index b2837405..ee33b5f4 100644
--- a/Makefile
+++ b/Makefile
@@ -6,8 +6,8 @@ all: options $(TARGET)
options:
@echo Build options:
- @echo CC = $(CC)
- @echo CFLAGS = $(CFLAGS_OPTIONS)
+ @echo CC = $(CC)
+ @echo CFLAGS = $(CFLAGS_OPTIONS)
@echo LDFLAGS = $(LDFLAGS)
@echo CPPFLAGS = $(CPPFLAGS)
@@ -18,12 +18,12 @@ $(TARGET): $(SUBDIRS:%=%.subdir-make)
#$(SRCDIR)/%: $(SUBDIR_BUILD_FIRST:%=%.subdir-buildfirst)
-%.subdir-buildfirst:
+%.subdir-buildfirst:
@$(MAKE) $(MFLAGS) -C $*
-clean: $(SUBDIRS:%=%.subdir-clean) $(SUBDIR_BUILD_FIRST:%=%.subdir-cleanfirst)
+clean: $(SUBDIRS:%=%.subdir-clean) $(SUBDIR_BUILD_FIRST:%=%.subdir-cleanfirst) $(SUBDIR_BUILD_LIB:%=%.subdir-cleanlib)
-%.subdir-clean %.subdir-cleanfirst:
+%.subdir-clean %.subdir-cleanfirst %.subdir-cleanlib:
@$(MAKE) $(MFLAGS) clean -C $*
@@ -31,7 +31,7 @@ install: $(TARGET) install-man install-data
@# Install binaries
install -d $(DESTDIR)$(BINDIR)
install -m 755 $(SRCDIR)/$(TARGET) $(DESTDIR)$(BINDIR)/$(TARGET)
- install -m 755 $(TOOLDIR)/$(EXTENSION_MANAGER) $(DESTDIR)$(BINDIR)/$(EXTENSION_MANAGER)
+ install -m 755 $(DWBEMDIR)/$(EXTENSION_MANAGER) $(DESTDIR)$(BINDIR)/$(EXTENSION_MANAGER)
install-man: all
install -d $(DESTDIR)$(MAN1DIR)
diff --git a/config.mk b/config.mk
index 0dee4cc0..a0a35168 100644
--- a/config.mk
+++ b/config.mk
@@ -18,17 +18,21 @@ SHAREDIR=share
M4DIR=m4
UTILDIR=$(SRCDIR)/util
TOOLDIR=tools
-SUBDIRS=$(M4DIR) $(SRCDIR) $(TOOLDIR)
-SUBDIR_BUILD_FIRST=$(UTILDIR)
EXTENSIONDIR=extensions
CONTRIBDIR=contrib
+DWB_LIB_DIRS = exar
+
+SUBDIRS=$(M4DIR) $(SRCDIR) $(DWBEMDIR)
+SUBDIR_BUILD_FIRST=$(UTILDIR) $(DWB_LIB_DIRS)
+
+DWBEMDIR=dwbem
EXTENSION_MANAGER=dwbem
DTARGET=$(TARGET)_d
REAL_VERSION=$(BUILDDATE)
# Version info
-GIT_VERSION=$(shell git log -1 --format="%cd %h" --date=short)
+GIT_VERSION=$(shell git log -1 --format="%cd %h" --date=short 2>/dev/null)
VERSION=$(shell if [ "$(GIT_VERSION)" ]; then echo "commit\ \"$(GIT_VERSION)\""; else echo "$(REAL_VERSION)"; fi)
NAME=$(shell if [ "$(GIT_VERSION)" ]; then echo "$(REAL_NAME)-git"; else echo "$(REAL_NAME)"; fi)
BUILDDATE=`date +%Y.%m.%d`
@@ -150,19 +154,14 @@ CFLAGS += -D_NETBSD_SOURCE
CFLAGS += $(shell pkg-config --cflags $(LIBS))
ifeq ($(shell pkg-config --exists '$(LIBSOUP) >= 2.38' && echo 1), 1)
-M4FLAGS += --define=WITH_LIBSOUP_2_38=1 -G
+M4FLAGS += -DWITH_LIBSOUP_2_38=1 -G
CFLAGS += -DWITH_LIBSOUP_2_38=1
endif
ifeq (${DISABLE_HSTS}, 1)
CFLAGS += -DDISABLE_HSTS
else
-M4FLAGS += --define=WITH_HSTS=1
-endif
-
-# If execinfo.h is not available, e.g. freebsd
-ifneq (${WITHOUT_EXECINFO}, 1)
-CFLAGS += -DHAS_EXECINFO
+M4FLAGS += -DWITH_HSTS=1 -G
endif
CFLAGS_OPTIONS := $(CFLAGS)
@@ -172,7 +171,7 @@ CPPFLAGS+=-DGTK_DISABLE_SINGLE_INCLUDES
CPPFLAGS+=-DGTK_DISABLE_DEPRECATED
CPPFLAGS+=-DGDK_DISABLE_DEPRECATED
CPPFLAGS+=-DGSEAL_ENABLE
-M4FLAGS += --define=WITH_GTK3=1
+M4FLAGS += -DWITH_GTK3=1
endif
@@ -196,7 +195,7 @@ CFLAGS += -DLIBJS_DIR=\"$(LIBJSDIR)\"
# LDFLAGS
LDFLAGS += $(shell pkg-config --libs $(LIBS))
-LDFLAGS += -lpthread
+LDFLAGS += -lpthread -lm
# Debug flags
DCFLAGS = $(CFLAGS)
@@ -215,5 +214,5 @@ HDR = $(wildcard *.h)
# OUTPUT
# Objects
OBJ = $(patsubst %.c, %.o, $(wildcard *.c))
-DOBJ = $(patsubst %.c, %.do, $(wildcard *.c))
-
+DOBJ = $(patsubst %.c, %.do, $(wildcard *.c))
+OBJLIB = exar/exar.o
diff --git a/contrib/bash-completion b/contrib/bash-completion
index 4bde472b..818ff634 100644
--- a/contrib/bash-completion
+++ b/contrib/bash-completion
@@ -10,7 +10,7 @@ __profile() {
COMPREPLY=( $( compgen -W "$( while read line; do
[[ "$line" == \[* ]] && {
line="${line#\[}"
- printf "${line%\]}"
+ printf "${line%\]} "
} done < "${HOME}/.config/dwb/settings" )" -- $cur ) )
}
@@ -78,13 +78,20 @@ __enabled_extension() {
[[ " ${disabled} " != *" ${installed} "* ]] && printf "${installed} "
done)" -- $cur ) )
}
+__archive() {
+ COMPREPLY=( $( compgen -W "$( case $prev in
+ --archive) printf 'a h d e l p s u ';;
+ *) _filedir;;
+ esac)" -- $cur ) )
+}
+
_dwb() {
local cur prev opts lopts
_init_completion || return
- opts="-h -c -e -f -l -n -r -R -p -x -v -S"
- lopts="--help --check-script --embed --force --list-sessions --new-instance
+ opts="-h -c -d -e -f -l -n -r -R -p -x -v -S"
+ lopts="--help --check-script --delete-profile --embed --force --list-sessions --new-instance
--restore --override-restore --profile --execute --version --enable-scripts
--set-as-default"
@@ -98,6 +105,9 @@ _dwb() {
-p|--profile)
__profile
return 0;;
+ -d|--delete-profile)
+ __profile
+ return 0;;
-x|--execute)
__execute
return 0;;
@@ -124,7 +134,7 @@ _dwbem() {
opts="-h -a -b -B -c -d -e -E -i -I -l -L -n -N -r -p -u -U"
lopts="--help --list-all --bind --setbind --config --disable --enable --edit
--install --info --list-installed --setload --no-config --no-confirm
- --remove --proxy --upgrade --update"
+ --remove --proxy --upgrade --update --archive"
extinstops=" -B --setbind -c --config -d --disable -e --enable -E --edit
-L --setload -r --remove -U --update "
@@ -138,6 +148,10 @@ _dwbem() {
__disabled_extension
return 0
}
+ [[ " --archive " == *" ${prev} "* ]] && {
+ __archive
+ return 0
+ }
[[ "${extinstops}" == *" ${prev} "* ]] && {
__installed_extension
return 0
@@ -151,9 +165,12 @@ _dwbem() {
--*)
COMPREPLY=( $( compgen -W "${lopts}" -- $cur ) )
return 0;;
- *)
+ -*)
COMPREPLY=( $( compgen -W "${opts} ${lopts}" -- $cur ) )
return 0;;
+ *)
+ _filedir
+ return 0;;
esac
} &&
complete -F _dwbem dwbem
diff --git a/doc/dwb.1 b/doc/dwb.1
index 585d713c..f4c1f2ad 100644
--- a/doc/dwb.1
+++ b/doc/dwb.1
@@ -2,12 +2,12 @@
.\" Title: dwb
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
-.\" Date: 04/27/2013
+.\" Date: 05/14/2013
.\" Manual: \ \&
.\" Source: \ \&
.\" Language: English
.\"
-.TH "DWB" "1" "04/27/2013" "\ \&" "\ \&"
+.TH "DWB" "1" "05/14/2013" "\ \&" "\ \&"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
@@ -42,6 +42,11 @@ dwb(1) is a small webbrowser based on WebKit and GTK which aims to be mostly key
Checks if a the syntax of a script that uses the javascript api is correct\&.
.RE
.PP
+\fB\-d, \-\-delete\-profile\fR=\fIprofile\fR
+.RS 4
+Deletes a profile\&.
+.RE
+.PP
\fB\-e, \-\-embed\fR=\fIwid\fR
.RS 4
Embed dwb into <wid>\&.
@@ -1402,9 +1407,19 @@ lt lt lt
lt lt lt
lt lt lt
lt lt lt
+lt lt lt
lt lt lt.
T{
.sp
+adblock_reload_rules
+T}:T{
+.sp
+T}:T{
+.sp
+Reload adblocker rules
+T}
+T{
+.sp
allow_cookie
T}:T{
.sp
diff --git a/doc/dwb.1.txt b/doc/dwb.1.txt
index d34345e4..f8267270 100644
--- a/doc/dwb.1.txt
+++ b/doc/dwb.1.txt
@@ -26,6 +26,9 @@ OPTIONS
*-c, --check-script-syntax*='script'::
Checks if a the syntax of a script that uses the javascript api is correct.
+*-d, --delete-profile*='profile'::
+ Deletes a profile.
+
*-e, --embed*='wid'::
Embed dwb into <wid>.
@@ -733,6 +736,7 @@ COMMAND OVERVIEW
[options="header"]
|================
|Command |Alias |Description
+|adblock_reload_rules | |Reload adblocker rules
|allow_cookie |cookie |Allow persistent cookies for current site
|allow_session_cookie |scookie |Allow session cookies for currrent site
|allow_session_cookie_tmp|tcookie |Allow session cookies for current site
diff --git a/doc/dwbem.1 b/doc/dwbem.1
index 2f90071b..307df978 100644
--- a/doc/dwbem.1
+++ b/doc/dwbem.1
@@ -1,13 +1,13 @@
'\" t
.\" Title: dwbem
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.77.1 <http://docbook.sf.net/>
-.\" Date: 02/01/2013
+.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
+.\" Date: 05/25/2013
.\" Manual: \ \&
.\" Source: \ \&
.\" Language: English
.\"
-.TH "DWBEM" "1" "02/01/2013" "\ \&" "\ \&"
+.TH "DWBEM" "1" "05/25/2013" "\ \&" "\ \&"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
@@ -174,6 +174,13 @@ Update
in combination with
\fB\-\-no\-confirm\fR\&.
.RE
+.PP
+\fB\-\-archive\fR
+.RS 4
+Create, unpack or manipulate extension archives, use
+\fIdwbem \-\-archive h\fR
+for detailed information\&.
+.RE
.SH "ENVIRONMENT VARIABLES"
.sp
\fBdwbem\fR expects the environment variables \fIEDITOR\fR and \fIDIFF_VIEWER\fR to be set, if they are unset \fIvim\fR and \fIvimdiff\fR are used respectively\&. \fIEDITOR\fR needs to be set if the configuration is edited or a new extension is installed, \fIDIFF_VIEWER\fR needs to be set if extensions are updated\&.
diff --git a/doc/dwbem.1.txt b/doc/dwbem.1.txt
index d7df0c7b..2a337eb5 100644
--- a/doc/dwbem.1.txt
+++ b/doc/dwbem.1.txt
@@ -112,6 +112,10 @@ OPTIONS
the configuration has been changed after using *--upgrade* in combination
with *--no-confirm*.
+*--archive*::
+ Create, unpack or manipulate extension archives, use 'dwbem --archive h' for
+ detailed information.
+
ENVIRONMENT VARIABLES
---------------------
diff --git a/tools/Makefile b/dwbem/Makefile
index 73eabf6f..059703f2 100644
--- a/tools/Makefile
+++ b/dwbem/Makefile
@@ -3,8 +3,12 @@
include ../config.mk
TARGET=dwbem
-OBJ = $(patsubst %.c, %.o, $(wildcard *.c))
-LIBS=libsoup-2.4 glib-2.0
+BASEDIR = ..
+
+CFLAGS += -I$(BASEDIR)/$(DWB_LIB_DIRS)
+
+OBJ = $(patsubst %.c, %.o, $(wildcard *.c)) $(BASEDIR)/$(OBJLIB)
+
SYSTEM_EXTENSION_DIR=$(PREFIX)/share/dwb/extensions
@@ -22,11 +26,11 @@ LDFLAGS += $(shell pkg-config --libs $(LIBS))
all: $(TARGET)
$(TARGET): $(OBJ)
- @echo $(CC) $@
- @$(CC) $< -o $@ $(LDFLAGS)
+ @echo $(CC) -o $@
+ @$(CC) $(OBJ) -o $@ $(LDFLAGS)
%.o: %.c
- @echo $(CC) $<
+ @echo $(CC) $<
@$(CC) $(CFLAGS) -c $< -o $@ $(CPPFLAGS)
clean:
diff --git a/tools/dwbem.c b/dwbem/dwbem.c
index 16aea385..cbfb4368 100644
--- a/tools/dwbem.c
+++ b/dwbem/dwbem.c
@@ -19,7 +19,7 @@
#ifndef _BSD_SOURCE
#define _BSD_SOURCE
#endif
-#ifndef _BSD_SOURCE
+#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
#endif
@@ -29,13 +29,16 @@
#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
-//#include <readline/readline.h>
+#include <features.h>
#include <termios.h>
+#include <JavaScriptCore/JavaScript.h>
#include <sys/stat.h>
#include <glib-2.0/glib.h>
#include <libsoup-2.4/libsoup/soup.h>
+#include <exar.h>
+
#define API_BASE "https://api.bitbucket.org/1.0/repositories/portix/dwb_extensions/src/tip/src/?format=yaml"
#define REPO_BASE "https://bitbucket.org/portix/dwb_extensions"
#define REPO_TREE "/raw/tip/src"
@@ -60,6 +63,19 @@
#define EXT(name) "\033[1m"#name"\033[0m"
#define FREE0(X) (X == NULL ? NULL : (X = (g_free(X), NULL)))
+enum {
+ EXAR_FLAG_v = 1<<0,
+ EXAR_FLAG_p = 1<<3,
+ EXAR_FLAG_u = 1<<4,
+ EXAR_FLAG_e = 1<<6,
+ EXAR_FLAG_d = 1<<7,
+ EXAR_FLAG_l = 1<<8,
+ EXAR_FLAG_s = 1<<9,
+ EXAR_FLAG_a = 1<<10,
+};
+#define EXAR_OPTION_FLAG (0xffff & ~(0x7))
+#define EXAR_CHECK_FLAG(x, flag) !!(((x) & (EXAR_FLAG_##flag)) && !((x) & ( (EXAR_OPTION_FLAG)^(EXAR_FLAG_##flag) ) ))
+
enum
{
F_NO_CONFIG = 1<<0,
@@ -367,17 +383,17 @@ get_response(char *buffer, size_t length, const char *format, ...)
}
int
-create_tmp(char *buffer, int size, const char *template, int suffix_length)
+create_tmp(char *buffer, int size, const char *template)
{
strncpy(buffer, template, size);
- int fd = mkstemps(buffer, suffix_length);
+ int fd = mkstemp(buffer);
if (fd == -1)
die(1, "Cannot create temporary file");
return fd;
}
static int
-diff(const char *text1, const char *text2, char **ret)
+diff(const char *text1, const char *text2, char **ret, int ntf)
{
gboolean spawn_success = true;
GError *e = NULL;
@@ -390,15 +406,18 @@ diff(const char *text1, const char *text2, char **ret)
return 0;
}
- notify("The default configuration differs from configuration in use");
- if (! yes_no(1, "Edit configuration"))
+ if (ntf)
{
- *ret = g_strdup(text1);
- return 1;
+ notify("The default configuration differs from configuration in use");
+ if (! yes_no(1, "Edit configuration"))
+ {
+ *ret = g_strdup(text1);
+ return 1;
+ }
}
- int fd1 = create_tmp(file1, sizeof(file1), "/tmp/tmpXXXXXXorig.js", 7);
- int fd2 = create_tmp(file2, sizeof(file2), "/tmp/tmpXXXXXXnew.js", 6);
+ int fd1 = create_tmp(file1, sizeof(file1), "/tmp/dwbem_orig_XXXXXX");
+ int fd2 = create_tmp(file2, sizeof(file2), "/tmp/dwbem_new_XXXXXX");
char *text2_new = g_strdup_printf("// THIS FILE WILL BE DISCARDED\n%s// THIS FILE WILL BE DISCARDED", text2);
if (g_file_set_contents(file1, text1, -1, &e) && g_file_set_contents(file2, text2_new, -1, &e))
@@ -436,7 +455,7 @@ edit(const char *text)
char *new_config = NULL;
char file[32];
- int fd = create_tmp(file, sizeof(file), "/tmp/tmpXXXXXX.js", 3);
+ int fd = create_tmp(file, sizeof(file), "/tmp/dwbem_XXXXXX");
if (g_file_set_contents(file, text, -1, NULL))
{
@@ -524,6 +543,40 @@ set_loader(const char *name, const char *config, int flags)
}
g_free(script);
}
+static int
+check_config(const char *config)
+{
+ int ret = 0;
+ JSValueRef exc = NULL;
+ JSGlobalContextRef ctx = JSGlobalContextCreate(NULL);
+ char *object_str = g_strdup_printf("var x = { \n%s\n };", config);
+ JSStringRef js_str = JSStringCreateWithUTF8CString(object_str);
+ if (!JSCheckScriptSyntax(ctx, js_str, NULL, 2, &exc))
+ {
+ ret = -1;
+ }
+ JSStringRelease(js_str);
+ g_free(object_str);
+ JSGlobalContextRelease(ctx);
+ return ret;
+}
+static char *
+do_edit(const char *config)
+{
+ char *new_config, *tmp_config = NULL;
+ while(1)
+ {
+ new_config = edit(config);
+ if (tmp_config != NULL)
+ g_free(tmp_config);
+ if (check_config(new_config) == 0 || yes_no(0, "The configuration contains errors, continue anyway"))
+ break;
+ config = new_config;
+ tmp_config = new_config;
+ }
+ return new_config;
+}
+
static int
add_to_loader(const char *name, const char *content, int flags)
@@ -532,6 +585,7 @@ add_to_loader(const char *name, const char *content, int flags)
char *config = NULL;
const char *new_config = NULL;
char *data = NULL;
+ int ntf = 1;
gchar **matches = g_regex_split_simple("//<DEFAULT_CONFIG|//>DEFAULT_CONFIG", content, G_REGEX_DOTALL, 0);
if (matches[1] == NULL)
@@ -545,19 +599,44 @@ add_to_loader(const char *name, const char *content, int flags)
}
data = get_data(name, m_loader, TMPL_CONFIG, 0);
- if (diff(data, matches[1], &config) == 0) {
- notify("Config is up to date");
- goto unwind;
+ while (1)
+ {
+ if (diff(data, matches[1], &config, ntf) == 0)
+ {
+ notify("Config is up to date");
+ goto unwind;
+ }
+ else
+ {
+ if (check_config(config) != 0)
+ {
+ if (yes_no(0, "The configuration contains errors, continue anyway"))
+ {
+ new_config = config;
+ break;
+ }
+ else
+ {
+ data = config;
+ ntf = 0;
+ }
+ }
+ else
+ {
+ new_config = config;
+ break;
+ }
+ }
}
- else
- new_config = config;
}
else if (flags & F_NO_CONFIRM) {
notify("Skipping configuration check");
new_config = matches[1];
}
else if (!(flags & F_NO_CONFIG) && yes_no(1, "Edit configuration") == 1)
- new_config = config = edit(matches[1]);
+ {
+ new_config = do_edit(matches[1]);
+ }
else
new_config = matches[1];
@@ -745,7 +824,7 @@ cl_change_config(const char *name, int flags)
new_config = config;
else
{
- new_config = edit(config);
+ new_config = do_edit(config);
g_free(config);
}
@@ -1073,7 +1152,7 @@ cl_edit(const char *name, int flags)
char *config = get_config(name);
if (config != NULL)
{
- char *new_config = edit(config);
+ char *new_config = do_edit(config);
if (new_config != NULL)
{
set_data(name, new_config, TMPL_CONFIG, 0);
@@ -1084,6 +1163,117 @@ cl_edit(const char *name, int flags)
}
return -1;
}
+void
+exar_help(int ret)
+{
+ printf("USAGE: \n"
+ " dwbem --archive option [arguments]\n\n"
+ "OPTIONS:\n"
+ " h Print this help and exit.\n"
+ " a[v] archive file Appends a file or directory to an archive\n"
+ " d[v] archive file Deletes a file from an archive, the file path is the\n"
+ " relative file path of the file in the archive\n"
+ " e[v] archive file Extracts a file from an archive and writes the content\n"
+ " to stdout, the archive is not modified, the file path \n"
+ " is the relative file path of the file in the archive.\n"
+ " l[v] archive List archive content\n"
+ " p[v] path Pack file or directory 'path'.\n"
+ " s[v] archive file Search for a file and write the content to stdout, the \n"
+ " archive is not modified, the filename is the basename\n"
+ " plus suffix of the file in the archive, all directory\n"
+ " parts are stripped\n"
+ " u[v] file [dir] Pack 'file' to directory 'dir' or to current directory.\n"
+ " v Verbose, pass multiple times (up to 3) to \n"
+ " get more verbose messages.\n\n"
+ "EXAMPLES:\n"
+ " dwbem --archive p /tmp/foo -- pack /tmp/foo to foo.exar\n"
+ " dwbem --archive s foo.js > foo.js -- Extract foo.js from the archive\n"
+ " dwbem --archive uvvv foo.exar -- unpack foo.exar to current directory,\n"
+ " verbosity level 3\n"
+ " dwbem --archive vu foo.exar /tmp -- unpack foo.exar to /tmp, verbosity\n"
+ " level 1\n");
+ exit(ret);
+}
+static void
+exar_xextract(const char *archive, const char *path,
+ unsigned char * (*extract_func)(const char *, const char *, off_t *))
+{
+ off_t s;
+ unsigned char *content = extract_func(archive, path, &s);
+ if (content != NULL)
+ {
+ fwrite(content, 1, s, stdout);
+ free(content);
+ }
+}
+void
+parse_exar_options(char **argv)
+{
+ int argc = 0, flag = 0;
+ const char *options;
+
+ if (argv == NULL)
+ exar_help(EXIT_FAILURE);
+
+ argc = g_strv_length(argv);
+ if (argc < 2)
+ exar_help(EXIT_FAILURE);
+ options = argv[0];
+ while (*options)
+ {
+ switch (*options)
+ {
+ case 'a' :
+ flag |= EXAR_FLAG_a;
+ break;
+ case 'd' :
+ flag |= EXAR_FLAG_d;
+ break;
+ case 'e' :
+ flag |= EXAR_FLAG_e;
+ break;
+ case 'l' :
+ flag |= EXAR_FLAG_l;
+ break;
+ case 'p' :
+ flag |= EXAR_FLAG_p;
+ break;
+ case 's' :
+ flag |= EXAR_FLAG_s;
+ break;
+ case 'u' :
+ flag |= EXAR_FLAG_u;
+ break;
+ case 'v' :
+ flag |= MAX(EXAR_FLAG_v, MIN(EXAR_VERBOSE_MASK, ((flag & EXAR_VERBOSE_MASK) << 1)));
+ break;
+ case 'h' :
+ exar_help(EXIT_SUCCESS);
+ default :
+ exar_help(EXIT_FAILURE);
+ }
+ options++;
+ }
+ if (flag & EXAR_VERBOSE_MASK)
+ exar_verbose(flag);
+
+ if (EXAR_CHECK_FLAG(flag, a) && argc > 2)
+ exar_append(argv[1], argv[2]);
+ else if (EXAR_CHECK_FLAG(flag, d) && argc > 2)
+ exar_delete(argv[1], argv[2]);
+ else if (EXAR_CHECK_FLAG(flag, e) && argc > 2)
+ exar_xextract(argv[1], argv[2], exar_extract);
+ else if (EXAR_CHECK_FLAG(flag, l))
+ exar_info(argv[1]);
+ else if (EXAR_CHECK_FLAG(flag, p))
+ exar_pack(argv[1]);
+ else if (EXAR_CHECK_FLAG(flag, s) && argc > 2)
+ exar_xextract(argv[1], argv[2], exar_search_extract);
+ else if (EXAR_CHECK_FLAG(flag, u))
+ exar_unpack(argv[1], argv[2]);
+ else
+ exar_help(EXIT_FAILURE);
+}
int
main(int argc, char **argv)
@@ -1107,6 +1297,8 @@ main(int argc, char **argv)
char **o_show_config = NULL;
char **o_edit = NULL;
char *o_proxy = NULL;
+ char **o_archive_options = NULL;
+ gboolean o_archive = NULL;
gboolean o_noconfig = false;
gboolean o_update = false;
gboolean o_list_installed = false;
@@ -1132,6 +1324,8 @@ main(int argc, char **argv)
{ "proxy", 'p', 0, G_OPTION_ARG_STRING, &o_proxy, "HTTP-proxy to use", NULL },
{ "upgrade", 'u', 0, G_OPTION_ARG_NONE, &o_update, "Update all extensions", NULL },
{ "update", 'U', 0, G_OPTION_ARG_STRING_ARRAY, &o_update_ext, "Update <extension>", "<extension>" },
+ { "archive", 0, 0, G_OPTION_ARG_NONE, &o_archive, "Create, open or modify extension archives", NULL },
+ { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &o_archive_options, NULL, NULL },
{ NULL, 0, 0, 0, NULL, NULL, NULL },
};
@@ -1196,6 +1390,8 @@ main(int argc, char **argv)
list(m_installed, "Installed extensions", false);
if (o_update)
cl_update(flags);
+ if (o_archive)
+ parse_exar_options(o_archive_options);
for_each(o_update_ext, flags, cl_update_ext);
for_each(o_info, flags, cl_info);
@@ -1207,6 +1403,7 @@ main(int argc, char **argv)
for_each(o_install, flags, cl_install);
for_each(o_show_config, flags, cl_show_config);
for_each(o_edit, flags, cl_edit);
+
clean_up();
return 0;
}
diff --git a/exar/Makefile b/exar/Makefile
new file mode 100644
index 00000000..c8db4a4b
--- /dev/null
+++ b/exar/Makefile
@@ -0,0 +1,34 @@
+ORIG_CFLAGS := $(CFLAGS)
+
+CFLAGS := -Wall -pedantic -Werror -Wextra -std=c99 -Os
+CFLAGS += $(ORIG_CFLAGS)
+
+DCFLAGS += -g -O0 -Wall -pedantic -Werror -Wextra -std=c99
+DCFLAGS += $(ORIG_CFLAGS)
+
+TARGET = exar
+OBJ = $(patsubst %.c, %.o, $(wildcard *.c))
+
+SHARED_OBJ = exar.o
+
+all: $(SHARED_OBJ)
+
+$(TARGET): $(OBJ)
+ @echo $(CC) -o $@
+ @$(CC) $(OBJ) -o $@ $(CFLAGS) $(CPPFLAGS)
+
+%.o: %.c
+ @echo $(CC) $<
+ @$(CC) -c $< -o $@ $(CFLAGS) $(CPPFLAGS)
+
+debug:
+ make CFLAGS="$(DCFLAGS)"
+
+cgdb:
+ make CFLAGS="$(DCFLAGS)"
+ cgdb exar
+
+clean:
+ $(RM) -rf $(OBJ) $(TARGET)
+
+.PHONY: clean
diff --git a/exar/exar.c b/exar/exar.c
new file mode 100644
index 00000000..96940d2b
--- /dev/null
+++ b/exar/exar.c
@@ -0,0 +1,677 @@
+/*
+ * Copyright (c) 2013 Stefan Bolte <portix@gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _BSD_SOURCE
+#define _BSD_SOURCE
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <ftw.h>
+#include <errno.h>
+#include <assert.h>
+#include <inttypes.h>
+#include "exar.h"
+
+#define EXAR_VERSION_BASE "exar-"
+#define EXAR_VERSION EXAR_VERSION_BASE "1"
+#define EXTENSION "exar"
+
+#define SZ_VERSION 7
+#define SZ_DFLAG 1
+#define SZ_SIZE 14
+
+#define HDR_DFLAG (0)
+#define HDR_SIZE (HDR_DFLAG + SZ_DFLAG)
+#define HDR_NAME (HDR_SIZE + SZ_SIZE)
+
+#define DIR_FLAG (100)
+#define FILE_FLAG (102)
+
+#define MAX_FILE_HANDLES 64
+
+#define MIN(X, Y) ((X) > (Y) ? (Y) : (X))
+
+#define EXAR_NAME_MAX 4096
+
+#define EE_OK 0
+#define EE_ERROR -1
+#define EE_EOF -2
+
+struct exar_header_s {
+ unsigned char eh_flag;
+ off_t eh_size;
+ char eh_name[EXAR_NAME_MAX];
+};
+#define EXAR_HEADER_EMPTY { 0, 0, { 0 } }
+
+#define LOG(level, ...) do { if (s_verbose & EXAR_VERBOSE_L##level) { \
+ fprintf(stderr, "exar-log%d: ", level); \
+ fprintf(stderr, __VA_ARGS__); } } while(0)
+
+static size_t s_offset;
+static FILE *s_out;
+static unsigned char s_verbose = 0;
+
+static void *
+xcalloc(size_t nmemb, size_t size)
+{
+ void *ret = calloc(nmemb, size);
+ if (ret == NULL)
+ {
+ fprintf(stderr, "Cannot malloc %zu bytes\n", size * nmemb);
+ exit(EXIT_FAILURE);
+ }
+ return ret;
+}
+
+static size_t
+get_offset(char *buffer, size_t n, const char *path, int *end)
+{
+ const char *tmp = path, *slash;
+ size_t len = strlen(path);
+ size_t offset = 0;
+ int i=0;
+
+ // strip trailing '/'
+ while (tmp[len-1] == '/')
+ len--;
+ strncpy(buffer, path, MIN(n, len));
+
+ // get base name offset
+ slash = strrchr(buffer, '/');
+ if (slash != NULL)
+ offset = slash - buffer + 1;
+ for (tmp = path + offset; *tmp && *tmp != '/'; i++, tmp++)
+ buffer[i] = *tmp;
+ if (end != NULL)
+ *end = i;
+ return offset;
+}
+static int
+check_version(FILE *f, int verbose)
+{
+ unsigned char version[SZ_VERSION] = {0};
+ unsigned char orig_version[SZ_VERSION] = {0};
+ LOG(2, "Reading version header\n");
+ if (fread(version, 1, SZ_VERSION, f) != SZ_VERSION)
+ {
+ if (feof(f))
+ return EE_EOF;
+ else
+ {
+ if (verbose)
+ fprintf(stderr, "Not an exar file?\n");
+ return EE_ERROR;
+ }
+ }
+ memcpy(orig_version, EXAR_VERSION, sizeof(orig_version));
+ LOG(2, "Checking filetype\n");
+ if (strncmp((char*)version, EXAR_VERSION_BASE, 5))
+ {
+ if (verbose)
+ fprintf(stderr, "Not an exar file?\n");
+ return EE_ERROR;
+ }
+
+ LOG(2, "Found version %s\n", version);
+ if (memcmp(version, orig_version, SZ_VERSION))
+ {
+ if (verbose)
+ fprintf(stderr, "Incompatible version number\n");
+ return EE_ERROR;
+ }
+ return EE_OK;
+}
+/*
+ * Opens archive and checks version, mode is either read or read-write
+ * */
+static FILE *
+open_archive(const char *path, const char *mode)
+{
+ FILE *f = NULL;
+ LOG(3, "Opening %s for %s\n", path, strcmp(mode, "r") == 0 ? "reading" : "reading and writing");
+ if ((f = fopen(path, mode)) == NULL)
+ {
+ perror(path);
+ return NULL;
+ }
+ return f;
+}
+static void
+close_file(FILE *f, const char *archive)
+{
+ if (f != NULL)
+ {
+ LOG(3, "Closing %s\n", archive);
+ fclose(f);
+ }
+}
+
+static int
+get_file_header(FILE *f, struct exar_header_s *head)
+{
+ char *endptr;
+ char header[HDR_NAME];
+ off_t fs;
+ char rb;
+ size_t i = 0;
+ int st_version = 0;
+
+ if ((st_version = check_version(f, 1)) != EE_OK)
+ return st_version;
+
+ LOG(2, "Reading file header\n");
+ if (fread(header, 1, HDR_NAME, f) != HDR_NAME)
+ {
+ fprintf(stderr, "Reading file header failed");
+ return EE_ERROR;
+ }
+
+ head->eh_flag = header[HDR_DFLAG];
+
+ if (head->eh_flag != DIR_FLAG && head->eh_flag != FILE_FLAG)
+ {
+ LOG(1, "No file flag found\n");
+ fprintf(stderr, "The archive seems to be corrupted\n");
+ return EE_ERROR;
+ }
+ if (head->eh_flag == FILE_FLAG)
+ {
+ fs = strtol(&header[HDR_SIZE], &endptr, 16);
+ if (*endptr)
+ {
+ LOG(1, "Cannot determine file size\n");
+ fprintf(stderr, "The archive seems to be corrupted\n");
+ return EE_ERROR;
+ }
+ head->eh_size = fs;
+ }
+ else
+ head->eh_size = 0;
+
+ while (fread(&rb, 1, 1, f) > 0)
+ {
+ head->eh_name[i] = rb;
+ i++;
+ if (rb == '\0')
+ break;
+ else if (i == EXAR_NAME_MAX)
+ {
+ fprintf(stderr, "Cannot get filename\n");
+ return EE_ERROR;
+ }
+ }
+
+ LOG(2, "Found file header (%s, %c, %jd)\n", head->eh_name, head->eh_flag, (intmax_t)head->eh_size);
+ return EE_OK;
+}
+static int
+next_file(FILE *f, struct exar_header_s *header)
+{
+ if (*(header->eh_name))
+ {
+ if (header->eh_flag == FILE_FLAG)
+ {
+ if (fseek(f, header->eh_size, SEEK_CUR) != 0)
+ return EE_ERROR;
+ else
+ LOG(3, "Skipping %s\n", header->eh_name);
+ }
+ }
+ return get_file_header(f, header);
+}
+static int
+find_cmp(const char *name, const char *search)
+{
+ char buffer[EXAR_NAME_MAX];
+ if (strcmp(name, search) != 0)
+ {
+ size_t offset = get_offset(buffer, EXAR_NAME_MAX, name, NULL);
+ return strcmp(&name[offset], search);
+ }
+ return 0;
+}
+static int
+contains(const char *archive, const char *name, int (*cmp)(const char *, const char *))
+{
+ FILE *f = NULL;
+ struct exar_header_s header = EXAR_HEADER_EMPTY;
+ int result = EE_ERROR;
+
+ if ((f = open_archive(archive, "r")) == NULL)
+ goto finish;
+ while (next_file(f, &header) == EE_OK)
+ {
+ if (cmp(header.eh_name, name) == 0)
+ {
+ result = EE_OK;
+ break;
+ }
+ }
+
+finish:
+ close_file(f, archive);
+ return result;
+}
+static unsigned char *
+extract(const char *archive, const char *file, off_t *s, int (*cmp)(const char *, const char *))
+{
+ struct exar_header_s header = EXAR_HEADER_EMPTY;
+ FILE *f = NULL;
+ unsigned char *ret = NULL;
+ if (s != NULL)
+ *s = 0;
+
+ if ((f = open_archive(archive, "r")) == NULL)
+ goto finish;
+ while (get_file_header(f, &header) == EE_OK)
+ {
+ if (cmp(header.eh_name, file) == 0)
+ {
+ if (header.eh_flag == FILE_FLAG)
+ {
+ ret = xcalloc(header.eh_size, sizeof(unsigned char));
+ LOG(3, "Reading %s\n", header.eh_name);
+ if (fread(ret, 1, header.eh_size, f) != (size_t)header.eh_size)
+ {
+ fprintf(stderr, "Failed to read %s\n", header.eh_name);
+ *s = -1;
+ free(ret);
+ ret = NULL;
+ }
+ else if (s != NULL)
+ *s = header.eh_size;
+ }
+ else
+ fprintf(stderr, "%s is a directory, only regular files can be extracted\n", file);
+ goto finish;
+ }
+ else if (header.eh_flag == FILE_FLAG)
+ {
+ LOG(3, "Skipping %s\n", header.eh_name);
+ fseek(f, header.eh_size, SEEK_CUR);
+ }
+ }
+ fprintf(stderr, "File %s was not found in %s\n", file, archive);
+finish:
+ close_file(f, archive);
+ return ret;
+}
+
+static int
+write_file_header(FILE *f, const char *name, char flag, off_t r)
+{
+ unsigned char version[SZ_VERSION] = {0};
+ char buffer[HDR_NAME] = {0};
+ size_t l_name;
+ char term = 0;
+
+ l_name = strlen(name);
+ if (l_name > EXAR_NAME_MAX)
+ {
+ fprintf(stderr, "Filename too long\n");
+ return EE_ERROR;
+ }
+
+ LOG(2, "Writing version header (%s)\n", EXAR_VERSION);
+
+ memcpy(version, EXAR_VERSION, sizeof(version));
+ if (fwrite(version, 1, sizeof(version), f) != sizeof(version))
+ {
+ fprintf(stderr, "Failed to write %zu bytes", sizeof(version));
+ return EE_ERROR;
+ }
+
+ LOG(2, "Writing file header for %s\n", name);
+
+ memset(buffer, 0, sizeof(buffer));
+ buffer[HDR_DFLAG] = flag;
+ snprintf(buffer + HDR_SIZE, SZ_SIZE, "%.13x", flag == FILE_FLAG ? (unsigned int)r : 0);
+ if (fwrite(buffer, 1, HDR_NAME, f) != HDR_NAME)
+ return EE_ERROR;
+ if (fwrite(name, 1, l_name, f) != l_name)
+ return EE_ERROR;
+ if (fwrite(&term, 1, 1, f) != 1)
+ return EE_ERROR;
+
+ return EE_OK;
+}
+
+static int
+ftw_pack(const char *fpath, const struct stat *st, int tf)
+{
+ (void)tf;
+
+ int result = -1;
+ unsigned char rbuf[512];
+ size_t r;
+ FILE *f = NULL;
+ char flag;
+ const char *stripped = &fpath[s_offset];
+
+ LOG(1, "Packing %s (archive path: %s)\n", fpath, stripped);
+ if (S_ISDIR(st->st_mode))
+ flag = DIR_FLAG;
+ else if (S_ISREG(st->st_mode))
+ {
+ flag = FILE_FLAG;
+ LOG(3, "Opening %s for reading\n", fpath);
+ f = fopen(fpath, "r");
+ if (f == NULL)
+ {
+ perror(fpath);
+ return 0;
+ }
+ }
+ else
+ {
+ LOG(1, "Only directories and regular files will be packed, ignoring %s\n", fpath);
+ return 0;
+ }
+
+ if (write_file_header(s_out, stripped, flag, st->st_size) != 0)
+ goto finish;
+
+ if (f != NULL)
+ {
+ LOG(2, "Writing %s (%jd bytes)\n", stripped, (intmax_t)(st->st_size));
+ while ((r = fread(rbuf, 1, sizeof(rbuf), f)) > 0)
+ {
+ if (fwrite(rbuf, 1, r, s_out) != r)
+ {
+ fprintf(stderr, "Failed to write %zu bytes", r);
+ goto finish;
+ }
+ }
+ }
+ result = 0;
+finish:
+ close_file(f, fpath);
+ return result;
+}
+static int
+pack (const char *archive, const char *path, const char *mode)
+{
+ int ret = EE_OK;
+
+ LOG(3, "Opening %s for writing\n", archive);
+ if ((s_out = fopen(archive, mode)) == NULL)
+ {
+ perror(archive);
+ return EE_ERROR;
+ }
+
+ ret = ftw(path, ftw_pack, MAX_FILE_HANDLES);
+
+ LOG(3, "Closing %s\n", archive);
+
+ fclose(s_out);
+ return ret;
+}
+
+int
+exar_pack(const char *path)
+{
+ assert(path != NULL);
+
+ char archive[EXAR_NAME_MAX];
+ int i = 0;
+
+ s_offset = get_offset(archive, sizeof(archive), path, &i);
+
+ strncpy(&archive[i], "." EXTENSION, sizeof(archive) - i);
+
+ return pack(archive, path, "w");
+}
+int
+exar_append(const char *archive, const char *path)
+{
+ assert(path != NULL);
+ char stripped[EXAR_NAME_MAX];
+
+ s_offset = get_offset(stripped, sizeof(stripped), path, 0);
+
+ return pack(archive, path, "a");
+}
+
+int
+exar_unpack(const char *archive, const char *dest)
+{
+ assert(archive != NULL);
+
+ struct exar_header_s header = EXAR_HEADER_EMPTY;
+ int ret = EE_ERROR;
+ FILE *of, *f = NULL;
+ unsigned char buf[512];
+ size_t r = 0;
+ int status;
+
+ if ((f = open_archive(archive, "r")) == NULL)
+ goto finish;
+
+ if (dest != NULL)
+ {
+ LOG(2, "Changing to directory %s\n", dest);
+ if (chdir(dest) != 0)
+ {
+ perror(dest);
+ goto finish;
+ }
+ }
+
+ while (1)
+ {
+ status = get_file_header(f, &header);
+ if (status == EE_EOF)
+ break;
+ else if (status == EE_ERROR)
+ goto finish;
+
+ if (header.eh_flag == DIR_FLAG)
+ {
+ LOG(1, "Creating directory %s\n", header.eh_name);
+ if (mkdir(header.eh_name, 0755) != 0 && errno != EEXIST)
+ {
+ perror(header.eh_name);
+ goto finish;
+ }
+ }
+ else
+ {
+ LOG(1, "Unpacking %s\n", header.eh_name);
+
+ LOG(3, "Opening %s for writing\n", header.eh_name);
+ of = fopen(header.eh_name, "w");
+ if (of == NULL)
+ {
+ perror(header.eh_name);
+ goto finish;
+ }
+
+ LOG(2, "Writing %s (%jd bytes)\n", header.eh_name, (intmax_t)header.eh_size);
+ for (off_t i=0; i<header.eh_size; i += sizeof(buf))
+ {
+ if ( (r = fread(buf, 1, MIN(sizeof(buf), (size_t)header.eh_size - i), f)) != 0)
+ {
+ if (fwrite(buf, 1, r, of) != r)
+ {
+ fprintf(stderr, "Failed to write %zu bytes\n", r);
+ goto finish;
+ }
+ }
+ }
+ LOG(3, "Closing %s\n", header.eh_name);
+ fclose(of);
+ }
+ }
+ ret = EE_OK;
+finish:
+ close_file(f, archive);
+ return ret;
+}
+
+unsigned char *
+exar_extract(const char *archive, const char *file, off_t *s)
+{
+ assert(archive != NULL && file != NULL);
+
+ return extract(archive, file, s, strcmp);
+}
+unsigned char *
+exar_search_extract(const char *archive, const char *file, off_t *s)
+{
+ assert(archive != NULL && file != NULL);
+
+ return extract(archive, file, s, find_cmp);
+}
+int
+exar_delete(const char *archive, const char *file)
+{
+ assert(archive != NULL && file != NULL);
+
+ int result = EE_ERROR;
+ struct exar_header_s header = EXAR_HEADER_EMPTY;
+ FILE *f = NULL, *ftmp = NULL;
+ char tmp_file[128];
+ char dir_name[EXAR_NAME_MAX-1] = {0};
+ unsigned char rbuf;
+ size_t dir_length = 0;
+ int status = EE_ERROR;
+
+ if ((f = open_archive(archive, "r")) == NULL)
+ goto finish;
+
+ snprintf(tmp_file, sizeof(tmp_file), "%s.XXXXXX", archive);
+ if (mktemp(tmp_file) == NULL)
+ {
+ fprintf(stderr, "Failed to create temporary file\n");
+ goto finish;
+ }
+
+ LOG(3, "Opening %s for writing\n", tmp_file);
+ if ((ftmp = fopen(tmp_file, "w")) == NULL)
+ goto finish;
+
+ while ((status = get_file_header(f, &header)) == EE_OK)
+ {
+ if (strcmp(header.eh_name, file) == 0)
+ {
+ if (header.eh_flag == FILE_FLAG)
+ {
+ LOG(1, "Skipping %s\n", header.eh_name);
+ fseek(f, header.eh_size, SEEK_CUR);
+ }
+ else if (header.eh_flag == DIR_FLAG)
+ dir_length = snprintf(dir_name, sizeof(dir_name), "%s/", header.eh_name);
+ }
+ else if (*dir_name && strncmp(dir_name, header.eh_name, dir_length) == 0)
+ {
+ if (header.eh_flag == FILE_FLAG)
+ {
+ LOG(1, "Skipping %s\n", header.eh_name);
+ fseek(f, header.eh_size, SEEK_CUR);
+ }
+ }
+ else
+ {
+ LOG(1, "Packing %s\n", header.eh_name);
+ write_file_header(ftmp, header.eh_name, header.eh_flag, header.eh_size);
+ if (header.eh_flag == FILE_FLAG)
+ {
+ LOG(2, "Copying %s (%jd bytes)\n", header.eh_name, (intmax_t)header.eh_size);
+ for (off_t s=0; s<header.eh_size; s++)
+ {
+ if (fread(&rbuf, 1, 1, f) != 1 || fwrite(&rbuf, 1, 1, ftmp) != 1)
+ {
+ fprintf(stderr, "Error copying %s\n", header.eh_name);
+ goto finish;
+ }
+ }
+ }
+ }
+ }
+finish:
+ if (status == EE_EOF)
+ {
+ LOG(2, "Copying %s to %s\n", tmp_file, archive);
+ if (rename(tmp_file, archive) == -1)
+ perror(archive);
+ else
+ result = EE_OK;
+ }
+ else if (status == EE_ERROR)
+ {
+ LOG(1, "An error occured, removing temporary file\n");
+ unlink(tmp_file);
+ }
+ close_file(f, archive);
+ close_file(ftmp, tmp_file);
+ return result;
+}
+void
+exar_info(const char *archive)
+{
+ assert(archive != NULL);
+
+ FILE *f = NULL;
+ struct exar_header_s header = EXAR_HEADER_EMPTY;
+
+ if ((f = open_archive(archive, "r")) == NULL)
+ goto finish;
+ while(next_file(f, &header) == EE_OK)
+ fprintf(stdout, "%c %-14jd %s\n", header.eh_flag, (intmax_t)header.eh_size, header.eh_name);
+finish:
+ close_file(f, archive);
+}
+int
+exar_contains(const char *archive, const char *name)
+{
+ assert(archive != NULL);
+
+ return contains(archive, name, strcmp);
+}
+int
+exar_search_contains(const char *archive, const char *name)
+{
+ assert(archive != NULL);
+
+ return contains(archive, name, find_cmp);
+}
+
+int
+exar_check_version(const char *archive)
+{
+ assert(archive != NULL);
+
+ int result = EE_ERROR;
+ FILE *f;
+ if ( (f = fopen(archive, "r")) != NULL && check_version(f, 0) == EE_OK)
+ result = EE_OK;
+ close_file(f, archive);
+ return result;
+}
+void
+exar_verbose(unsigned char v)
+{
+ s_verbose = v & EXAR_VERBOSE_MASK;
+}
diff --git a/exar/exar.h b/exar/exar.h
new file mode 100644
index 00000000..f52cfa1e
--- /dev/null
+++ b/exar/exar.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2013 Stefan Bolte <portix@gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * Pack/Unpack files/directories to dwb-extension archive. Only directories and
+ * regular files are supported. Unlike tar exar doesn't preserve symbolic links,
+ * instead a copy is packed into the archive and an extension archive doesn't
+ * preserve any ownership or permissions, so an extension archive cannot contain
+ * any executables.
+ *
+ * File format:
+ *
+ * [file header][file][file header][file][file he...
+ *
+ * file header : - file info 22 bytes
+ * - 7 bytes version header, null terminated (char)
+ * - 1 byte filetype flag (d|f) (char)
+ * - 14 byte file size, null terminated (char, hex)
+ * - file name, null terminated, maximum 4096 bytes
+ * file : saved as unsigned char
+ * */
+
+#ifndef __EXAR_H__
+#define __EXAR_H__
+
+#include <sys/types.h>
+
+enum {
+ EXAR_VERBOSE_L1 = 1<<0,
+ EXAR_VERBOSE_L2 = 1<<1,
+ EXAR_VERBOSE_L3 = 1<<2,
+};
+#define EXAR_VERBOSE_MASK (0x7)
+
+/*
+ * Packs a file or directory
+ * @path: Path to the file or directory to pack
+ *
+ * @returns 0 on success and -1 on error
+ * */
+int
+exar_pack(const char *path);
+
+/*
+ * Appends a file or directory to the archive
+ * @archive The archive
+ * @path The file or directory to append
+ *
+ * @returns 0 on success
+ */
+int
+exar_append(const char *archive, const char *path);
+
+/*
+ * Unpacks an archive
+ * @path: Path to the extension archive
+ * @dest: Destination directory or NULL for current directory
+ *
+ * @returns 0 on success and -1 on error
+ * */
+int
+exar_unpack(const char *path, const char *dest);
+
+/*
+ * Extracts a file from an extension archive
+ * @archive The archive
+ * @file The path of the file in the archive
+ * @size Return location for the size, if an error occurs size will be set to -1
+ *
+ * @returns A newly allocated char buffer with the file content or NULL if an error
+ * occured or the file was not found in the archive
+ * */
+unsigned char *
+exar_extract(const char *archive, const char *file, off_t *size);
+
+/*
+ * Searches for a file and extracts the content from the archive.
+ *
+ * @archive The archive
+ * @search The search term. The search term must either match the full path or
+ * the filename
+ * @size Return location for the size, if an error occurs size will be set to -1
+ *
+ * @returns A newly allocated char buffer with the file content or NULL if an error
+ * occured or the file was not found in the archive
+ * */
+unsigned char *
+exar_search_extract(const char *archive, const char *search, off_t *size);
+
+/*
+ * Deletes a file from the archive, if it is a directory it is removed
+ * recursively.
+ *
+ * @archive The archive
+ * @file The file to delete
+ *
+ * @returns 0 on success and -1 on error
+ */
+int
+exar_delete(const char *archive, const char *file);
+
+/*
+ * Checks if the file is an archive file with compatible version number
+ *
+ * @archive The archive
+ * @verbose Whether to print error messages to stderr
+ *
+ * @returns 0 on success and -1 on error
+ */
+int
+exar_check_version(const char *archive);
+
+/*
+ * Print info about the archive to stdout.
+ *
+ * @archive The archive
+ * */
+void
+exar_info(const char *archive);
+
+/*
+ * Checks if an archive contains a file
+ *
+ * @archive The archive
+ * @path The path of the file in the archive
+ *
+ * @returns 0 if the file was found, -1 otherwise
+ * */
+int
+exar_contains(const char *archive, const char *path);
+
+/*
+ * Checks if an archive contains a file
+ *
+ * @archive The archive
+ * @search The search term. The search term must either match the full path or
+ * the filename
+ *
+ * @returns 0 if the file was found, -1 otherwise
+ * */
+int
+exar_search_contains(const char *archive, const char *search);
+/*
+ * Set verbosity flags, exar will be most verbose if all flags are set, log
+ * messages are printed to stderr.
+ * @v_flags
+ */
+void
+exar_verbose(unsigned char v_flags);
+#endif
diff --git a/exar/main.c b/exar/main.c
new file mode 100644
index 00000000..297c118e
--- /dev/null
+++ b/exar/main.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2013 Stefan Bolte <portix@gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include "exar.h"
+
+enum {
+ EXAR_FLAG_V = 1<<0,
+ EXAR_FLAG_P = 1<<3,
+ EXAR_FLAG_U = 1<<4,
+ EXAR_FLAG_E = 1<<6,
+ EXAR_FLAG_D = 1<<7,
+ EXAR_FLAG_L = 1<<8,
+ EXAR_FLAG_S = 1<<9,
+ EXAR_FLAG_A = 1<<10,
+};
+#ifndef MIN
+#define MIN(X, Y) ((X) > (Y) ? (Y) : (X))
+#endif
+#ifndef MAX
+#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
+#endif
+
+
+#define EXAR_OPTION_FLAG (0xffff & ~(0x7))
+#define EXAR_CHECK_FLAG(x, flag) !!(((x) & (flag)) && !((x) & ( (EXAR_OPTION_FLAG)^(flag) ) ))
+void
+help(int ret)
+{
+ printf("USAGE: \n"
+ " exar option [arguments]\n\n"
+ "OPTIONS:\n"
+ " h Print this help and exit.\n"
+ " a[v] archive file Appends a file or directory to the archive\n"
+ " d[v] archive file Deletes a file from an archive, the file path is the\n"
+ " relative file path of the file in the archive\n"
+ " e[v] archive file Extracts a file from an archive and writes the content\n"
+ " to stdout, the archive is not modified, the file path \n"
+ " is the relative file path of the file in the archive.\n"
+ " l[v] archive List archive content\n"
+ " p[v] path Pack file or directory 'path'.\n"
+ " s[v] archive file Search for a file and write the content to stdout, the \n"
+ " archive is not modified, the filename is the basename\n"
+ " plus suffix of the file in the archive, all directory\n"
+ " parts are stripped\n"
+ " u[v] file [dir] Pack 'file' to directory 'dir' or to current directory.\n"
+ " v Verbose, pass multiple times (up to 3) to \n"
+ " get more verbose messages.\n\n"
+ "EXAMPLES:\n"
+ " exar p /tmp/foo -- pack /tmp/foo to foo.exar\n"
+ " exar s foo.js > foo.js -- Extract foo.js from the archive\n"
+ " exar uvvv foo.exar -- unpack foo.exar to current directory,\n"
+ " verbosity level 3\n"
+ " exar vu foo.exar /tmp -- unpack foo.exar to /tmp, verbosity\n"
+ " level 1\n");
+ exit(ret);
+}
+static void
+extract(const char *archive, const char *path,
+ unsigned char * (*extract_func)(const char *, const char *, off_t *))
+{
+ off_t s;
+ unsigned char *content = extract_func(archive, path, &s);
+ if (content != NULL)
+ {
+ if (fwrite(content, 1, s, stdout) != (size_t)s)
+ {
+ fprintf(stderr, "An error occured\n");
+ }
+ free(content);
+ }
+}
+int
+main (int argc, char **argv)
+{
+ int flag = 0;
+ if (argc < 3)
+ {
+ help(EXIT_FAILURE);
+ }
+ const char *options = argv[1];
+ while (*options)
+ {
+ switch (*options)
+ {
+ case 'a' :
+ flag |= EXAR_FLAG_A;
+ break;
+ case 'd' :
+ flag |= EXAR_FLAG_D;
+ break;
+ case 'e' :
+ flag |= EXAR_FLAG_E;
+ break;
+ case 'l' :
+ flag |= EXAR_FLAG_L;
+ break;
+ case 'p' :
+ flag |= EXAR_FLAG_P;
+ break;
+ case 's' :
+ flag |= EXAR_FLAG_S;
+ break;
+ case 'u' :
+ flag |= EXAR_FLAG_U;
+ break;
+ case 'v' :
+ flag |= MAX(EXAR_FLAG_V, MIN(EXAR_VERBOSE_MASK, ((flag & EXAR_VERBOSE_MASK) << 1)));
+ break;
+ case 'h' :
+ help(EXIT_SUCCESS);
+ default :
+ help(EXIT_FAILURE);
+ }
+ options++;
+ }
+ if (flag & EXAR_VERBOSE_MASK)
+ exar_verbose(flag);
+
+ if (EXAR_CHECK_FLAG(flag, EXAR_FLAG_U))
+ exar_unpack(argv[2], argv[3]);
+ else if (EXAR_CHECK_FLAG(flag, EXAR_FLAG_A) && argc > 3)
+ exar_append(argv[2], argv[3]);
+ else if (EXAR_CHECK_FLAG(flag, EXAR_FLAG_P))
+ exar_pack(argv[2]);
+ else if (EXAR_CHECK_FLAG(flag, EXAR_FLAG_L))
+ exar_info(argv[2]);
+ else if (EXAR_CHECK_FLAG(flag, EXAR_FLAG_D) && argc > 3)
+ exar_delete(argv[2], argv[3]);
+ else if (EXAR_CHECK_FLAG(flag, EXAR_FLAG_E) && argc > 3)
+ extract(argv[2], argv[3], exar_extract);
+ else if (EXAR_CHECK_FLAG(flag, EXAR_FLAG_S) && argc > 3)
+ extract(argv[2], argv[3], exar_search_extract);
+ else
+ help(EXIT_FAILURE);
+
+ return EXIT_SUCCESS;
+}
diff --git a/m4/keys.m4 b/m4/keys.m4
index 0847ea80..0265c670 100644
--- a/m4/keys.m4
+++ b/m4/keys.m4
@@ -158,6 +158,7 @@ html_input(proxy, text, Toggle proxy)
html_header(Miscellaneous)
dnl
+html_input(adblock_reload_rules, text, Reload adblock rules)
html_input(allow_cookie, text, Allow persistent cookies for current site)
html_input(allow_session_cookie, text, Allow session cookies for current site)
html_input(allow_session_cookie_tmp, text, Allow session cookies for current site temporarily)
diff --git a/scripts/base.js b/scripts/base.js
index dae765cc..fdfa86f6 100644
--- a/scripts/base.js
+++ b/scripts/base.js
@@ -9,6 +9,8 @@ Object.freeze((function () {
};
var globals = {
active : null,
+ matchHint : -1,
+ escapeChar : "\\",
activeArr : [],
activeInput : null,
elements : [],
@@ -216,7 +218,7 @@ Object.freeze((function () {
var inArr = input.split(" ");
for (i=0; i<inArr.length; i++)
{
- if (!this.element.textContent.toUpperCase().match(inArr[i]))
+ if (!this.element.textContent.toUpperCase().match(inArr[i].toUpperCase()))
return false;
}
return true;
@@ -243,7 +245,7 @@ Object.freeze((function () {
{
if (element.hasAttribute("onclick"))
{
- p_mouseEvent(element, ev, !globals.newTab);
+ p_mouseEvent(element, "click", !globals.newTab);
clicked = true;
}
if (element.hasAttribute("onmousedown"))
@@ -506,7 +508,16 @@ Object.freeze((function () {
globals.lastInput = input;
if (input)
{
- if (globals.style == "number")
+ if (input[input.length-1] == globals.escapeChar && globals.matchHint == -1)
+ {
+ globals.matchHint = input.indexOf(globals.escapeChar) + 1;
+ return null;
+ }
+ if (globals.matchHint != -1)
+ {
+ input = input.substring(globals.matchHint);
+ }
+ else if (globals.style == "number")
{
if (input[input.length-1].isInt())
{
@@ -602,6 +613,7 @@ Object.freeze((function () {
globals.lastPosition = 0;
globals.lastInput = null;
globals.positions = [];
+ globals.matchHint = -1;
if (globals.notify && globals.notify.parentNode)
{
globals.notify.parentNode.removeChild(globals.notify);
@@ -620,20 +632,15 @@ Object.freeze((function () {
{
e.target = null;
}
- if (type > 0)
+ if (type == HintTypes.HINT_T_IMAGES)
{
- switch (type)
- {
- case HintTypes.HINT_T_IMAGES:
- ret = e.src; p_clear();
- return ret;
- case HintTypes.HINT_T_URL :
- ret = e.hasAttribute("href") ? e.href : e.src; p_clear();
- return ret;
- default: break;
- }
+ ret = e.src;
+ }
+ else if (type == HintTypes.HINT_T_URL)
+ {
+ ret = e.hasAttribute("href") ? e.href : e.src;
}
- if ((tagname && (tagname == "input" || tagname == "textarea")))
+ else if ((tagname && (tagname == "input" || tagname == "textarea")))
{
if (type == "radio" || type == "checkbox")
{
diff --git a/scripts/lib/dwb.js b/scripts/lib/dwb.js
index 625511d7..b779bad9 100644
--- a/scripts/lib/dwb.js
+++ b/scripts/lib/dwb.js
@@ -504,6 +504,32 @@
{
return object[key + id];
}.bind(self, id)
+ },
+ /**
+ * Includes a script, same as {@link include} but
+ * the path must be relative to the including
+ * script's path.
+ *
+ * @name include
+ * @memberOf script
+ * @function
+ *
+ * @param {String} relPath
+ * The relative path of the script
+ * @param {Boolean} global
+ * Whether to inject the script into the global
+ * scope
+ *
+ * @returns {Object}
+ * The object returned from the included script
+ * */
+ "include" :
+ {
+ value : function(relPath, global)
+ {
+ var dirName = path.substring(0, path.lastIndexOf("/") + 1);
+ return include(dirName + relPath, global);
+ }
}
});
Object.preventExtensions(self);
@@ -886,4 +912,5 @@
});
}
})();
+
Object.preventExtensions(this);
diff --git a/scripts/lib/extensions.js b/scripts/lib/extensions.js
index 0edab6ec..07699714 100644
--- a/scripts/lib/extensions.js
+++ b/scripts/lib/extensions.js
@@ -97,17 +97,11 @@
var getPlugin = function(name, filename)
{
- var ret = null;
- try
- {
- if (system.fileTest(filename, FileTest.exists))
- ret = include(filename);
- }
- catch(e)
- {
- extensions.error(name, "Error in line " + e.line + " parsing " + filename);
- }
- return ret;
+ if (system.fileTest(filename, FileTest.exists))
+ return include(filename);
+ else if (system.fileTest(filename + ".exar", FileTest.exists))
+ return include(filename + ".exar");
+ return null;
};
var getStack = function(offset)
{
@@ -128,7 +122,7 @@
{
if (_registered[name] !== undefined)
{
- if (_registered[name].end instanceof Function)
+ if (typeof _registered[name].end == "function")
{
_registered[name].end();
extensions.message(name, "Extension unloaded.");
@@ -299,6 +293,11 @@
return;
}
}
+ if (plugin === undefined || typeof plugin.init != "function")
+ {
+ extensions.warning(name, "Missing initializer");
+ return;
+ }
try
{
plugin._name = name;
diff --git a/scripts/lib/net.js b/scripts/lib/net.js
new file mode 100644
index 00000000..b2c6cfa8
--- /dev/null
+++ b/scripts/lib/net.js
@@ -0,0 +1,79 @@
+(function() {
+ var tldEnd = new RegExp("\\.tld$");
+ Object.defineProperties(net, {
+ /**
+ * Checks if two domain matches checking for .tld which matches all top level
+ * domains
+ * @name domainMatch
+ * @memberOf net
+ * @function
+ *
+ * @param {String} domain
+ * The domain to test
+ * @param {String} match
+ * A domain that may contain .tld as top level domain
+ *
+ * @returns {Boolean}
+ * Whether the domains match
+ *
+ * @example
+ * net.domainMatch("example.com", "example.tld"); // true
+ * net.domainMatch("example.com", "example.org"); // false
+ * */
+ "domainMatch" :
+ {
+ value : function(domain, match) {
+ var result = false;
+ if (tldEnd.test(match))
+ {
+ return domain.substring(0, domain.indexOf(".")) === match.substring(0, match.indexOf("."));
+ }
+ else
+ {
+ return domain === match;
+ }
+ }
+ },
+ /**
+ * Checks if hostnames match checking for .tld which matches all top level
+ * domains
+ * @name hostMatch
+ * @memberOf net
+ * @function
+ *
+ * @param {String} domain
+ * The host to test
+ * @param {String} match
+ * A host name that may contain .tld as top level domain
+ *
+ * @returns {Boolean}
+ * Whether the hosts match
+ * @example
+ * net.hostMatch("www.example.com", "www.example.tld"); // true
+ * net.hostMatch("www.example.com", "example.tld"); // false
+ * net.hostMatch("www.example.com", "www.example.com"); // false
+ * */
+ "hostMatch" :
+ {
+ value : function(host, match) {
+ if (tldEnd.test(match))
+ {
+ var domain = net.domainFromHost(host);
+ if (domain == host)
+ return net.domainMatch(host, match);
+
+ var domainStart = host.indexOf(domain);
+ if (host.substring(0, domainStart) != match.substring(0, domainStart))
+ return false;
+
+ return net.domainMatch(host.indexOf(domainStart), match.indexOf(domainStart));
+ }
+ else
+ {
+ return host == match;
+ }
+ }
+ },
+ });
+ Object.freeze(net);
+})();
diff --git a/scripts/lib/system.js b/scripts/lib/system.js
new file mode 100644
index 00000000..1a276f8b
--- /dev/null
+++ b/scripts/lib/system.js
@@ -0,0 +1,27 @@
+(function() {
+ Object.defineProperties(system, {
+ "spawn" :
+ {
+ value : (function() {
+ return function(command, onStdout, onStderr, stdin, environ) {
+ var stdout, stderr;
+ return system._spawn(command,
+ !onStdout ? null : function(response) {
+ var ret;
+ stdout = onStdout.call(onStdout, response) || response;
+ },
+ !onStderr ? null : function(response) {
+ var ret;
+ stderr = onStderr.call(onStderr, response) || response;
+ return ret;
+ },
+ stdin, environ).then(
+ function() { return stdout; },
+ function() { return stdin; }
+ );
+ };
+ })()
+ }
+ });
+ Object.freeze(system);
+})();
diff --git a/src/Makefile b/src/Makefile
index a1a3f287..d6c6bb0f 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -3,21 +3,27 @@
BASEDIR=..
include $(BASEDIR)/config.mk
+include config.mk
+CFLAGS+=-I$(BASEDIR)/$(DWB_LIB_DIRS)
-DEPS=$(patsubst %.o, %.d, $(OBJ))
+DEPS=$(patsubst %.o, %.d, $(OBJ))
+
+OBJ += $(BASEDIR)/$(OBJLIB)
+
+DOBJ += $(BASEDIR)/exar/exar.do
all: $(TARGET)
$(TARGET): $(OBJ)
- @echo "$(CC) $@"
+ @echo $(CC) -o $@
@$(CC) $(OBJ) -o $(TARGET) $(LDFLAGS)
-include $(OBJ:.o=.d)
-include $(DOBJ:.do=.dd)
%.o: %.c %.h config.h dwb.h
- @echo "${CC} $<"
+ @echo $(CC) $<
@$(CC) -c -o $@ $< $(CFLAGS) $(CPPFLAGS)
debug: $(DTARGET)
diff --git a/src/adblock.c b/src/adblock.c
index 4bdf86bd..4ffd0270 100644
--- a/src/adblock.c
+++ b/src/adblock.c
@@ -906,29 +906,52 @@ error_out:
void
adblock_end()
{
+ if (!s_init)
+ return;
for (GSList *l = s_css_hider_list; l; l=l->next)
g_free(l->data);
g_slist_free(s_css_hider_list);
+ s_css_hider_list = NULL;
+
if (s_css_exceptions != NULL)
+ {
g_string_free(s_css_exceptions, true);
+ s_css_exceptions = NULL;
+ }
if (s_rules != NULL)
+ {
g_ptr_array_free(s_rules, true);
+ s_rules = NULL;
+ }
if (s_simple_rules != NULL)
+ {
g_ptr_array_free(s_simple_rules, true);
+ s_simple_rules = NULL;
+ }
if (s_simple_exceptions != NULL)
+ {
g_ptr_array_free(s_simple_exceptions, true);
+ s_simple_exceptions = NULL;
+ }
if(s_exceptions != NULL)
+ {
g_ptr_array_free(s_exceptions, true);
+ s_exceptions = NULL;
+ }
if (s_hider_rules != NULL)
+ {
g_hash_table_remove_all(s_hider_rules);
+ }
if (s_hider_list != NULL)
{
for (GSList *l = s_hider_list; l; l=l->next)
adblock_element_hider_free((AdblockElementHider*)l->data);
g_slist_free(s_hider_list);
+ s_hider_list = NULL;
}
+ s_init = false;
}/*}}}*/
/* adblock_init() {{{*/
@@ -965,3 +988,14 @@ adblock_init()
return true;
}/*}}}*//*}}}*/
+gboolean
+adblock_reload()
+{
+ for (GList *gl = dwb.state.views; gl; gl=gl->next)
+ adblock_disconnect(gl);
+ adblock_end();
+ gboolean ret = adblock_init();
+ for (GList *gl = dwb.state.views; gl; gl=gl->next)
+ adblock_connect(gl);
+ return ret;
+}
diff --git a/src/adblock.h b/src/adblock.h
index 956ac112..462d9707 100644
--- a/src/adblock.h
+++ b/src/adblock.h
@@ -16,13 +16,14 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef ADBLOCK_H
-#define ADBLOCK_H
+#ifndef __DWB_ADBLOCK_H__
+#define __DWB_ADBLOCK_H__
-gboolean adblock_init();
-gboolean adblock_running();
-void adblock_end();
+gboolean adblock_init(void);
+gboolean adblock_running(void);
+void adblock_end(void);
+gboolean adblock_reload(void);
void adblock_connect(GList *gl);
void adblock_disconnect(GList *gl);
-#endif // ADBLOCK_H
+#endif // __DWB_ADBLOCK_H__
diff --git a/src/application.c b/src/application.c
index cba6ab5b..a8f8a524 100644
--- a/src/application.c
+++ b/src/application.c
@@ -40,6 +40,7 @@ static gboolean s_opt_override_restore = false;
static gboolean s_opt_version = false;
static gboolean s_opt_force = false;
static gboolean s_opt_enable_scripts = false;
+static gchar **s_opt_delete_profile = NULL;
static gchar *s_opt_restore = NULL;
static gchar **s_opt_exe = NULL;
static gchar **s_scripts;
@@ -56,6 +57,7 @@ static GOptionEntry options[] = {
{ "execute", 'x', 0, G_OPTION_ARG_STRING_ARRAY, &s_opt_exe, "Execute commands", NULL},
{ "version", 'v', 0, G_OPTION_ARG_NONE, &s_opt_version, "Show version information and exit", NULL},
{ "enable-scripts", 'S', 0, G_OPTION_ARG_NONE, &s_opt_enable_scripts, "Enable javascript api", NULL},
+ { "delete-profile", 'd', 0, G_OPTION_ARG_STRING_ARRAY, &s_opt_delete_profile, "Deletes a profile", NULL},
{ "set-as-default", 0, G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, &application_parse_option, "Sets dwb as default browser", NULL},
{ NULL, 0, 0, 0, NULL, NULL, NULL }
};
@@ -159,13 +161,6 @@ dwb_application_local_command_line(GApplication *app, gchar ***argv, gint *exit_
char *appid;
GDBusConnection *bus;
- //GDesktopAppInfo *info = g_desktop_app_info_new("dwb.desktop");
- //g_app_info_set_as_default_for_type(G_APP_INFO(info), "text/html", NULL);
- //g_app_info_set_as_default_for_type(G_APP_INFO(info), "text/xml", NULL);
- //g_app_info_set_as_default_for_type(G_APP_INFO(info), "application/xhtml+xml", NULL);
- //g_app_info_set_as_default_for_type(G_APP_INFO(info), "x-scheme-handler/http", NULL);
- //g_app_info_set_as_default_for_type(G_APP_INFO(info), "x-scheme-handler/https", NULL);
-
GOptionContext *c = application_get_option_context();
if (!g_option_context_parse(c, &argc, argv, &error))
{
@@ -182,6 +177,17 @@ dwb_application_local_command_line(GApplication *app, gchar ***argv, gint *exit_
if (s_opt_exe != NULL)
argc_exe = g_strv_length(s_opt_exe);
+ if (s_opt_delete_profile != NULL)
+ {
+ for (int i=0; s_opt_delete_profile[i]; i++)
+ {
+ if (!dwb_delete_profile(s_opt_delete_profile[i]))
+ {
+ fprintf(stderr, "Deleting profile \"%s\" failed\n", s_opt_delete_profile[i]);
+ }
+ }
+ return true;
+ }
if (s_opt_list_sessions)
{
session_list();
diff --git a/src/application.h b/src/application.h
index 7b5d128f..14dfffb5 100644
--- a/src/application.h
+++ b/src/application.h
@@ -16,8 +16,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef APPLICATION_H
-#define APPLICATION_H
+#ifndef __DWB_APPLICATION_H__
+#define __DWB_APPLICATION_H__
gint application_run(int, char **);
void application_stop(void);
diff --git a/src/callback.c b/src/callback.c
index 05916830..30b2b707 100644
--- a/src/callback.c
+++ b/src/callback.c
@@ -88,7 +88,10 @@ callback_entry_key_press(GtkWidget* entry, GdkEventKey *e)
return true;
}
else if (mode & COMPLETE_SCRIPTS && !DWB_COMPLETE_KEY(e))
- return dwb.state.script_comp_readonly;
+ if (dwb.state.script_comp_readonly)
+ return true;
+ else
+ goto skip;
else if (e->keyval == GDK_KEY_BackSpace && !complete)
return false;
else if (mode == HINT_MODE)
diff --git a/src/callback.h b/src/callback.h
index 4cb8d0d2..a68123ce 100644
--- a/src/callback.h
+++ b/src/callback.h
@@ -16,8 +16,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef CALLBACKS_H
-#define CALLBACKS_H
+#ifndef __DWB_CALLBACK_H__
+#define __DWB_CALLBACK_H__
gboolean callback_entry_key_release(GtkWidget *, GdkEventKey *);
gboolean callback_entry_key_press(GtkWidget *, GdkEventKey *);
diff --git a/src/commands.c b/src/commands.c
index 1e01d593..2676420d 100644
--- a/src/commands.c
+++ b/src/commands.c
@@ -336,8 +336,7 @@ commands_zoom(KeyMap *km, Arg *arg)
DwbStatus
commands_scroll(KeyMap *km, Arg *arg)
{
- GList *gl = arg->p ? arg->p : dwb.state.fview;
- dwb_scroll(gl, dwb.misc.scroll_step, arg->n);
+ dwb_scroll(dwb.state.fview, dwb.misc.scroll_step, arg->n);
return STATUS_OK;
}/*}}}*/
@@ -345,10 +344,9 @@ commands_scroll(KeyMap *km, Arg *arg)
DwbStatus
commands_set_zoom_level(KeyMap *km, Arg *arg)
{
- GList *gl = arg->p ? arg->p : dwb.state.fview;
double zoomlevel = dwb.state.nummod < 0 ? arg->d : (double)dwb.state.nummod / 100;
- webkit_web_view_set_zoom_level(WEBKIT_WEB_VIEW(((View*)gl->data)->web), zoomlevel);
+ webkit_web_view_set_zoom_level(CURRENT_WEBVIEW(), zoomlevel);
dwb_set_normal_message(dwb.state.fview, true, "Zoomlevel: %d%%", (int)(zoomlevel * 100));
return STATUS_OK;
}/*}}}*/
@@ -649,7 +647,9 @@ commands_undo(KeyMap *km, Arg *arg)
{
if (dwb.state.undo_list)
{
- WebKitWebView *web = WEBVIEW(view_add(NULL, false));
+
+ GList *gl = view_add(NULL, false);
+ WebKitWebView *web = WEBVIEW(gl);
WebKitWebBackForwardList *bflist = webkit_web_back_forward_list_new_with_web_view(web);
for (GList *l = dwb.state.undo_list->data; l; l=l->next)
@@ -657,9 +657,10 @@ commands_undo(KeyMap *km, Arg *arg)
Navigation *n = l->data;
WebKitWebHistoryItem *item = webkit_web_history_item_new_with_data(n->first, n->second);
- webkit_web_back_forward_list_add_item(bflist, item);
if (!l->next)
- webkit_web_view_go_to_back_forward_item(web, item);
+ dwb_load_uri(gl, n->first);
+ else
+ webkit_web_back_forward_list_add_item(bflist, item);
dwb_navigation_free(n);
}
@@ -1077,3 +1078,10 @@ commands_tabdo(KeyMap *km, Arg *arg)
dwb_focus_view(current, "tabdo");
return STATUS_OK;
}
+DwbStatus
+commands_adblock_reload_rules(KeyMap *km, Arg *arg)
+{
+ adblock_reload();
+ dwb_set_normal_message(dwb.state.fview, true, "Adblock rules reloaded");
+ return STATUS_OK;
+}
diff --git a/src/commands.h b/src/commands.h
index 62f9b1ae..a4fece94 100644
--- a/src/commands.h
+++ b/src/commands.h
@@ -16,8 +16,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef COMMANDS_H
-#define COMMANDS_H
+#ifndef __DWB_COMMANDS_H__
+#define __DWB_COMMANDS_H__
DwbStatus commands_simple_command(KeyMap *km, const char *);
@@ -96,5 +96,6 @@ DwbStatus commands_reload_bookmarks(KeyMap *, Arg *);
DwbStatus commands_reload_quickmarks(KeyMap *, Arg *);
DwbStatus commands_print_preview(KeyMap *, Arg *);
DwbStatus commands_tabdo(KeyMap *, Arg *);
+DwbStatus commands_adblock_reload_rules(KeyMap *, Arg *);
#endif
diff --git a/src/completion.h b/src/completion.h
index 9cd1dc8f..4a2e43c1 100644
--- a/src/completion.h
+++ b/src/completion.h
@@ -16,8 +16,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef COMPLETION_H
-#define COMPLETION_H
+#ifndef __DWB_COMPLETION_H__
+#define __DWB_COMPLETION_H__
#define COMP_EVENT_BOX(X) (((Completion*)((X)->data))->event)
diff --git a/src/config.h b/src/config.h
index 51b4e98d..cba31ea0 100644
--- a/src/config.h
+++ b/src/config.h
@@ -189,6 +189,7 @@ static KeyValue KEYS[] = {
{ "reload_bookmarks", { NULL, 0, 0 }, },
{ "reload_quickmarks", { NULL, 0, 0 }, },
{ "print_preview", { NULL, 0, 0 }, },
+ { "adblock_reload_rules", { NULL, 0, 0 }, },
};
/* FUNCTION_MAP{{{*/
@@ -206,6 +207,11 @@ static FunctionMap FMAP [] = {
{ { "reload_bookmarks", "Reload bookmarks", }, CP_COMMANDLINE,
(Func)commands_reload_bookmarks, NULL, ALWAYS_SM,
{ .p = NULL }, EP_NONE, { NULL }, },
+
+ { { "adblock_reload_rules", "Reload adblock rulse", }, CP_COMMANDLINE,
+ (Func)commands_adblock_reload_rules, NULL, POST_SM,
+ { .p = NULL }, EP_NONE, { NULL }, },
+
{ { "toggle_tab", "Toggle between last and current tab", }, CP_COMMANDLINE,
(Func)commands_toggle_tab, NULL, ALWAYS_SM,
{ .p = NULL }, EP_NONE, { "ttab" }, },
diff --git a/src/config.mk b/src/config.mk
new file mode 100644
index 00000000..bb4706af
--- /dev/null
+++ b/src/config.mk
@@ -0,0 +1,3 @@
+ifeq ($(shell $(BASEDIR)/$(TOOLDIR)/check_header.sh execinfo.h $(CC)), 1)
+CFLAGS += -DHAS_EXECINFO
+endif
diff --git a/src/dom.h b/src/dom.h
index 697b80f4..b495f312 100644
--- a/src/dom.h
+++ b/src/dom.h
@@ -16,8 +16,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef DOM_H
-#define DOM_H
+#ifndef __DWB_DOM_H__
+#define __DWB_DOM_H__
char * dom_node_get_attribute(WebKitDOMNode *node, const char *attribute);
gboolean dom_add_frame_listener(WebKitWebFrame *frame, const char *signal, GCallback callback, gboolean bubble, GList *gl);
diff --git a/src/domain.h b/src/domain.h
index 8203db78..0ff458b7 100644
--- a/src/domain.h
+++ b/src/domain.h
@@ -18,8 +18,8 @@
*/
-#ifndef DOMAIN_H
-#define DOMAIN_H
+#ifndef __DWB_DOMAIN_H__
+#define __DWB_DOMAIN_H__
#define SUBDOMAIN_MAX 32
diff --git a/src/download.h b/src/download.h
index d171c475..b640a407 100644
--- a/src/download.h
+++ b/src/download.h
@@ -16,8 +16,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef DOWNLOAD_H
-#define DOWNLOAD_H
+#ifndef __DWB_DOWNLOAD_H__
+#define __DWB_DOWNLOAD_H__
void download_get_path(GList *, WebKitDownload *);
diff --git a/src/dwb.c b/src/dwb.c
index fce36fca..5ed33223 100644
--- a/src/dwb.c
+++ b/src/dwb.c
@@ -28,6 +28,8 @@
#ifdef HAS_EXECINFO
#include <execinfo.h>
#endif
+
+#include <exar.h>
#include "dwb.h"
#include "soup.h"
#include "completion.h"
@@ -2159,11 +2161,13 @@ dwb_update_hints(GdkEventKey *e)
char *val;
gboolean ret = false;
char json[BUFFER_LENGTH] = {0};
+ char *escaped;
if (IS_RETURN_KEY(e))
{
com = "followActive";
snprintf(json, sizeof(json), "{ \"type\" : \"%d\" }", hint_map[dwb.state.hint_type].arg);
+ ret = true;
}
else if (DWB_COMPLETE_KEY(e))
{
@@ -2180,16 +2184,18 @@ dwb_update_hints(GdkEventKey *e)
else
{
val = util_keyval_to_char(e->keyval, true);
- snprintf(json, sizeof(json), "{ \"input\" : \"%s%s\", \"type\" : %d }", GET_TEXT(), val ? val : "", hint_map[dwb.state.hint_type].arg);
+ escaped = g_strescape(GET_TEXT(), NULL);
+ snprintf(json, sizeof(json), "{ \"input\" : \"%s%s\", \"type\" : %d }", escaped, val ? (*val == '\\' ? "\\\\" : val) : "", hint_map[dwb.state.hint_type].arg);
com = "updateHints";
g_free(val);
+ g_free(escaped);
}
if (com)
{
buffer = js_call_as_function(MAIN_FRAME(), CURRENT_VIEW()->js_base, com, *json ? json : NULL, *json ? kJSTypeObject : kJSTypeUndefined, &buffer);
}
- if (buffer != NULL) {
-
+ if (buffer != NULL)
+ {
if (dwb_evaluate_hints(buffer) == STATUS_END)
ret = true;
g_free(buffer);
@@ -2962,7 +2968,7 @@ dwb_eval_key(GdkEventKey *e)
if (key == NULL)
return false;
- if (DIGIT(e))
+ if (dwb.state.buffer->len == 0 && DIGIT(e))
{
keynum = e->keyval - GDK_KEY_0;
if (dwb.state.nummod >= 0)
@@ -3088,7 +3094,7 @@ dwb_eval_override_key(GdkEventKey *e, CommandProperty prop)
if (gtk_widget_has_focus(dwb.gui.entry) && e->keyval == GDK_KEY_BackSpace)
entry_clear_history();
- if (CLEAN_STATE(e) && (key = dwb_get_key(e, &mod, &isprint)) != NULL)
+ if ((key = dwb_get_key(e, &mod, &isprint)) != NULL)
{
for (GList *l = dwb.override_keys; l; l=l->next)
{
@@ -3209,7 +3215,7 @@ dwb_change_mode(Mode mode, ...)
* */
char buffer[] = { BASIC_MODES(mode) + 48, 0 };
- ScriptSignal sig = { SCRIPTS_WV(dwb.state.fview), SCRIPTS_SIG_META(buffer, CHANGE_MODE, 0) };
+ ScriptSignal sig = { .jsobj = (dwb.state.fview ? CURRENT_VIEW()->script_wv : NULL), SCRIPTS_SIG_META(buffer, CHANGE_MODE, 0) };
if (scripts_emit(&sig))
return STATUS_OK;
}
@@ -3407,7 +3413,7 @@ dwb_get_scripts()
GList *gl = NULL;
Navigation *n;
GError *error = NULL;
- FILE *f;
+ FILE *f = NULL;
int l1, l2;
if ( (dir = g_dir_open(dwb.files[FILES_USERSCRIPTS], 0, NULL)) )
@@ -3437,18 +3443,27 @@ dwb_get_scripts()
path = realpath;
}
}
- if (dwb.misc.js_api != JS_API_DISABLED && (f = fopen(path, "r")) != NULL && (l1 = fgetc(f)) && (l2 = fgetc(f)) )
+ if (dwb.misc.js_api != JS_API_DISABLED)
{
- if ( (l1 == '#' && l2 == '!') || (l1 == '/' && l2 == '/' && fgetc(f) == '!') )
+ if (exar_check_version(path) == 0)
+ {
+ content = (char *) exar_search_extract(path, "main.js", NULL);
+ if (content != NULL)
+ scripts_init_archive(path, content);
+ goto loop_end;
+ }
+ else if ( (f = fopen(path, "r")) != NULL)
{
- if (fgets(buf, sizeof(buf), f) != NULL && !g_strcmp0(buf, "javascript"))
+ if ( ( (l1 = fgetc(f)) && (l2 = fgetc(f)) ) &&
+ ( (l1 == '#' && l2 == '!') || (l1 == '/' && l2 == '/' && fgetc(f) == '!') ) &&
+ (fgets(buf, sizeof(buf), f) != NULL && !g_strcmp0(buf, "javascript")) )
{
int next = fgetc(f);
if (g_ascii_isspace(next))
javascript = true;
}
+ fclose(f);
}
- fclose(f);
}
@@ -3614,7 +3629,7 @@ dwb_clean_up()
KeyMap *m = l->data;
if (m->map->prop & CP_SCRIPT)
{
- scripts_unbind(m->map->arg.p);
+ scripts_unprotect(m->map->arg.p);
g_free(m->map->n.first);
g_free(m->map->n.second);
}
@@ -3902,6 +3917,7 @@ dwb_str_to_key(char *str)
if ((escape = strchr(start, '\\')))
{
keybuffer = g_string_new(NULL);
+ g_string_append_len(keybuffer, start, escape - start);
do
{
if (*(escape + 1) == '\\')
@@ -4902,27 +4918,27 @@ dwb_parse_command_line(const char *line)
return STATUS_OK;
DwbStatus ret = STATUS_OK;
- const char *bak;
- int nummod;
- line = util_str_chug(line);
- bak = dwb_parse_nummod(line);
- nummod = dwb.state.nummod;
- char **token = g_strsplit(bak, " ", 2);
+ const char *bak = NULL;
+ char **token = NULL;
KeyMap *m = NULL;
- gboolean found;
+ gboolean found = false;
gboolean has_arg = false;
const char *argument = NULL;
+ line = util_str_chug(line);
+ bak = dwb_parse_nummod(line);
+ token = g_strsplit(bak, " ", 2);
+
if (!token[0])
return STATUS_OK;
+ bak = token[0];
+
if (token[1])
has_arg = true;
for (GList *l = dwb.keymap; l; l=l->next)
{
- bak = token[0];
- found = false;
m = l->data;
if (!g_strcmp0(m->map->n.first, bak))
found = true;
@@ -4942,7 +4958,6 @@ dwb_parse_command_line(const char *line)
if (m->map->prop & CP_HAS_MODE)
dwb_change_mode(NORMAL_MODE, true);
- dwb.state.nummod = nummod;
if (token[1] && ! m->map->arg.ro)
{
g_strstrip(token[1]);
@@ -4987,6 +5002,34 @@ dwb_parse_commands(const char *line)
}
/*}}}*/
+
+static gboolean
+dwb_remove_key_group(GKeyFile *kf, const char *group)
+{
+ return g_key_file_remove_group(kf, group, NULL);
+}
+gboolean
+dwb_delete_profile(const char *profile)
+{
+ gboolean success = false;
+ char *path = util_build_path();
+
+ char *filename = g_build_filename(path, "keys", NULL);
+ success = util_keyfile_do(filename, (KeyFileAction)dwb_remove_key_group, profile) || success;
+ g_free(filename);
+
+ filename = g_build_filename(path, "settings", NULL);
+ success = util_keyfile_do(filename, (KeyFileAction)dwb_remove_key_group, profile) || success;
+ g_free(filename);
+
+ filename = g_build_filename(path, profile, NULL);
+ success = util_rmdir(filename, false, true) || success;
+ g_free(filename);
+
+ g_free(path);
+ return success;
+}
+
void
dwb_version()
{
diff --git a/src/dwb.h b/src/dwb.h
index c36d55a1..471c1edc 100644
--- a/src/dwb.h
+++ b/src/dwb.h
@@ -16,8 +16,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef DWB_H
-#define DWB_H
+#ifndef __DWB_DWB_H__
+#define __DWB_DWB_H__
#include <gtk/gtk.h>
#include <webkit/webkit.h>
#include <gdk/gdkkeysyms.h>
@@ -1014,6 +1014,7 @@ DwbStatus dwb_pack(const char *layout, gboolean rebuild);
void dwb_init_signals(void);
void dwb_init_vars(void);
void dwb_parse_commands(const char *line);
+gboolean dwb_delete_profile(const gchar *);
DwbStatus dwb_scheme_handler(GList *gl, WebKitNetworkRequest *request);
GList *dwb_get_simple_list(GList *, const char *filename);
char * dwb_prompt(gboolean visibility, char *prompt, ...);
diff --git a/src/editor.h b/src/editor.h
index 634cdeb3..cd28e6ff 100644
--- a/src/editor.h
+++ b/src/editor.h
@@ -16,8 +16,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef EDITOR_H
-#define EDITOR_H
+#ifndef __DWB_EDITOR_H__
+#define __DWB_EDITOR_H__
DwbStatus editor_open(void);
diff --git a/src/entry.h b/src/entry.h
index 1e5c6197..8da60e99 100644
--- a/src/entry.h
+++ b/src/entry.h
@@ -16,8 +16,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef ENTRY_H
-#define ENTRY_H
+#ifndef __DWB_ENTRY_H__
+#define __DWB_ENTRY_H__
DwbStatus entry_history_forward(GList **last);
DwbStatus entry_history_back(GList **list, GList **last);
void entry_focus();
diff --git a/src/hsts.h b/src/hsts.h
index 504570fc..17f14117 100644
--- a/src/hsts.h
+++ b/src/hsts.h
@@ -17,8 +17,8 @@
*/
#ifndef DISABLE_HSTS
-#ifndef HSTS_H
-#define HSTS_H
+#ifndef __DWB_HSTS_H__
+#define __DWB_HSTS_H__
gboolean hsts_running();
gboolean hsts_init();
@@ -27,5 +27,5 @@ void hsts_save();
void hsts_activate();
void hsts_deactivate();
-#endif // HSTS_H
+#endif // __DWB_HSTS_H__
#endif
diff --git a/src/html.h b/src/html.h
index 1269dbeb..25da6895 100644
--- a/src/html.h
+++ b/src/html.h
@@ -16,8 +16,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef HTML_H
-#define HTML_H
+#ifndef __DWB_HTML_H__
+#define __DWB_HTML_H__
gboolean html_load(GList *, const char *);
diff --git a/src/js.c b/src/js.c
index e391abb4..eb0a118c 100644
--- a/src/js.c
+++ b/src/js.c
@@ -49,6 +49,14 @@ js_set_object_property(JSContextRef ctx, JSObjectRef arg, const char *name, cons
JSObjectSetProperty(ctx, arg, js_key, js_value, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly, exc);
JSStringRelease(js_key);
}
+gboolean
+js_object_has_property(JSContextRef ctx, JSObjectRef arg, const char *name)
+{
+ JSStringRef js_key = JSStringCreateWithUTF8CString(name);
+ gboolean result = JSObjectHasProperty(ctx, arg, js_key);
+ JSStringRelease(js_key);
+ return result;
+}
void
js_set_object_number_property(JSContextRef ctx, JSObjectRef arg, const char *name, gdouble value, JSValueRef *exc)
{
diff --git a/src/js.h b/src/js.h
index 5fc5f201..50366c70 100644
--- a/src/js.h
+++ b/src/js.h
@@ -16,8 +16,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef JS_H
-#define JS_H
+#ifndef __DWB_JS_H__
+#define __DWB_JS_H__
#include <JavaScriptCore/JavaScript.h>
typedef struct js_iterator_s js_array_iterator;
@@ -58,6 +58,7 @@ JSValueRef js_json_to_value(JSContextRef ctx, const char *text);
JSValueRef js_context_change(JSContextRef, JSContextRef, JSValueRef, JSValueRef *);
JSObjectRef js_value_to_function(JSContextRef, JSValueRef, JSValueRef *);
gboolean js_check_syntax(JSContextRef ctx, const char *script, const char *filename, int lineOffset);
+gboolean js_object_has_property(JSContextRef ctx, JSObjectRef arg, const char *name);
void js_array_iterator_init(JSContextRef ctx, js_array_iterator *iter, JSObjectRef object);
JSValueRef js_array_iterator_next(js_array_iterator *iter, JSValueRef *exc);
diff --git a/src/local.h b/src/local.h
index 52f68b0c..2a1395ef 100644
--- a/src/local.h
+++ b/src/local.h
@@ -16,8 +16,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef LOCAL_H
-#define LOCAL_H
+#ifndef __DWB_LOCAL_H__
+#define __DWB_LOCAL_H__
gboolean
local_check_directory(GList *, const char *, gboolean add_to_history, GError **);
diff --git a/src/plugins.h b/src/plugins.h
index 4c4c2597..4abad9d5 100644
--- a/src/plugins.h
+++ b/src/plugins.h
@@ -16,8 +16,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef PLUGINS_H
-#define PLUGINS_H
+#ifndef __DWB_PLUGINS_H__
+#define __DWB_PLUGINS_H__
void plugins_connect(GList *);
void plugins_disconnect(GList *);
diff --git a/src/scripts.c b/src/scripts.c
index d5d1d69e..2ce29c7a 100644
--- a/src/scripts.c
+++ b/src/scripts.c
@@ -20,6 +20,7 @@
#define _POSIX_C_SOURCE 200112L
#include <stdio.h>
+#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
@@ -27,6 +28,8 @@
#include <errno.h>
#include <JavaScriptCore/JavaScript.h>
#include <glib.h>
+#include <cairo.h>
+#include <exar.h>
#include "dwb.h"
#include "scripts.h"
#include "session.h"
@@ -37,16 +40,26 @@
#include "application.h"
#include "completion.h"
#include "entry.h"
+
+#define API_VERSION 1
+
//#define kJSDefaultFunction (kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete )
#define kJSDefaultProperty (kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly )
#define kJSDefaultAttributes (kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly )
-#define SCRIPT_TEMPLATE_START "try{_initNewContext(this, arguments, '%s');const script=this;/*<dwb*/"
+#define SCRIPT_TEMPLATE_START "try{_initNewContext(this,arguments,'%s');const script=this;/*<dwb*/"
+#define SCRIPT_TEMPLATE_XSTART "try{"\
+"var exports=arguments[0];"\
+"_initNewContext(this,arguments,'%s');"\
+"var xinclude=_xinclude.bind(this,this.path);"\
+"const script=this;"\
+"if(!exports.id)Object.defineProperty(exports,'id',{value:script.generateId()});/*<dwb*/"
#define SCRIPT_TEMPLATE_END "%s/*dwb>*/}catch(e){script.debug(e);};"
#define SCRIPT_TEMPLATE SCRIPT_TEMPLATE_START"//!javascript\n"SCRIPT_TEMPLATE_END
#define SCRIPT_TEMPLATE_INCLUDE SCRIPT_TEMPLATE_START SCRIPT_TEMPLATE_END
+#define SCRIPT_TEMPLATE_XINCLUDE SCRIPT_TEMPLATE_XSTART SCRIPT_TEMPLATE_END
#define SCRIPT_WEBVIEW(o) (WEBVIEW(((GList*)JSObjectGetPrivate(o))))
#define EXCEPTION(X) "DWB EXCEPTION : "X
@@ -91,10 +104,6 @@ typedef struct DeferredPriv_s
JSObjectRef next;
} DeferredPriv;
-typedef struct _SpawnChannel {
- JSObjectRef callback;
- int infd;
-} SpawnChannel;
//static GSList *s_signals;
#define S_SIGNAL(X) ((SSignal*)X->data)
@@ -126,6 +135,7 @@ static Sigmap s_sigmap[] = {
{ SCRIPTS_SIG_CHANGE_MODE, "changeMode" },
{ SCRIPTS_SIG_EXECUTE_COMMAND, "executeCommand" },
{ SCRIPTS_SIG_CONTEXT_MENU, "contextMenu" },
+ { SCRIPTS_SIG_ERROR, "error" },
{ 0, NULL },
};
@@ -166,7 +176,6 @@ static JSClassRef s_gobject_class,
s_message_class,
s_deferred_class,
s_history_class;
-static gboolean s_commandline = false;
static JSObjectRef s_array_contructor;
static JSObjectRef s_completion_callback;
static GQuark s_ref_quark;
@@ -176,6 +185,8 @@ static JSObjectRef s_soup_session;
static GSList *s_timers = NULL;
static GPtrArray *s_gobject_signals = NULL;
static gboolean s_debugging = false;
+static GHashTable *s_exports = NULL;
+
/* Only defined once */
static JSValueRef UNDEFINED, NIL;
@@ -354,25 +365,6 @@ inject(JSContextRef ctx, JSContextRef wctx, JSObjectRef function, JSObjectRef th
}/*}}}*/
/*}}}*/
-SpawnChannel *
-spawn_channel_new(JSObjectRef callback, int infd)
-{
- SpawnChannel *channel = g_malloc(sizeof(SpawnChannel));
- channel->callback = callback;
- JSValueProtect(s_global_context, callback);
- if (infd >= 0)
- channel->infd = dup(infd);
-
- return channel;
-}
-void
-spawn_channel_close(SpawnChannel *channel)
-{
- JSValueUnprotect(s_global_context, channel->callback);
- if (channel->infd >= 0)
- close(channel->infd);
- g_free(channel);
-}
/* Deferred {{{*/
void
deferred_destroy(JSContextRef ctx, JSObjectRef this, DeferredPriv *priv)
@@ -513,7 +505,16 @@ deferred_resolve(JSContextRef ctx, JSObjectRef f, JSObjectRef this, size_t argc,
deferred_transition(ctx, next, o)->reject = NULL;
}
else
- deferred_resolve(ctx, f, next, argc, argv, exc);
+ {
+ if (ret && !JSValueIsUndefined(ctx, ret))
+ {
+ JSValueRef args[] = { ret };
+ deferred_resolve(ctx, f, next, 1, args, exc);
+ }
+ else
+ deferred_resolve(ctx, f, next, argc, argv, exc);
+
+ }
}
return UNDEFINED;
}
@@ -549,7 +550,15 @@ deferred_reject(JSContextRef ctx, JSObjectRef f, JSObjectRef this, size_t argc,
deferred_transition(ctx, next, o)->resolve = NULL;
}
else
- deferred_reject(ctx, f, next, argc, argv, exc);
+ {
+ if (ret && !JSValueIsUndefined(ctx, ret))
+ {
+ JSValueRef args[] = { ret };
+ deferred_resolve(ctx, f, next, 1, args, exc);
+ }
+ else
+ deferred_resolve(ctx, f, next, argc, argv, exc);
+ }
}
return UNDEFINED;
}/*}}}*/
@@ -1446,6 +1455,10 @@ frame_inject(JSContextRef ctx, JSObjectRef function, JSObjectRef this, size_t ar
* @callback bindCallback
*
* @param {Object} arguments
+ * @param {String} arguments.shortcut
+ * The shortcut that was pressed
+ * @param {Number} arguments.modifier
+ * The modifier
* @param {Number} arguments.nummod
* Numerical modifier that was used or -1 if no modifier was used.
* @param {Number} [arguments.arg]
@@ -1460,21 +1473,22 @@ scripts_eval_key(KeyMap *m, Arg *arg)
if (! (m->map->prop & CP_OVERRIDE))
CLEAR_COMMAND_TEXT();
- if (!TRY_CONTEXT_LOCK)
- return STATUS_ERROR;
-
if (s_global_context != NULL)
{
if (arg->p == NULL)
- json = util_create_json(1, INTEGER, "nummod", dwb.state.nummod);
+ json = util_create_json(3, CHAR, "key", m->key,
+ INTEGER, "modifier", m->mod,
+ INTEGER, "nummod", dwb.state.nummod);
else
- json = util_create_json(2, INTEGER, "nummod", dwb.state.nummod, CHAR, "arg", arg->p);
+ json = util_create_json(4, CHAR, "key", m->key,
+ INTEGER, "modifier", m->mod,
+ INTEGER, "nummod", dwb.state.nummod,
+ CHAR, "arg", arg->p);
JSValueRef argv[] = { js_json_to_value(s_global_context, json) };
call_as_function_debug(s_global_context, arg->js, arg->js, 1, argv);
}
- CONTEXT_UNLOCK;
g_free(json);
return STATUS_OK;
@@ -1631,6 +1645,8 @@ error_out:
/**
* Refers to the global object
* @name global
+ * @type Object
+ * @readonly
*
* */
static JSValueRef
@@ -1643,6 +1659,7 @@ global_get(JSContextRef ctx, JSObjectRef object, JSStringRef js_name, JSValueRef
*
* @name session
* @type SoupSession
+ * @readonly
*
* */
static JSValueRef
@@ -1677,6 +1694,8 @@ global_execute(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, s
status = dwb_parse_command_line(command);
g_free(command);
}
+ if (status == STATUS_END)
+ exit(0);
return JSValueMakeBoolean(ctx, status == STATUS_OK);
}/*}}}*/
@@ -1690,11 +1709,9 @@ global_execute(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, s
static JSValueRef
global_exit(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argc, const JSValueRef argv[], JSValueRef* exc)
{
- if (s_commandline)
- application_stop();
- else
- dwb_end(0);
- return UNDEFINED;
+ dwb_end(0);
+ exit(EXIT_SUCCESS);
+ return 0;
}/*}}}*/
/*{{{*/
@@ -1723,14 +1740,102 @@ settings_get(JSContextRef ctx, JSObjectRef jsobj, JSStringRef js_name, JSValueRe
}
/*}}}*/
+static JSValueRef
+do_include(JSContextRef ctx, const char *path, const char *script, gboolean global, gboolean is_archive, size_t argc, const JSValueRef *argv, JSValueRef *exc)
+{
+ JSStringRef js_script;
+ JSValueRef ret = NIL;
+
+ if (global)
+ {
+ js_script = JSStringCreateWithUTF8CString(script);
+ ret = JSEvaluateScript(ctx, js_script, NULL, NULL, 0, exc);
+ }
+ else
+ {
+ char *debug = g_strdup_printf(is_archive ? SCRIPT_TEMPLATE_XINCLUDE : SCRIPT_TEMPLATE_INCLUDE, path, script);
+ js_script = JSStringCreateWithUTF8CString(debug);
+ JSObjectRef function = JSObjectMakeFunction(ctx, NULL, 0, NULL, js_script, NULL, 1, exc);
+ if (function != NULL)
+ {
+ ret = JSObjectCallAsFunction(ctx, function, function, argc, argv, exc);
+ }
+ g_free(debug);
+ }
+ JSStringRelease(js_script);
+ return ret;
+}
/* global_include {{{*/
+/**
+ * Includes a file.
+ * Note that included files are not visible in other scripts unless they are
+ * explicitly injected into the global scope. To use functions or variables from
+ * an included script the script can either return an object containing the
+ * public functions/variables or {@link provide} can be called in the included
+ * script.
+ *
+ * @name include
+ * @function
+ * @param {String} path
+ * The path to the script
+ * @param {Boolean} global
+ * Whether the script should be included into the global scope.
+ *
+ * @return {Object}
+ * The object returned from the included script
+ *
+ * @example
+ * // included script
+ * function foo()
+ * {
+ * io.print("bar");
+ * }
+ * return {
+ * foo : foo
+ * }
+ *
+ * // including script
+ * var x = include("/path/to/script");
+ * x.foo();
+ *
+ *
+ * // included script
+ * provide("foo", {
+ * foo : function()
+ * {
+ * io.print("bar");
+ * }
+ * });
+ *
+ * // including script
+ * include("/path/to/script");
+ * require(["foo"], function(foo) {
+ * foo.foo();
+ * });
+ * */
+
+static JSObjectRef
+get_exports(JSContextRef ctx, const char *path)
+{
+ JSObjectRef ret = g_hash_table_lookup(s_exports, path);
+ if (ret == NULL)
+ {
+ ret = JSObjectMake(ctx, NULL, NULL);
+ JSValueProtect(ctx, ret);
+ g_hash_table_insert(s_exports, g_strdup(path), ret);
+ }
+ return ret;
+}
+
+
static JSValueRef
-global_include(JSContextRef ctx, JSObjectRef f, JSObjectRef thisObject, size_t argc, const JSValueRef argv[], JSValueRef* exc)
+global_include(JSContextRef ctx, JSObjectRef f, JSObjectRef this, size_t argc, const JSValueRef argv[], JSValueRef* exc)
{
- JSValueRef ret = NULL;
- JSStringRef script;
+ JSValueRef ret = NIL;
gboolean global = false;
char *path = NULL, *content = NULL;
+ JSValueRef exports[1];
+ gboolean is_archive = false;
if (argc < 1)
return NIL;
@@ -1741,11 +1846,23 @@ global_include(JSContextRef ctx, JSObjectRef f, JSObjectRef thisObject, size_t a
if ( (path = js_value_to_char(ctx, argv[0], PATH_MAX, exc)) == NULL)
goto error_out;
- if ( (content = util_get_file_content(path, NULL)) == NULL)
+ if (exar_check_version(path) == 0)
+ {
+ content = (char*) exar_search_extract(path, "main.js", NULL);
+ if (content == NULL)
+ {
+ js_make_exception(ctx, exc, EXCEPTION("include: main.js was not found in %s."), path);
+ goto error_out;
+ }
+ exports[0] = get_exports(ctx, path);
+ is_archive = true;
+ }
+ else if ( (content = util_get_file_content(path, NULL)) == NULL)
{
js_make_exception(ctx, exc, EXCEPTION("include: reading %s failed."), path);
goto error_out;
}
+
const char *tmp = content;
if (*tmp == '#')
{
@@ -1755,35 +1872,100 @@ global_include(JSContextRef ctx, JSObjectRef f, JSObjectRef thisObject, size_t a
tmp++;
}
- if (global)
- {
- script = JSStringCreateWithUTF8CString(tmp);
- ret = JSEvaluateScript(ctx, script, NULL, NULL, 0, exc);
- }
- else
- {
- char *debug = g_strdup_printf(SCRIPT_TEMPLATE_INCLUDE, path, tmp);
- script = JSStringCreateWithUTF8CString(debug);
- JSObjectRef function = JSObjectMakeFunction(ctx, NULL, 0, NULL, script, NULL, 1, exc);
- if (function != NULL)
- {
- JSObjectRef this = JSObjectMake(ctx, NULL, NULL);
- JSValueProtect(ctx, this);
- js_set_object_property(ctx, this, "path", path, exc);
- ret = JSObjectCallAsFunction(ctx, function, this, 0, NULL, exc);
- }
- g_free(debug);
- }
- JSStringRelease(script);
+ ret = do_include(ctx, path, tmp, global, is_archive, is_archive ? 1 : 0, is_archive ? exports : NULL, exc);
error_out:
g_free(content);
g_free(path);
- if (ret == NULL)
- return NIL;
return ret;
}/*}}}*/
+/**
+ * Include scripts from an archive.
+ *
+ * Same as {@link include} but this function can only be called from scripts
+ * inside an archive, so this is mostly useful in extensions. However it is
+ * possible to include scripts from an archive calling the internal function
+ * _xinclude which takes two parameters, the path of the archive and the path of
+ * the included file in the archive.
+ * All scripts in an archive share an object <b>exports</b> which can be used
+ * to share data between scripts in an archive, all exports objects have a
+ * readonly property <b>id</b> which is unique to all archives, it can be used
+ * together with require/provide to define unique module names.
+ *
+ * Unlike {@link include} included archive-scripts cannot be included into the
+ * global scope.
+ *
+ * @name xinclude
+ * @function
+ * @param {String} path
+ * Path of the file in the archive
+ *
+ * @returns {Object}
+ * The object returned from the included file.
+ *
+ * @example
+ * // Archive structure
+ * //
+ * // main.js
+ * // content/foo.js
+ * // content/bar.js
+ *
+ * // main.js
+ * xinclude("content/foo.js");
+ * xinclude("content/bar.js");
+ *
+ * // content/foo.js
+ * function getFoo() {
+ * return 37;
+ * }
+ * exports.getFoo = getFoo;
+ *
+ * // content/bar.js
+ * var x = exports.getFoo();
+ *
+ * // using require/provide
+ * // main.js
+ * require(["foo" + exports.id], function(foo) {
+ * io.print(foo.bar);
+ * });
+ * xinclude("content/foo.js");
+ *
+ * // content/foo.js
+ * provide("foo" + exports.id, {
+ * bar : 37
+ * });
+ *
+ * */
+
+static JSValueRef
+global_xinclude(JSContextRef ctx, JSObjectRef f, JSObjectRef thisObject, size_t argc, const JSValueRef argv[], JSValueRef* exc)
+{
+ char *archive = NULL, *path = NULL, *content = NULL;
+ JSValueRef ret = NIL;
+ off_t fs;
+
+ if (argc < 2)
+ return NIL;
+ if ((archive = js_value_to_char(ctx, argv[0], -1, exc)) == NULL)
+ goto error_out;
+ if ((path = js_value_to_char(ctx, argv[1], -1, exc)) == NULL)
+ goto error_out;
+
+ content = (char*)exar_extract(archive, path, &fs);
+ if (content != NULL)
+ {
+ JSValueRef exports[] = { get_exports(ctx, archive) };
+ ret = do_include(ctx, archive, content, false, true, 1, exports, exc);
+ }
+
+error_out:
+ g_free(archive);
+ g_free(path);
+ g_free(content);
+ return ret;
+}
+
/* global_send_request {{{*/
static JSValueRef
@@ -2799,72 +2981,39 @@ system_get_env(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, s
/* spawn_output {{{*/
static gboolean
-spawn_output(GIOChannel *channel, GIOCondition condition, SpawnChannel *sc)
+spawn_output(GIOChannel *channel, GIOCondition condition, JSObjectRef callback)
{
char *content = NULL;
gboolean result = false;
gsize length;
- char *output = NULL;
if (condition == G_IO_HUP || condition == G_IO_ERR || condition == G_IO_NVAL)
{
g_io_channel_unref(channel);
- spawn_channel_close(sc);
+ if (TRY_CONTEXT_LOCK)
+ {
+ if (s_global_context != NULL)
+ JSValueUnprotect(s_global_context, callback);
+ CONTEXT_UNLOCK;
+ }
return false;
}
else
{
- if (sc->infd != -1)
- {
- if (g_io_channel_read_to_end(channel, &content, &length, NULL) == G_IO_STATUS_NORMAL && content != NULL)
- {
- if (TRY_CONTEXT_LOCK)
- {
- if (s_global_context != NULL)
- {
- JSValueRef arg = js_char_to_value(s_global_context, content);
- if (arg != NULL)
- {
- JSValueRef argv[] = { arg };
- call_as_function_debug(s_global_context, sc->callback, sc->callback, 1, argv);
- }
- }
- CONTEXT_UNLOCK;
- }
- }
- }
- else if (g_io_channel_read_line(channel, &content, &length, NULL, NULL) == G_IO_STATUS_NORMAL && content != NULL)
+ if (g_io_channel_read_to_end(channel, &content, &length, NULL) == G_IO_STATUS_NORMAL && content != NULL)
{
if (TRY_CONTEXT_LOCK)
{
if (s_global_context != NULL)
{
JSValueRef arg = js_char_to_value(s_global_context, content);
- if (arg != NULL)
+ if (arg != NULL)
{
JSValueRef argv[] = { arg };
- JSValueRef ret = call_as_function_debug(s_global_context, sc->callback, sc->callback, 1, argv);
- output = js_value_to_char(s_global_context, ret, -1, NULL);
- if (output && sc->infd != -1)
- {
- size_t r = write(sc->infd, output, strlen(output));
- if (r > 0 && output[r-1] != '\n')
- {
- r = write(sc->infd, "\n", 1);
-
- }
- if ((int)r == -1)
- {
- fputs("Error cannot write to stdin", stderr);
- close(sc->infd);
- sc->infd = -1;
- }
- }
- g_free(output);
+ call_as_function_debug(s_global_context, callback, callback, 1, argv);
}
}
+ CONTEXT_UNLOCK;
}
- CONTEXT_UNLOCK;
- result = true;
}
}
g_free(content);
@@ -2879,6 +3028,8 @@ get_environment(JSContextRef ctx, JSValueRef v, JSValueRef *exc)
JSValueRef current;
char **envp = g_get_environ();
+ if (JSValueIsNull(ctx, v) || JSValueIsUndefined(ctx, v))
+ return envp;
JSObjectRef o = JSValueToObject(ctx, v, exc);
if (o)
{
@@ -2994,7 +3145,7 @@ watch_spawn(GPid pid, gint status, JSObjectRef deferred)
* io.print(stdin);
* }, function(stderr) {
* io.print(stderr, "stderr");
- * }, { foo : "bar"});
+ * }, null, { foo : "bar"});
*
* @param {String} command The command to execute
* @param {Function} [stdout] A callback function that is called when a line from
@@ -3008,12 +3159,10 @@ watch_spawn(GPid pid, gint status, JSObjectRef deferred)
* string the string is passed to stdin of the
* spawned process, pass <i>null</i> if stderr is not
* needed and environment variables should be set
+ * @param {String} [stdin] String that will be piped to stdin, a newline will
+ * be appended to the string.
* @param {Object} [environ] Object that can be used to add environment
* variables to the childs environment
- * @param {Boolean} [toStdin] If set to <i>true</i> stdout- and stderr-callback can be
- * used to write to stdin of the childs and stdout
- * is read line by line, if set to true stdout is
- * read all at once.
*
* @returns {Deferred}
* A deferred, it will be resolved if the child exits normally, it will be
@@ -3029,10 +3178,9 @@ system_spawn(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, siz
char **envp = NULL;
int srgc;
GIOChannel *out_channel, *err_channel;
- SpawnChannel *out = NULL, *err = NULL;
JSObjectRef oc = NULL, ec = NULL;
GPid pid;
- gboolean get_stdin = false;
+ char *pipe_stdin = NULL;
if (argc == 0)
@@ -3055,15 +3203,16 @@ system_spawn(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, siz
ec = js_value_to_function(ctx, argv[2], NULL);
if (argc > 3)
- envp = get_environment(ctx, argv[3], exc);
+ pipe_stdin = js_value_to_char(ctx, argv[3], -1, exc);
+
if (argc > 4)
- get_stdin = JSValueToBoolean(ctx, argv[4]);
-
+ envp = get_environment(ctx, argv[4], exc);
if (!g_shell_parse_argv(cmdline, &srgc, &srgv, NULL) ||
!g_spawn_async_with_pipes(NULL, srgv, envp, G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
NULL, NULL, &pid,
- get_stdin && (oc != NULL || ec != NULL) ? NULL : &infd,
+ //NULL,
+ pipe_stdin != NULL ? &infd : NULL,
oc != NULL ? &outfd : NULL,
ec != NULL ? &errfd : NULL, NULL))
{
@@ -3074,17 +3223,24 @@ system_spawn(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, siz
if (oc != NULL)
{
out_channel = g_io_channel_unix_new(outfd);
- out = spawn_channel_new(oc, infd);
- g_io_add_watch(out_channel, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, (GIOFunc)spawn_output, out);
+ JSValueProtect(ctx, oc);
+ g_io_channel_set_flags(out_channel, G_IO_FLAG_NONBLOCK, NULL);
+ g_io_add_watch(out_channel, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, (GIOFunc)spawn_output, oc);
g_io_channel_set_close_on_unref(out_channel, true);
}
if (ec != NULL)
{
err_channel = g_io_channel_unix_new(errfd);
- err = spawn_channel_new(ec, infd);
- g_io_add_watch(err_channel, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, (GIOFunc)spawn_output, err);
+ JSValueProtect(ctx, ec);
+ g_io_channel_set_flags(err_channel, G_IO_FLAG_NONBLOCK, NULL);
+ g_io_add_watch(err_channel, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, (GIOFunc)spawn_output, ec);
g_io_channel_set_close_on_unref(err_channel, true);
}
+ if (pipe_stdin != NULL && infd != -1)
+ {
+ if (write(infd, pipe_stdin, strlen(pipe_stdin)) == -1 || write(infd, "\n", 1) == -1)
+ perror("system.spawn");
+ }
if (infd != -1)
close(infd);
JSObjectRef deferred = deferred_new(s_global_context);
@@ -3094,6 +3250,7 @@ system_spawn(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, siz
error_out:
CONTEXT_UNLOCK;
+ g_free(pipe_stdin);
g_strfreev(envp);
g_free(cmdline);
g_strfreev(srgv);
@@ -3154,6 +3311,7 @@ system_file_test(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
static JSValueRef
system_mkdir(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argc, const JSValueRef argv[], JSValueRef* exc)
{
+ char expanded[4096];
gboolean ret = false;
if (argc < 2)
{
@@ -3164,8 +3322,15 @@ system_mkdir(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, siz
double mode = JSValueToNumber(ctx, argv[1], exc);
if (path != NULL && !isnan(mode))
{
- ret = g_mkdir_with_parents(path, (gint)mode) == 0;
+ if (util_expand_home(expanded, path, sizeof(expanded)) == NULL)
+ {
+ js_make_exception(ctx, exc, EXCEPTION("Filename too long"));
+ goto error_out;
+ }
+
+ ret = g_mkdir_with_parents(expanded, (gint)mode) == 0;
}
+error_out:
g_free(path);
return JSValueMakeBoolean(ctx, ret);
@@ -3230,6 +3395,7 @@ io_prompt(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t
static JSValueRef
io_read(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argc, const JSValueRef argv[], JSValueRef* exc)
{
+ char expanded[4096];
JSValueRef ret = NULL;
char *path = NULL, *content = NULL;
if (argc < 1)
@@ -3240,7 +3406,12 @@ io_read(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t a
if ( (path = js_value_to_char(ctx, argv[0], PATH_MAX, exc) ) == NULL )
goto error_out;
- if ( (content = util_get_file_content(path, NULL) ) == NULL )
+ if (util_expand_home(expanded, path, sizeof(expanded)) == NULL)
+ {
+ js_make_exception(ctx, exc, EXCEPTION("Filename too long"));
+ goto error_out;
+ }
+ if ( (content = util_get_file_content(expanded, NULL) ) == NULL )
goto error_out;
ret = js_char_to_value(ctx, content);
@@ -3322,15 +3493,21 @@ io_dir_names(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, siz
if (argc < 1)
return NIL;
- JSValueRef ret;
+ char expanded[4096];
+ JSValueRef ret = NIL;
GDir *dir;
char *dir_name = js_value_to_char(ctx, argv[0], PATH_MAX, exc);
const char *name;
if (dir_name == NULL)
return NIL;
+ if (util_expand_home(expanded, dir_name, sizeof(expanded)) == NULL)
+ {
+ js_make_exception(ctx, exc, EXCEPTION("Filename too long"));
+ goto error_out;
+ }
- if ((dir = g_dir_open(dir_name, 0, NULL)) != NULL)
+ if ((dir = g_dir_open(expanded, 0, NULL)) != NULL)
{
GSList *list = NULL;
while ((name = g_dir_read_name(dir)) != NULL)
@@ -3351,6 +3528,7 @@ io_dir_names(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, siz
else
ret = NIL;
+error_out:
g_free(dir_name);
return ret;
}
@@ -3372,6 +3550,7 @@ io_dir_names(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, siz
static JSValueRef
io_write(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argc, const JSValueRef argv[], JSValueRef* exc)
{
+ char expanded[4096];
gboolean ret = false;
FILE *f;
char *path = NULL, *content = NULL, *mode = NULL;
@@ -3395,7 +3574,12 @@ io_write(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t
if ( (content = js_value_to_char(ctx, argv[2], -1, exc)) == NULL )
goto error_out;
- if ( (f = fopen(path, mode)) != NULL)
+ if (util_expand_home(expanded, path, sizeof(expanded)) == NULL)
+ {
+ js_make_exception(ctx, exc, EXCEPTION("Filename too long"));
+ goto error_out;
+ }
+ if ( (f = fopen(expanded, mode)) != NULL)
{
fputs(content, f);
fclose(f);
@@ -4721,10 +4905,11 @@ create_global_object()
JSStaticFunction global_functions[] = {
{ "execute", global_execute, kJSDefaultAttributes },
- { "exit", global_exit, kJSDefaultAttributes },
- { "bind", global_bind, kJSDefaultAttributes },
- { "unbind", global_unbind, kJSDefaultAttributes },
+ { "exit", global_exit, kJSDefaultAttributes },
+ { "bind", global_bind, kJSDefaultAttributes },
+ { "unbind", global_unbind, kJSDefaultAttributes },
{ "include", global_include, kJSDefaultAttributes },
+ { "_xinclude", global_xinclude, kJSDefaultAttributes },
{ 0, 0, 0 },
};
@@ -4742,6 +4927,14 @@ create_global_object()
* @static
* */
JSObjectRef global_object = JSContextGetGlobalObject(ctx);
+ /**
+ * The api-version
+ * @name version
+ * @readonly
+ * @type Number
+ * */
+ js_set_object_number_property(ctx, global_object, "version", API_VERSION, NULL);
+
JSStaticValue data_values[] = {
{ "profile", data_get_profile, NULL, kJSDefaultAttributes },
@@ -4792,6 +4985,7 @@ create_global_object()
* Object that can be used to get dwb's settings, to set dwb's settings use
* {@link execute}
* @name settings
+ * @type Object
* @example
* if (settings.enableScripts == true)
* execute("local_set enable-scripts false");
@@ -4835,7 +5029,7 @@ create_global_object()
* @static
* */
JSStaticFunction system_functions[] = {
- { "spawn", system_spawn, kJSDefaultAttributes },
+ { "_spawn", system_spawn, kJSDefaultAttributes },
{ "spawnSync", system_spawn_sync, kJSDefaultAttributes },
{ "getEnv", system_get_env, kJSDefaultAttributes },
{ "fileTest", system_file_test, kJSDefaultAttributes },
@@ -5340,6 +5534,9 @@ apply_scripts()
{
int length = g_slist_length(s_script_list);
int i=0;
+ JSValueRef exports[1];
+ size_t argc = 0;
+ char *path;
// XXX Not needed?
JSObjectRef *objects = g_malloc(length * sizeof(JSObjectRef));
@@ -5357,7 +5554,15 @@ apply_scripts()
for (GSList *l = s_script_list; l; l=l->next)
{
- JSObjectCallAsFunction(s_global_context, l->data, l->data, 0, NULL, NULL);
+ argc = 0;
+ path = js_get_string_property(s_global_context, l->data, "path");
+ if (path != NULL)
+ {
+ exports[0] = get_exports(s_global_context, path);
+ argc = 1;
+ }
+
+ JSObjectCallAsFunction(s_global_context, l->data, l->data, argc, argc > 0 ? exports : NULL, NULL);
}
g_slist_free(s_script_list);
s_script_list = NULL;
@@ -5423,9 +5628,8 @@ scripts_remove_tab(JSObjectRef obj)
CONTEXT_UNLOCK;
}/*}}}*/
-/* scripts_init_script {{{*/
-void
-scripts_init_script(const char *path, const char *script)
+void
+init_script(const char *path, const char *script, gboolean is_archive, const char *template, int offset)
{
char *debug = NULL;
if (s_global_context == NULL)
@@ -5433,17 +5637,31 @@ scripts_init_script(const char *path, const char *script)
if (js_check_syntax(s_global_context, script, path, 2))
{
- debug = g_strdup_printf(SCRIPT_TEMPLATE, path, script);
- JSObjectRef function = js_make_function(s_global_context, debug, path, 1);
+ debug = g_strdup_printf(template, path, script);
+ JSObjectRef function = js_make_function(s_global_context, debug, path, offset);
+ if (is_archive)
+ js_set_object_property(s_global_context, function, "path", path, NULL);
if (function != NULL)
s_script_list = g_slist_prepend(s_script_list, function);
}
-
g_free(debug);
+}
+
+/* scripts_init_script {{{*/
+void
+scripts_init_script(const char *path, const char *script)
+{
+ init_script(path, script, false, SCRIPT_TEMPLATE, 1);
}/*}}}*/
void
+scripts_init_archive(const char *path, const char *script)
+{
+ init_script(path, script, true, SCRIPT_TEMPLATE_XINCLUDE, 0);
+}
+
+void
evaluate(const char *script)
{
if (!TRY_CONTEXT_LOCK)
@@ -5489,51 +5707,46 @@ scripts_reapply()
gboolean
scripts_init(gboolean force)
{
- static gsize init = 0;
- if (g_once_init_enter(&init))
+ dwb.misc.script_signals = 0;
+ if (s_global_context == NULL)
{
- gsize val = 37;
- dwb.misc.script_signals = 0;
- if (s_global_context == NULL)
- {
- if (force || dwb.misc.js_api == JS_API_ENABLED)
- s_global_context = create_global_object();
- else
- return false;
- }
- s_gobject_signals = g_ptr_array_new();
+ if (force || dwb.misc.js_api == JS_API_ENABLED)
+ s_global_context = create_global_object();
+ else
+ return false;
+ }
+ s_gobject_signals = g_ptr_array_new();
+ s_exports = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)scripts_unprotect);
- dwb.state.script_completion = NULL;
+ dwb.state.script_completion = NULL;
- char *dir = util_get_data_dir(LIBJS_DIR);
- if (dir != NULL)
+ char *dir = util_get_data_dir(LIBJS_DIR);
+ if (dir != NULL)
+ {
+ GString *content = g_string_new(NULL);
+ util_get_directory_content(content, dir, "js");
+ if (content != NULL)
{
- GString *content = g_string_new(NULL);
- util_get_directory_content(content, dir, "js");
- if (content != NULL)
- {
- JSStringRef js_script = JSStringCreateWithUTF8CString(content->str);
- JSEvaluateScript(s_global_context, js_script, NULL, NULL, 0, NULL);
- JSStringRelease(js_script);
- }
- g_string_free(content, true);
- g_free(dir);
+ JSStringRef js_script = JSStringCreateWithUTF8CString(content->str);
+ JSEvaluateScript(s_global_context, js_script, NULL, NULL, 0, NULL);
+ JSStringRelease(js_script);
}
+ g_string_free(content, true);
+ g_free(dir);
+ }
- UNDEFINED = JSValueMakeUndefined(s_global_context);
- JSValueProtect(s_global_context, UNDEFINED);
- NIL = JSValueMakeNull(s_global_context);
- JSValueProtect(s_global_context, NIL);
+ UNDEFINED = JSValueMakeUndefined(s_global_context);
+ JSValueProtect(s_global_context, UNDEFINED);
+ NIL = JSValueMakeNull(s_global_context);
+ JSValueProtect(s_global_context, NIL);
- s_init_before = get_private(s_global_context, "_initBefore");
- s_init_after = get_private(s_global_context, "_initAfter");
- //s_private = get_private(s_global_context, "_private");
+ s_init_before = get_private(s_global_context, "_initBefore");
+ s_init_after = get_private(s_global_context, "_initAfter");
+ //s_private = get_private(s_global_context, "_private");
- JSObjectRef o = JSObjectMakeArray(s_global_context, 0, NULL, NULL);
- s_array_contructor = js_get_object_property(s_global_context, o, "constructor");
- JSValueProtect(s_global_context, s_array_contructor);
- g_once_init_leave(&init, val);
- }
+ JSObjectRef o = JSObjectMakeArray(s_global_context, 0, NULL, NULL);
+ s_array_contructor = js_get_object_property(s_global_context, o, "constructor");
+ JSValueProtect(s_global_context, s_array_contructor);
return true;
}/*}}}*/
@@ -5551,7 +5764,7 @@ scripts_execute_one(const char *script)
return ret;
}
void
-scripts_unbind(JSObjectRef obj)
+scripts_unprotect(JSObjectRef obj)
{
if (!TRY_CONTEXT_LOCK)
return;
@@ -5618,6 +5831,7 @@ scripts_end()
}
g_ptr_array_free(s_gobject_signals, false);
s_gobject_signals = NULL;
+ g_hash_table_unref(s_exports);
for (int i=0; i<CONSTRUCTOR_LAST; i++)
diff --git a/src/scripts.h b/src/scripts.h
index 3bb03cb2..53571f99 100644
--- a/src/scripts.h
+++ b/src/scripts.h
@@ -16,8 +16,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef SCRIPTS_H
-#define SCRIPTS_H
+#ifndef __DWB_SCRIPTS_H__
+#define __DWB_SCRIPTS_H__
#include <JavaScriptCore/JavaScript.h>
@@ -49,6 +49,7 @@ enum SIGNALS {
SCRIPTS_SIG_CHANGE_MODE,
SCRIPTS_SIG_EXECUTE_COMMAND,
SCRIPTS_SIG_CONTEXT_MENU,
+ SCRIPTS_SIG_ERROR,
SCRIPTS_SIG_LAST,
} ;
@@ -68,9 +69,10 @@ void scripts_create_tab(GList *gl);
void scripts_remove_tab(JSObjectRef );
void scripts_end(void);
void scripts_init_script(const char *, const char *);
+void scripts_init_archive(const char *, const char *);
gboolean scripts_init(gboolean);
void scripts_reinit();
-void scripts_unbind(JSObjectRef);
+void scripts_unprotect(JSObjectRef);
DwbStatus scripts_eval_key(KeyMap *m, Arg *arg);
gboolean scripts_execute_one(const char *script);
void scripts_completion_activate(void);
diff --git a/src/session.h b/src/session.h
index 5a953ff1..1da7c4ec 100644
--- a/src/session.h
+++ b/src/session.h
@@ -16,8 +16,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef SESSION_H
-#define SESSION_H
+#ifndef __DWB_SESSION_H__
+#define __DWB_SESSION_H__
enum SessionFlags {
SESSION_FORCE = 1<<0,
diff --git a/src/soup.h b/src/soup.h
index cecb45c3..a49a1cc8 100644
--- a/src/soup.h
+++ b/src/soup.h
@@ -16,8 +16,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef COOKIES_H
-#define COOKIES_H
+#ifndef __DWB_SOUP_H__
+#define __DWB_SOUP_H__
void dwb_soup_clean(void);
void dwb_soup_sync_cookies();
diff --git a/src/util.c b/src/util.c
index e33fdd05..e208775f 100644
--- a/src/util.c
+++ b/src/util.c
@@ -318,12 +318,12 @@ util_get_directory_content(GString *buffer, const char *dirname, const char *ext
}
}/*}}}*/
-void
+gboolean
util_rmdir(const char *path, gboolean only_content, gboolean recursive)
{
GDir *dir = g_dir_open(path, 0, NULL);
if (dir == NULL)
- return;
+ return false;
const char *filename = NULL;
char *fullpath = NULL;
while ( (filename = g_dir_read_name(dir)) )
@@ -342,6 +342,7 @@ util_rmdir(const char *path, gboolean only_content, gboolean recursive)
rmdir(path);
g_dir_close(dir);
+ return true;
}
/* util_get_file_content(const char *filename) return: char * (alloc) {{{*/
char *
@@ -370,6 +371,11 @@ util_get_lines(const char *filename)
char *
util_expand_home(char *buffer, const char *filename, size_t length)
{
+ if (strlen(filename) >= length)
+ {
+ *buffer = 0;
+ return NULL;
+ }
if (*filename == '~')
{
const char *home = g_getenv("HOME");
@@ -386,7 +392,16 @@ util_normalize_filename(char *buffer, const char *filename, size_t length)
char *tmp = buffer;
for (size_t i=0; *filename != 0 && i<length-1; i++, filename++, buffer++)
{
- *buffer = *filename == '/' ? '_' : *filename;
+ switch (*filename)
+ {
+ case '/':
+ case '#':
+ *buffer = '_';
+ break;
+ default :
+ *buffer = *filename;
+ break;
+ }
}
*buffer = 0;
return tmp;
@@ -958,3 +973,27 @@ util_resolve_symlink(char *path)
}
return ret;
}
+gboolean
+util_keyfile_do(char *path, KeyFileAction action, const void *data)
+{
+ char *content;
+ gboolean result = false;
+ GKeyFile *kf = g_key_file_new();
+ if (kf == NULL)
+ return result;
+
+ if (g_key_file_load_from_file(kf, path, G_KEY_FILE_KEEP_COMMENTS, NULL))
+ {
+ if (action(kf, data))
+ {
+ if ((content = g_key_file_to_data(kf, NULL, NULL)) != NULL)
+ {
+ util_set_file_content(path, content);
+ g_free(content);
+ result = true;
+ }
+ }
+ }
+ g_key_file_free(kf);
+ return result;
+}
diff --git a/src/util.h b/src/util.h
index 825467de..14ee8c06 100644
--- a/src/util.h
+++ b/src/util.h
@@ -16,9 +16,10 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef UTIL_H
-#define UTIL_H
+#ifndef __DWB_UTIL_H__
+#define __DWB_UTIL_H__
+typedef gboolean (*KeyFileAction)(GKeyFile *, const void *data);
// strings
char * util_string_replace(const char *haystack, const char *needle, const char *replacemant);
@@ -28,7 +29,7 @@ gboolean util_is_hex(const char *string);
int util_test_connect(const char *uri);
char * util_get_temp_filename(const char *);
-void util_rmdir(const char *path, gboolean, gboolean recursive);
+gboolean util_rmdir(const char *path, gboolean, gboolean recursive);
// keys
char * dwb_modmask_to_string(guint );
@@ -103,6 +104,7 @@ Sanitize util_string_to_sanitize(const char *);
char *util_create_json(int, ...);
int util_modulo(int, int);
char *util_resolve_symlink(char *path);
+gboolean util_keyfile_do(char *path, KeyFileAction action, const void *data);
#endif
diff --git a/src/util/Makefile b/src/util/Makefile
index 05a4b0cf..fc74323c 100644
--- a/src/util/Makefile
+++ b/src/util/Makefile
@@ -34,7 +34,7 @@ $(TLDS_H): $(TLDS_IN) $(MKTLDS)
@./$(MKTLDS) < $(TLDS_IN) > $@
$(MKTLDS): $(MKTLDS_SRC)
- @echo "${CC} $<"
+ @echo $(CC) $<
@$(CC) -o $@ $< $(CFLAGS) $(CPPFLAGS) $(LDFLAGS)
$(HSTS_PRELOAD): $(HSTS) $(TRANSPORT_SECURITY_CERTS) $(TRANSPORT_SECURITY_JSON)
@@ -42,7 +42,7 @@ $(HSTS_PRELOAD): $(HSTS) $(TRANSPORT_SECURITY_CERTS) $(TRANSPORT_SECURITY_JSON)
@./$(HSTS) > $@
$(HSTS): $(HSTS).c
- @echo "${CC} $<"
+ @echo $(CC) $<
@$(CC) $(CFLAGS) $(CPPFLAGS) -o $(HSTS) $(HSTS).c $(LDFLAGS)
clean:
diff --git a/src/view.c b/src/view.c
index 681696ec..fce0bce3 100644
--- a/src/view.c
+++ b/src/view.c
@@ -1164,6 +1164,38 @@ view_load_status_cb(WebKitWebView *web, GParamSpec *pspec, GList *gl)
static gboolean
view_load_error_cb(WebKitWebView *web, WebKitWebFrame *frame, char *uri, GError *weberror, GList *gl)
{
+ if (EMIT_SCRIPT(ERROR))
+ {
+ /**
+ * Emitted when an error occurs dureing a page load
+ *
+ * @event error
+ * @memberOf signals
+ * @param {signals~onError} callback
+ * Callback function that will be called when the signal is emitted
+ * */
+ /**
+ * Callback called when the load-status of a WebKitWebView changes
+ * @callback signals~onError
+ * @param {WebKitWebView} webview
+ * The webview that emitted the signal
+ * @param {WebKitWebFrame} frame
+ * The webframe that emitted the signal
+ * @param {Object} error
+ * The error
+ * @param {String} error.message
+ * The error message
+ * @param {Number} error.code
+ * The error code
+ *
+ * @returns {Boolean}
+ * Return true to prevent the default action
+ * */
+ char *json = util_create_json(2, INTEGER, "code", weberror->code,
+ CHAR, "message", weberror->message);
+ ScriptSignal sig = { SCRIPTS_WV(gl), { G_OBJECT(frame) }, SCRIPTS_SIG_META(json, ERROR, 1) };
+ SCRIPTS_EMIT_RETURN(sig, json, true);
+ }
if (weberror->code == WEBKIT_NETWORK_ERROR_CANCELLED
|| weberror->code == WEBKIT_PLUGIN_ERROR_WILL_HANDLE_LOAD
|| weberror->code == WEBKIT_POLICY_ERROR_FRAME_LOAD_INTERRUPTED_BY_POLICY_CHANGE)
diff --git a/src/view.h b/src/view.h
index 10b31553..16e23568 100644
--- a/src/view.h
+++ b/src/view.h
@@ -16,8 +16,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef VIEW_H
-#define VIEW_H
+#ifndef __DWB_VIEW_H__
+#define __DWB_VIEW_H__
GList * view_add(const char *uri, gboolean background);
DwbStatus view_remove(GList *gl);
diff --git a/tools/backtrace.sh b/tools/backtrace.sh
index 0c77dd3f..b21b3c8d 100755
--- a/tools/backtrace.sh
+++ b/tools/backtrace.sh
@@ -17,6 +17,8 @@ tar xvf master.tar.gz -C "${DWBDIR}" --strip-components=1
cd "${DWBDIR}/src/util"
make
+cd "${DWBDIR}/exar"
+make
cd "${DWBDIR}/src"
make debug
gdb -batch -ex "set logging on ${LOGFILE}" -ex "run" -ex "bt" -ex "quit" dwb_d
diff --git a/tools/check_header.sh b/tools/check_header.sh
new file mode 100755
index 00000000..19d7064a
--- /dev/null
+++ b/tools/check_header.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+HEADER="$1"
+CC="${2:-gcc}"
+
+echo "#include <$HEADER>" | "${CC}" -E - &>/dev/null
+if [ $? -eq 0 ]; then
+ echo 1
+else
+ echo 0
+fi
+