diff options
-rw-r--r-- | AUTHORS | 2 | ||||
-rw-r--r-- | Makefile | 12 | ||||
-rw-r--r-- | config.mk | 27 | ||||
-rw-r--r-- | contrib/bash-completion | 27 | ||||
-rw-r--r-- | doc/dwb.1 | 19 | ||||
-rw-r--r-- | doc/dwb.1.txt | 4 | ||||
-rw-r--r-- | doc/dwbem.1 | 13 | ||||
-rw-r--r-- | doc/dwbem.1.txt | 4 | ||||
-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/Makefile | 34 | ||||
-rw-r--r-- | exar/exar.c | 677 | ||||
-rw-r--r-- | exar/exar.h | 165 | ||||
-rw-r--r-- | exar/main.c | 155 | ||||
-rw-r--r-- | m4/keys.m4 | 1 | ||||
-rw-r--r-- | scripts/base.js | 37 | ||||
-rw-r--r-- | scripts/lib/dwb.js | 27 | ||||
-rw-r--r-- | scripts/lib/extensions.js | 23 | ||||
-rw-r--r-- | scripts/lib/net.js | 79 | ||||
-rw-r--r-- | scripts/lib/system.js | 27 | ||||
-rw-r--r-- | src/Makefile | 12 | ||||
-rw-r--r-- | src/adblock.c | 34 | ||||
-rw-r--r-- | src/adblock.h | 13 | ||||
-rw-r--r-- | src/application.c | 20 | ||||
-rw-r--r-- | src/application.h | 4 | ||||
-rw-r--r-- | src/callback.c | 5 | ||||
-rw-r--r-- | src/callback.h | 4 | ||||
-rw-r--r-- | src/commands.c | 22 | ||||
-rw-r--r-- | src/commands.h | 5 | ||||
-rw-r--r-- | src/completion.h | 4 | ||||
-rw-r--r-- | src/config.h | 6 | ||||
-rw-r--r-- | src/config.mk | 3 | ||||
-rw-r--r-- | src/dom.h | 4 | ||||
-rw-r--r-- | src/domain.h | 4 | ||||
-rw-r--r-- | src/download.h | 4 | ||||
-rw-r--r-- | src/dwb.c | 87 | ||||
-rw-r--r-- | src/dwb.h | 5 | ||||
-rw-r--r-- | src/editor.h | 4 | ||||
-rw-r--r-- | src/entry.h | 4 | ||||
-rw-r--r-- | src/hsts.h | 6 | ||||
-rw-r--r-- | src/html.h | 4 | ||||
-rw-r--r-- | src/js.c | 8 | ||||
-rw-r--r-- | src/js.h | 5 | ||||
-rw-r--r-- | src/local.h | 4 | ||||
-rw-r--r-- | src/plugins.h | 4 | ||||
-rw-r--r-- | src/scripts.c | 568 | ||||
-rw-r--r-- | src/scripts.h | 8 | ||||
-rw-r--r-- | src/session.h | 4 | ||||
-rw-r--r-- | src/soup.h | 4 | ||||
-rw-r--r-- | src/util.c | 45 | ||||
-rw-r--r-- | src/util.h | 8 | ||||
-rw-r--r-- | src/util/Makefile | 4 | ||||
-rw-r--r-- | src/view.c | 32 | ||||
-rw-r--r-- | src/view.h | 4 | ||||
-rwxr-xr-x | tools/backtrace.sh | 2 | ||||
-rwxr-xr-x | tools/check_header.sh | 12 |
56 files changed, 2197 insertions, 353 deletions
@@ -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 @@ -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) @@ -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 @@ -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; +} @@ -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 @@ -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 *); @@ -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() { @@ -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(); @@ -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 @@ -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 *); @@ -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) { @@ -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, @@ -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(); @@ -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; +} @@ -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: @@ -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) @@ -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 + |