From: David Bremner Date: Sat, 27 Jun 2015 13:29:52 +0000 (+0200) Subject: Merge tag '0.20.2' X-Git-Tag: 0.21_rc0~89 X-Git-Url: https://git.notmuchmail.org/git?p=notmuch;a=commitdiff_plain;h=8cca886b10c5ec44f3214701c0c1e3c896d53d5c;hp=c66e0d4bc2b2152a5d9b1f300fcf5c853bc07cb6 Merge tag '0.20.2' notmuch 0.20.2 release Conflicts: NEWS --- diff --git a/Makefile.local b/Makefile.local index 6d547423..61a9c4c3 100644 --- a/Makefile.local +++ b/Makefile.local @@ -59,7 +59,7 @@ endif FINAL_LIBNOTMUCH_LDFLAGS = $(LDFLAGS) $(AS_NEEDED_LDFLAGS) $(CONFIGURE_LDFLAGS) .PHONY: all -all: notmuch notmuch-shared build-man +all: notmuch notmuch-shared build-man ruby-bindings ifeq ($(MAKECMDGOALS),) ifeq ($(shell cat .first-build-message 2>/dev/null),) @NOTMUCH_FIRST_BUILD=1 $(MAKE) --no-print-directory all diff --git a/NEWS b/NEWS index cc8d7edf..e474e53e 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,11 @@ +Notmuch 0.21 (UNRELEASED) +========================= + +Library +------- + +The use of absolute paths is now enforced when calling notmuch_database_{open, create} + Notmuch 0.20.2 (2015-06-27) =========================== @@ -2767,7 +2775,7 @@ New 'G' key binding to trigger mail refresh (G == "Get new mail") The 'G' key works wherever '=' works. Before refreshing the screen it calls an external program that can be used to poll email servers, - run notmuch new and setup specific tags for the new emails. The + run notmuch new and set up specific tags for the new emails. The script to be called should be configured with the "Notmuch Poll Script" setting in the customize interface. This script will typically invoke "notmuch new" and then perhaps several "notmuch diff --git a/bindings/Makefile b/bindings/Makefile new file mode 100644 index 00000000..de492a7c --- /dev/null +++ b/bindings/Makefile @@ -0,0 +1,7 @@ +# See Makefile.local for the list of files to be compiled in this +# directory. +all: + $(MAKE) -C .. all + +.DEFAULT: + $(MAKE) -C .. $@ diff --git a/bindings/Makefile.local b/bindings/Makefile.local new file mode 100644 index 00000000..4ecf839d --- /dev/null +++ b/bindings/Makefile.local @@ -0,0 +1,21 @@ +# -*- makefile -*- + +dir := bindings + +# force the shared library to be built +ruby-bindings: lib/$(LINKER_NAME) +ifeq ($(HAVE_RUBY_DEV),1) + cd $(dir)/ruby && \ + EXTRA_LDFLAGS="$(NO_UNDEFINED_LDFLAGS)" \ + LIBNOTMUCH="../../lib/$(LINKER_NAME)" \ + ruby extconf.rb --vendor + $(MAKE) -C $(dir)/ruby +else + @echo Missing dependency, skipping ruby bindings +endif + +CLEAN += $(patsubst %,$(dir)/ruby/%, \ + .RUBYARCHDIR.time \ + Makefile database.o directory.o filenames.o\ + init.o message.o messages.o mkmf.log notmuch.so query.o \ + status.o tags.o thread.o threads.o) diff --git a/bindings/ruby/README b/bindings/ruby/README new file mode 100644 index 00000000..a2946b66 --- /dev/null +++ b/bindings/ruby/README @@ -0,0 +1,7 @@ +To build the the notmuch ruby extension, run the following commands +from the *top level* notmuch source directory: + +% ./configure +% make ruby-bindings + +The generic documentation about building notmuch also applies. diff --git a/bindings/ruby/extconf.rb b/bindings/ruby/extconf.rb index 6160db26..ddaa6841 100644 --- a/bindings/ruby/extconf.rb +++ b/bindings/ruby/extconf.rb @@ -10,22 +10,16 @@ dir = File.join('..', '..', 'lib') # includes $INCFLAGS = "-I#{dir} #{$INCFLAGS}" -# make sure there are no undefined symbols -$LDFLAGS += ' -Wl,--no-undefined' - -def have_local_library(lib, path, func, headers = nil) - checking_for checking_message(func, lib) do - lib = File.join(path, lib) - if try_func(func, lib, headers) - $LOCAL_LIBS += lib - end - end +if ENV['EXTRA_LDFLAGS'] + $LDFLAGS += " " + ENV['EXTRA_LDFLAGS'] end -if not have_local_library('libnotmuch.so', dir, 'notmuch_database_create', 'notmuch.h') +if not ENV['LIBNOTMUCH'] exit 1 end +$LOCAL_LIBS += ENV['LIBNOTMUCH'] + # Create Makefile dir_config('notmuch') create_makefile('notmuch') diff --git a/configure b/configure index 4af7ba94..b967a4ed 100755 --- a/configure +++ b/configure @@ -21,6 +21,7 @@ srcdir=$(dirname "$0") subdirs="util compat lib parse-time-string completion doc emacs" subdirs="${subdirs} performance-test test test/test-databases" +subdirs="${subdirs} bindings" # For a non-srcdir configure invocation (such as ../configure), create # the directory structure and copy Makefiles. @@ -47,9 +48,11 @@ CC=${CC:-cc} CXX=${CXX:-c++} CFLAGS=${CFLAGS:--g -O2} CPPFLAGS=${CPPFLAGS:-} +CXXFLAGS_for_sh=${CXXFLAGS:-${CFLAGS}} CXXFLAGS=${CXXFLAGS:-\$(CFLAGS)} LDFLAGS=${LDFLAGS:-} XAPIAN_CONFIG=${XAPIAN_CONFIG:-xapian-config} +PYTHON=${PYTHON:-} # We don't allow the EMACS or GZIP Makefile variables inherit values # from the environment as we do with CC and CXX above. The reason is @@ -269,6 +272,35 @@ dependencies are available: EOF errors=0 +printf "int main(void){return 0;}\n" > minimal.c + +printf "Sanity checking C compilation environment... " +if ${CC} ${CFLAGS} ${CPPFLAGS} minimal.c ${LDFLAGS} -o minimal > /dev/null 2>&1 +then + printf "OK.\n" +else + printf "Fail.\n" + errors=$((errors + 1)) +fi + +printf "Sanity checking C++ compilation environment... " +if ${CXX} ${CXXFLAGS_for_sh} ${CPPFLAGS} minimal.c ${LDFLAGS} -o minimal > /dev/null 2>&1 +then + printf "OK.\n" +else + printf "Fail.\n" + errors=$((errors + 1)) +fi + +if [ $errors -gt 0 ]; then + cat < /dev/null 2>&1; then have_pkg_config=1 @@ -377,7 +409,7 @@ fi printf "Checking for python... " have_python=0 -for name in python python2 python3; do +for name in ${PYTHON} python python2 python3; do if command -v $name > /dev/null; then have_python=1 python=$name @@ -443,6 +475,15 @@ else have_doxygen=0 fi +printf "Checking for ruby development files... " +if ruby -e "require 'mkmf'"> /dev/null 2>&1; then + printf "Yes.\n" + have_ruby_dev=1 +else + printf "No (skipping ruby bindings)\n" + have_ruby_dev=0 +fi + printf "Checking if sphinx is available and supports nroff output... " if command -v sphinx-build > /dev/null && ${python} -m sphinx.writers.manpage > /dev/null 2>&1 ; then printf "Yes.\n" @@ -690,8 +731,6 @@ else fi rm -f compat/check_asctime -printf "int main(void){return 0;}\n" > minimal.c - printf "Checking for rpath support... " if ${CC} -Wl,--enable-new-dtags -Wl,-rpath,/tmp/ -o minimal minimal.c >/dev/null 2>&1 then @@ -712,6 +751,16 @@ else as_needed_ldflags="" fi +printf "Checking for -Wl,--no-undefined... " +if ${CC} -Wl,--no-undefined -o minimal minimal.c >/dev/null 2>&1 +then + printf "Yes.\n" + no_undefined_ldflags="-Wl,--no-undefined" +else + printf "No (nothing to worry about).\n" + no_undefined_ldflags="" +fi + WARN_CXXFLAGS="" printf "Checking for available C++ compiler warning flags... " for flag in -Wall -Wextra -Wwrite-strings; do @@ -858,6 +907,10 @@ HAVE_CANONICALIZE_FILE_NAME = ${have_canonicalize_file_name} # build its own version) HAVE_GETLINE = ${have_getline} +# Are the ruby development files (and ruby) available? If not skip +# building/testing ruby bindings. +HAVE_RUBY_DEV = ${have_ruby_dev} + # Whether the strcasestr function is available (if not, then notmuch will # build its own version) HAVE_STRCASESTR = ${have_strcasestr} @@ -912,6 +965,9 @@ RPATH_LDFLAGS = ${rpath_ldflags} # Flags needed to have linker link only to necessary libraries AS_NEEDED_LDFLAGS = ${as_needed_ldflags} +# Flags to have the linker flag undefined symbols in object files +NO_UNDEFINED_LDFLAGS = ${no_undefined_ldflags} + # Whether valgrind header files are available HAVE_VALGRIND = ${have_valgrind} @@ -970,6 +1026,10 @@ NOTMUCH_HAVE_MAN=$((have_sphinx)) # Name of python interpreter NOTMUCH_PYTHON=${python} + +# Are the ruby development files (and ruby) available? If not skip +# building/testing ruby bindings. +NOTMUCH_HAVE_RUBY_DEV=${have_ruby_dev} EOF # Finally, after everything configured, inform the user how to continue. diff --git a/debian/rules b/debian/rules index 56378ecd..04f0062a 100755 --- a/debian/rules +++ b/debian/rules @@ -19,7 +19,6 @@ override_dh_auto_build: dh_auto_build -- V=1 dh_auto_build --sourcedirectory bindings/python cd bindings/python && $(python3_all) setup.py build - cd bindings/ruby && ruby extconf.rb --vendor && make $(MAKE) -C contrib/notmuch-mutt override_dh_auto_clean: diff --git a/devel/release-checks.sh b/devel/release-checks.sh index ae02f557..efd0b344 100755 --- a/devel/release-checks.sh +++ b/devel/release-checks.sh @@ -59,6 +59,17 @@ readonly VERSION # In the rest of this file, tests collect list of errors to be fixed +echo -n "Checking that git working directory is clean... " +git_status=`git status --porcelain` +if [ "$git_status" = '' ] +then + echo Yes. +else + echo No. + append_emsg "Git working directory is not clean (git status --porcelain)." +fi +unset git_status + verfail () { echo No. diff --git a/doc/man1/notmuch.rst b/doc/man1/notmuch.rst index b33738ed..ae0461a2 100644 --- a/doc/man1/notmuch.rst +++ b/doc/man1/notmuch.rst @@ -49,6 +49,10 @@ Supported global options for ``notmuch`` include Specify the configuration file to use. This overrides any configuration file specified by ${NOTMUCH\_CONFIG}. +All global options except ``--config`` can also be specified after the +command. For example, ``notmuch subcommand --version`` is equivalent to +``notmuch --version subcommand``. + COMMANDS ======== diff --git a/emacs/notmuch.el b/emacs/notmuch.el index ab004543..6564816f 100644 --- a/emacs/notmuch.el +++ b/emacs/notmuch.el @@ -181,6 +181,7 @@ there will be called at other points of notmuch execution." (defvar notmuch-search-stash-map (let ((map (make-sparse-keymap))) (define-key map "i" 'notmuch-search-stash-thread-id) + (define-key map "q" 'notmuch-stash-query) (define-key map "?" 'notmuch-subkeymap-help) map) "Submap for stash commands") @@ -191,6 +192,11 @@ there will be called at other points of notmuch execution." (interactive) (notmuch-common-do-stash (notmuch-search-find-thread-id))) +(defun notmuch-stash-query () + "Copy current query to kill-ring." + (interactive) + (notmuch-common-do-stash (notmuch-search-get-query))) + (defvar notmuch-search-query-string) (defvar notmuch-search-target-thread) (defvar notmuch-search-target-line) @@ -855,13 +861,15 @@ See `notmuch-tag' for information on the format of TAG-CHANGES." "Read a notmuch-query from the minibuffer with completion. PROMPT is the string to prompt with." - (lexical-let - ((completions - (append (list "folder:" "path:" "thread:" "id:" "date:" "from:" "to:" - "subject:" "attachment:" "mimetype:") - (mapcar (lambda (tag) - (concat "tag:" (notmuch-escape-boolean-term tag))) - (process-lines notmuch-command "search" "--output=tags" "*"))))) + (lexical-let* + ((all-tags + (mapcar (lambda (tag) (notmuch-escape-boolean-term tag)) + (process-lines notmuch-command "search" "--output=tags" "*"))) + (completions + (append (list "folder:" "path:" "thread:" "id:" "date:" "from:" "to:" + "subject:" "attachment:" "mimetype:") + (mapcar (lambda (tag) (concat "tag:" tag)) all-tags) + (mapcar (lambda (tag) (concat "is:" tag)) all-tags)))) (let ((keymap (copy-keymap minibuffer-local-map)) (current-query (case major-mode (notmuch-search-mode (notmuch-search-get-query)) diff --git a/lib/Makefile.local b/lib/Makefile.local index f9ecd50e..b58b4e8a 100644 --- a/lib/Makefile.local +++ b/lib/Makefile.local @@ -33,7 +33,7 @@ LIBRARY_SUFFIX = so LINKER_NAME = libnotmuch.$(LIBRARY_SUFFIX) SONAME = $(LINKER_NAME).$(LIBNOTMUCH_VERSION_MAJOR) LIBNAME = $(SONAME).$(LIBNOTMUCH_VERSION_MINOR).$(LIBNOTMUCH_VERSION_RELEASE) -LIBRARY_LINK_FLAG = -shared -Wl,--version-script=notmuch.sym,-soname=$(SONAME) -Wl,--no-undefined +LIBRARY_LINK_FLAG = -shared -Wl,--version-script=notmuch.sym,-soname=$(SONAME) $(NO_UNDEFINED_LDFLAGS) ifeq ($(PLATFORM),OPENBSD) LIBRARY_LINK_FLAG += -lc endif diff --git a/lib/database.cc b/lib/database.cc index cffab62c..6a151749 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -342,6 +342,8 @@ notmuch_status_to_string (notmuch_status_t status) return "Unsupported operation"; case NOTMUCH_STATUS_UPGRADE_REQUIRED: return "Operation requires a database upgrade"; + case NOTMUCH_STATUS_PATH_ERROR: + return "Path supplied is illegal for this function"; default: case NOTMUCH_STATUS_LAST_STATUS: return "Unknown error status value"; @@ -657,6 +659,12 @@ notmuch_database_create_verbose (const char *path, goto DONE; } + if (path[0] != '/') { + message = strdup ("Error: Database path must be absolute.\n"); + status = NOTMUCH_STATUS_PATH_ERROR; + goto DONE; + } + err = stat (path, &st); if (err) { IGNORE_RESULT (asprintf (&message, "Error: Cannot create database at %s: %s.\n", @@ -847,6 +855,12 @@ notmuch_database_open_verbose (const char *path, goto DONE; } + if (path[0] != '/') { + message = strdup ("Error: Database path must be absolute.\n"); + status = NOTMUCH_STATUS_PATH_ERROR; + goto DONE; + } + if (! (notmuch_path = talloc_asprintf (local, "%s/%s", path, ".notmuch"))) { message = strdup ("Out of memory\n"); status = NOTMUCH_STATUS_OUT_OF_MEMORY; @@ -1336,7 +1350,7 @@ notmuch_database_upgrade (notmuch_database_t *notmuch, return NOTMUCH_STATUS_SUCCESS; if (progress_notify) { - /* Setup our handler for SIGALRM */ + /* Set up our handler for SIGALRM */ memset (&action, 0, sizeof (struct sigaction)); action.sa_handler = handle_sigalrm; sigemptyset (&action.sa_mask); diff --git a/lib/notmuch.h b/lib/notmuch.h index 20c4e019..c8edc84d 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -163,6 +163,11 @@ typedef enum _notmuch_status { * The operation requires a database upgrade. */ NOTMUCH_STATUS_UPGRADE_REQUIRED, + /** + * There is a problem with the proposed path, e.g. a relative path + * passed to a function expecting an absolute path. + */ + NOTMUCH_STATUS_PATH_ERROR, /** * Not an actual status value. Just a way to find out how many * valid status values there are. diff --git a/notmuch-client.h b/notmuch-client.h index fb3021cc..78680aa1 100644 --- a/notmuch-client.h +++ b/notmuch-client.h @@ -466,4 +466,8 @@ notmuch_database_dump (notmuch_database_t *notmuch, notmuch_bool_t gzip_output); #include "command-line-arguments.h" +extern const notmuch_opt_desc_t notmuch_shared_options []; +void notmuch_process_shared_options (const char* subcommand_name); +int notmuch_minimal_options (const char* subcommand_name, + int argc, char **argv); #endif diff --git a/notmuch-compact.c b/notmuch-compact.c index 2fc012a9..5be551d4 100644 --- a/notmuch-compact.c +++ b/notmuch-compact.c @@ -38,12 +38,16 @@ notmuch_compact_command (notmuch_config_t *config, int argc, char *argv[]) notmuch_opt_desc_t options[] = { { NOTMUCH_OPT_STRING, &backup_path, "backup", 0, 0 }, { NOTMUCH_OPT_BOOLEAN, &quiet, "quiet", 'q', 0 }, + { NOTMUCH_OPT_INHERIT, (void *) ¬much_shared_options, NULL, 0, 0 }, + { 0, 0, 0, 0, 0} }; opt_index = parse_arguments (argc, argv, options, 1); if (opt_index < 0) return EXIT_FAILURE; + notmuch_process_shared_options (argv[0]); + if (! quiet) printf ("Compacting database...\n"); ret = notmuch_database_compact (path, backup_path, diff --git a/notmuch-config.c b/notmuch-config.c index 2d5c297b..93482787 100644 --- a/notmuch-config.c +++ b/notmuch-config.c @@ -872,8 +872,15 @@ int notmuch_config_command (notmuch_config_t *config, int argc, char *argv[]) { int ret; + int opt_index; - argc--; argv++; /* skip subcommand argument */ + opt_index = notmuch_minimal_options ("config", argc, argv); + if (opt_index < 0) + return EXIT_FAILURE; + + /* skip at least subcommand argument */ + argc-= opt_index; + argv+= opt_index; if (argc < 1) { fprintf (stderr, "Error: notmuch config requires at least one argument.\n"); diff --git a/notmuch-count.c b/notmuch-count.c index 6058f7c9..57a88a8d 100644 --- a/notmuch-count.c +++ b/notmuch-count.c @@ -146,6 +146,7 @@ notmuch_count_command (notmuch_config_t *config, int argc, char *argv[]) { 0, 0 } } }, { NOTMUCH_OPT_BOOLEAN, &batch, "batch", 0, 0 }, { NOTMUCH_OPT_STRING, &input_file_name, "input", 'i', 0 }, + { NOTMUCH_OPT_INHERIT, (void *) ¬much_shared_options, NULL, 0, 0 }, { 0, 0, 0, 0, 0 } }; @@ -153,6 +154,8 @@ notmuch_count_command (notmuch_config_t *config, int argc, char *argv[]) if (opt_index < 0) return EXIT_FAILURE; + notmuch_process_shared_options (argv[0]); + if (input_file_name) { batch = TRUE; input = fopen (input_file_name, "r"); diff --git a/notmuch-dump.c b/notmuch-dump.c index 9c6ad7f4..fab22bdd 100644 --- a/notmuch-dump.c +++ b/notmuch-dump.c @@ -228,6 +228,7 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[]) { 0, 0 } } }, { NOTMUCH_OPT_STRING, &output_file_name, "output", 'o', 0 }, { NOTMUCH_OPT_BOOLEAN, &gzip_output, "gzip", 'z', 0 }, + { NOTMUCH_OPT_INHERIT, (void *) ¬much_shared_options, NULL, 0, 0 }, { 0, 0, 0, 0, 0 } }; @@ -235,6 +236,8 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[]) if (opt_index < 0) return EXIT_FAILURE; + notmuch_process_shared_options (argv[0]); + if (opt_index < argc) { query_str = query_string_from_args (notmuch, argc - opt_index, argv + opt_index); if (query_str == NULL) { diff --git a/notmuch-insert.c b/notmuch-insert.c index 90fe3bad..c277d620 100644 --- a/notmuch-insert.c +++ b/notmuch-insert.c @@ -466,6 +466,7 @@ notmuch_insert_command (notmuch_config_t *config, int argc, char *argv[]) { NOTMUCH_OPT_BOOLEAN, &create_folder, "create-folder", 0, 0 }, { NOTMUCH_OPT_BOOLEAN, &keep, "keep", 0, 0 }, { NOTMUCH_OPT_BOOLEAN, &no_hooks, "no-hooks", 'n', 0 }, + { NOTMUCH_OPT_INHERIT, (void *) ¬much_shared_options, NULL, 0, 0 }, { NOTMUCH_OPT_END, 0, 0, 0, 0 } }; @@ -473,6 +474,8 @@ notmuch_insert_command (notmuch_config_t *config, int argc, char *argv[]) if (opt_index < 0) return EXIT_FAILURE; + notmuch_process_shared_options (argv[0]); + db_path = notmuch_config_get_database_path (config); new_tags = notmuch_config_get_new_tags (config, &new_tags_length); synchronize_flags = notmuch_config_get_maildir_synchronize_flags (config); @@ -521,7 +524,7 @@ notmuch_insert_command (notmuch_config_t *config, int argc, char *argv[]) return EXIT_FAILURE; } - /* Setup our handler for SIGINT. We do not set SA_RESTART so that copying + /* Set up our handler for SIGINT. We do not set SA_RESTART so that copying * from standard input may be interrupted. */ memset (&action, 0, sizeof (struct sigaction)); action.sa_handler = handle_sigint; diff --git a/notmuch-new.c b/notmuch-new.c index e6c283eb..8ff1ade7 100644 --- a/notmuch-new.c +++ b/notmuch-new.c @@ -662,7 +662,7 @@ setup_progress_printing_timer (void) struct sigaction action; struct itimerval timerval; - /* Setup our handler for SIGALRM */ + /* Set up our handler for SIGALRM */ memset (&action, 0, sizeof (struct sigaction)); action.sa_handler = handle_sigalrm; sigemptyset (&action.sa_mask); @@ -934,6 +934,7 @@ notmuch_new_command (notmuch_config_t *config, int argc, char *argv[]) { NOTMUCH_OPT_BOOLEAN, &verbose, "verbose", 'v', 0 }, { NOTMUCH_OPT_BOOLEAN, &add_files_state.debug, "debug", 'd', 0 }, { NOTMUCH_OPT_BOOLEAN, &no_hooks, "no-hooks", 'n', 0 }, + { NOTMUCH_OPT_INHERIT, (void *) ¬much_shared_options, NULL, 0, 0 }, { 0, 0, 0, 0, 0 } }; @@ -941,6 +942,8 @@ notmuch_new_command (notmuch_config_t *config, int argc, char *argv[]) if (opt_index < 0) return EXIT_FAILURE; + notmuch_process_shared_options (argv[0]); + /* quiet trumps verbose */ if (quiet) add_files_state.verbosity = VERBOSITY_QUIET; @@ -1047,7 +1050,7 @@ notmuch_new_command (notmuch_config_t *config, int argc, char *argv[]) if (notmuch == NULL) return EXIT_FAILURE; - /* Setup our handler for SIGINT. We do this after having + /* Set up our handler for SIGINT. We do this after having * potentially done a database upgrade we this interrupt handler * won't support. */ memset (&action, 0, sizeof (struct sigaction)); diff --git a/notmuch-reply.c b/notmuch-reply.c index d51fdfc3..4464741f 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -790,6 +790,7 @@ notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[]) { "sender", FALSE }, { 0, 0 } } }, { NOTMUCH_OPT_BOOLEAN, ¶ms.crypto.decrypt, "decrypt", 'd', 0 }, + { NOTMUCH_OPT_INHERIT, (void *) ¬much_shared_options, NULL, 0, 0 }, { 0, 0, 0, 0, 0 } }; @@ -797,6 +798,8 @@ notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[]) if (opt_index < 0) return EXIT_FAILURE; + notmuch_process_shared_options (argv[0]); + if (format == FORMAT_HEADERS_ONLY) { reply_format_func = notmuch_reply_format_headers_only; } else if (format == FORMAT_JSON) { diff --git a/notmuch-restore.c b/notmuch-restore.c index 584d9f96..2a534dc4 100644 --- a/notmuch-restore.c +++ b/notmuch-restore.c @@ -154,6 +154,7 @@ notmuch_restore_command (notmuch_config_t *config, int argc, char *argv[]) { 0, 0 } } }, { NOTMUCH_OPT_STRING, &input_file_name, "input", 'i', 0 }, { NOTMUCH_OPT_BOOLEAN, &accumulate, "accumulate", 'a', 0 }, + { NOTMUCH_OPT_INHERIT, (void *) ¬much_shared_options, NULL, 0, 0 }, { 0, 0, 0, 0, 0 } }; @@ -163,6 +164,7 @@ notmuch_restore_command (notmuch_config_t *config, int argc, char *argv[]) goto DONE; } + notmuch_process_shared_options (argv[0]); name_for_error = input_file_name ? input_file_name : "stdin"; if (! accumulate) diff --git a/notmuch-search.c b/notmuch-search.c index b81ac013..b89a17e5 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -681,6 +681,7 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) { NOTMUCH_OPT_INT, &ctx->limit, "limit", 'L', 0 }, { NOTMUCH_OPT_INT, &ctx->dupe, "duplicate", 'D', 0 }, { NOTMUCH_OPT_INHERIT, (void *) &common_options, NULL, 0, 0 }, + { NOTMUCH_OPT_INHERIT, (void *) ¬much_shared_options, NULL, 0, 0 }, { 0, 0, 0, 0, 0 } }; @@ -689,6 +690,8 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[]) if (opt_index < 0) return EXIT_FAILURE; + notmuch_process_shared_options (argv[0]); + if (ctx->output != OUTPUT_FILES && ctx->output != OUTPUT_MESSAGES && ctx->dupe != -1) { fprintf (stderr, "Error: --duplicate=N is only supported with --output=files and --output=messages.\n"); @@ -737,6 +740,7 @@ notmuch_address_command (notmuch_config_t *config, int argc, char *argv[]) { "false", NOTMUCH_EXCLUDE_FALSE }, { 0, 0 } } }, { NOTMUCH_OPT_INHERIT, (void *) &common_options, NULL, 0, 0 }, + { NOTMUCH_OPT_INHERIT, (void *) ¬much_shared_options, NULL, 0, 0 }, { 0, 0, 0, 0, 0 } }; @@ -744,6 +748,8 @@ notmuch_address_command (notmuch_config_t *config, int argc, char *argv[]) if (opt_index < 0) return EXIT_FAILURE; + notmuch_process_shared_options (argv[0]); + if (! (ctx->output & (OUTPUT_SENDER | OUTPUT_RECIPIENTS))) ctx->output |= OUTPUT_SENDER; diff --git a/notmuch-setup.c b/notmuch-setup.c index 36a6171a..7dd5822a 100644 --- a/notmuch-setup.c +++ b/notmuch-setup.c @@ -145,6 +145,9 @@ notmuch_setup_command (notmuch_config_t *config, chomp_newline (response); \ } while (0) + if (notmuch_minimal_options ("setup", argc, argv) < 0) + return EXIT_FAILURE; + if (notmuch_config_is_new (config)) welcome_message_pre_setup (); diff --git a/notmuch-show.c b/notmuch-show.c index 43bf71c8..b80933ad 100644 --- a/notmuch-show.c +++ b/notmuch-show.c @@ -1114,6 +1114,7 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[]) { NOTMUCH_OPT_BOOLEAN, ¶ms.crypto.verify, "verify", 'v', 0 }, { NOTMUCH_OPT_BOOLEAN, ¶ms.output_body, "body", 'b', 0 }, { NOTMUCH_OPT_BOOLEAN, ¶ms.include_html, "include-html", 0, 0 }, + { NOTMUCH_OPT_INHERIT, (void *) ¬much_shared_options, NULL, 0, 0 }, { 0, 0, 0, 0, 0 } }; @@ -1121,6 +1122,8 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[]) if (opt_index < 0) return EXIT_FAILURE; + notmuch_process_shared_options (argv[0]); + /* decryption implies verification */ if (params.crypto.decrypt) params.crypto.verify = TRUE; diff --git a/notmuch-tag.c b/notmuch-tag.c index 5b2f1e48..38d99aa9 100644 --- a/notmuch-tag.c +++ b/notmuch-tag.c @@ -195,7 +195,7 @@ notmuch_tag_command (notmuch_config_t *config, int argc, char *argv[]) int opt_index; int ret; - /* Setup our handler for SIGINT */ + /* Set up our handler for SIGINT */ memset (&action, 0, sizeof (struct sigaction)); action.sa_handler = handle_sigint; sigemptyset (&action.sa_mask); @@ -206,6 +206,7 @@ notmuch_tag_command (notmuch_config_t *config, int argc, char *argv[]) { NOTMUCH_OPT_BOOLEAN, &batch, "batch", 0, 0 }, { NOTMUCH_OPT_STRING, &input_file_name, "input", 'i', 0 }, { NOTMUCH_OPT_BOOLEAN, &remove_all, "remove-all", 0, 0 }, + { NOTMUCH_OPT_INHERIT, (void *) ¬much_shared_options, NULL, 0, 0 }, { 0, 0, 0, 0, 0 } }; @@ -213,6 +214,8 @@ notmuch_tag_command (notmuch_config_t *config, int argc, char *argv[]) if (opt_index < 0) return EXIT_FAILURE; + notmuch_process_shared_options (argv[0]); + if (input_file_name) { batch = TRUE; input = fopen (input_file_name, "r"); diff --git a/notmuch.c b/notmuch.c index a5b2877a..c528dce2 100644 --- a/notmuch.c +++ b/notmuch.c @@ -43,11 +43,62 @@ notmuch_help_command (notmuch_config_t *config, int argc, char *argv[]); static int notmuch_command (notmuch_config_t *config, int argc, char *argv[]); +static int +_help_for (const char *topic); + +static notmuch_bool_t print_version = FALSE, print_help = FALSE; + +const notmuch_opt_desc_t notmuch_shared_options [] = { + { NOTMUCH_OPT_BOOLEAN, &print_version, "version", 'v', 0 }, + { NOTMUCH_OPT_BOOLEAN, &print_help, "help", 'h', 0 }, + {0, 0, 0, 0, 0} +}; + +/* any subcommand wanting to support these options should call + * inherit notmuch_shared_options and call + * notmuch_process_shared_options (subcommand_name); + */ +void +notmuch_process_shared_options (const char *subcommand_name) { + if (print_version) { + printf ("notmuch " STRINGIFY(NOTMUCH_VERSION) "\n"); + exit (EXIT_SUCCESS); + } + + if (print_help) { + int ret = _help_for (subcommand_name); + exit (ret); + } +} + +/* This is suitable for subcommands that do not actually open the + * database. + */ +int notmuch_minimal_options (const char *subcommand_name, + int argc, char **argv) +{ + int opt_index; + + notmuch_opt_desc_t options[] = { + { NOTMUCH_OPT_INHERIT, (void *) ¬much_shared_options, NULL, 0, 0 }, + { 0, 0, 0, 0, 0 } + }; + + opt_index = parse_arguments (argc, argv, options, 1); + + if (opt_index < 0) + return -1; + + /* We can't use argv here as it is sometimes NULL */ + notmuch_process_shared_options (subcommand_name); + return opt_index; +} + static command_t commands[] = { { NULL, notmuch_command, TRUE, "Notmuch main command." }, { "setup", notmuch_setup_command, TRUE, - "Interactively setup notmuch for first use." }, + "Interactively set up notmuch for first use." }, { "new", notmuch_new_command, FALSE, "Find and import new messages to the notmuch database." }, { "insert", notmuch_insert_command, FALSE, @@ -177,21 +228,19 @@ exec_man (const char *page) } static int -notmuch_help_command (notmuch_config_t *config, int argc, char *argv[]) +_help_for (const char *topic_name) { command_t *command; help_topic_t *topic; unsigned int i; - argc--; argv++; /* Ignore "help" */ - - if (argc == 0) { + if (!topic_name) { printf ("The notmuch mail system.\n\n"); usage (stdout); return EXIT_SUCCESS; } - if (strcmp (argv[0], "help") == 0) { + if (strcmp (topic_name, "help") == 0) { printf ("The notmuch help system.\n\n" "\tNotmuch uses the man command to display help. In case\n" "\tof difficulties check that MANPATH includes the pages\n" @@ -200,26 +249,46 @@ notmuch_help_command (notmuch_config_t *config, int argc, char *argv[]) return EXIT_SUCCESS; } - command = find_command (argv[0]); + command = find_command (topic_name); if (command) { - char *page = talloc_asprintf (config, "notmuch-%s", command->name); + char *page = talloc_asprintf (NULL, "notmuch-%s", command->name); exec_man (page); } for (i = 0; i < ARRAY_SIZE (help_topics); i++) { topic = &help_topics[i]; - if (strcmp (argv[0], topic->name) == 0) { - char *page = talloc_asprintf (config, "notmuch-%s", topic->name); + if (strcmp (topic_name, topic->name) == 0) { + char *page = talloc_asprintf (NULL, "notmuch-%s", topic->name); exec_man (page); } } fprintf (stderr, "\nSorry, %s is not a known command. There's not much I can do to help.\n\n", - argv[0]); + topic_name); return EXIT_FAILURE; } +static int +notmuch_help_command (unused (notmuch_config_t * config), int argc, char *argv[]) +{ + int opt_index; + + opt_index = notmuch_minimal_options ("help", argc, argv); + if (opt_index < 0) + return EXIT_FAILURE; + + /* skip at least subcommand argument */ + argc-= opt_index; + argv+= opt_index; + + if (argc == 0) { + return _help_for (NULL); + } + + return _help_for (argv[0]); +} + /* Handle the case of "notmuch" being invoked with no command * argument. For now we just call notmuch_setup_command, but we plan * to be more clever about this in the future. @@ -285,14 +354,12 @@ main (int argc, char *argv[]) command_t *command; char *config_file_name = NULL; notmuch_config_t *config = NULL; - notmuch_bool_t print_help=FALSE, print_version=FALSE; int opt_index; int ret; notmuch_opt_desc_t options[] = { - { NOTMUCH_OPT_BOOLEAN, &print_help, "help", 'h', 0 }, - { NOTMUCH_OPT_BOOLEAN, &print_version, "version", 'v', 0 }, { NOTMUCH_OPT_STRING, &config_file_name, "config", 'c', 0 }, + { NOTMUCH_OPT_INHERIT, (void *) ¬much_shared_options, NULL, 0, 0 }, { 0, 0, 0, 0, 0 } }; @@ -314,24 +381,7 @@ main (int argc, char *argv[]) goto DONE; } - /* Handle notmuch --help [command] and notmuch command --help. */ - if (print_help || - (opt_index + 1 < argc && strcmp (argv[opt_index + 1], "--help") == 0)) { - /* - * Pass the first positional argument as argv[1] so the help - * command can give help for it. The help command ignores the - * argv[0] passed to it. - */ - ret = notmuch_help_command (NULL, argc - opt_index + 1, - argv + opt_index - 1); - goto DONE; - } - - if (print_version) { - printf ("notmuch " STRINGIFY(NOTMUCH_VERSION) "\n"); - ret = EXIT_SUCCESS; - goto DONE; - } + notmuch_process_shared_options (NULL); if (opt_index < argc) command_name = argv[opt_index]; diff --git a/performance-test/perf-test-lib.sh b/performance-test/perf-test-lib.sh index 75e3d878..88601fc7 100644 --- a/performance-test/perf-test-lib.sh +++ b/performance-test/perf-test-lib.sh @@ -203,7 +203,7 @@ time_done () fi } -cd -P "$test" || error "Cannot setup test environment" +cd -P "$test" || error "Cannot set up test environment" test_failure=0 test_count=0 diff --git a/test/T010-help-test.sh b/test/T010-help-test.sh index caf8bdb0..d7266ffc 100755 --- a/test/T010-help-test.sh +++ b/test/T010-help-test.sh @@ -12,9 +12,9 @@ if [ $NOTMUCH_HAVE_MAN -eq 1 ]; then test_expect_success 'notmuch help tag' 'notmuch help tag' else test_expect_success 'notmuch --help tag (man pages not available)' \ - 'test_must_fail notmuch --help tag' + 'test_must_fail notmuch --help tag >/dev/null' test_expect_success 'notmuch help tag (man pages not available)' \ - 'test_must_fail notmuch help tag' + 'test_must_fail notmuch help tag >/dev/null' fi test_done diff --git a/test/T070-insert.sh b/test/T070-insert.sh index 74f19552..7e71c3b1 100755 --- a/test/T070-insert.sh +++ b/test/T070-insert.sh @@ -188,7 +188,7 @@ notmuch config set new.tags $OLDCONFIG # DUPLICATE_MESSAGE_ID is not tested here, because it should actually pass. for code in OUT_OF_MEMORY XAPIAN_EXCEPTION FILE_NOT_EMAIL \ - READ_ONLY_DATABASE UPGRADE_REQUIRED; do + READ_ONLY_DATABASE UPGRADE_REQUIRED PATH_ERROR; do gen_insert_msg cat < index-file-$code.gdb set breakpoint pending on diff --git a/test/T360-symbol-hiding.sh b/test/T360-symbol-hiding.sh index d2b5d1f5..98e4d4dc 100755 --- a/test/T360-symbol-hiding.sh +++ b/test/T360-symbol-hiding.sh @@ -11,16 +11,17 @@ test_description='exception symbol hiding' . ./test-lib.sh -run_test(){ - result=$(LD_LIBRARY_PATH="$TEST_DIRECTORY/../lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" $TEST_DIRECTORY/symbol-test 2>&1) -} - -output="A Xapian exception occurred opening database: Couldn't stat 'fakedb/.notmuch/xapian' -caught No chert database found at path \`./nonexistent'" - -mkdir -p fakedb/.notmuch - -test_expect_success 'running test' run_test +test_begin_subtest 'running test' run_test +mkdir -p ${PWD}/fakedb/.notmuch +( LD_LIBRARY_PATH="$TEST_DIRECTORY/../lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" \ + $TEST_DIRECTORY/symbol-test ${PWD}/fakedb ${PWD}/nonexistent \ + 2>&1 | sed "s,${PWD},CWD,g") > OUTPUT + +cat < EXPECTED +A Xapian exception occurred opening database: Couldn't stat 'CWD/fakedb/.notmuch/xapian' +caught No chert database found at path \`CWD/nonexistent' +EOF +test_expect_equal_file EXPECTED OUTPUT test_begin_subtest 'checking output' test_expect_equal "$result" "$output" diff --git a/test/T390-python.sh b/test/T390-python.sh index 3f03a2e3..26d0b976 100755 --- a/test/T390-python.sh +++ b/test/T390-python.sh @@ -11,7 +11,7 @@ db = notmuch.Database(mode=notmuch.Database.MODE.READ_ONLY) q_new = notmuch.Query(db, 'tag:inbox') q_new.set_sort(notmuch.Query.SORT.OLDEST_FIRST) for t in q_new.search_threads(): - print t.get_thread_id() + print (t.get_thread_id()) EOF notmuch search --sort=oldest-first --output=threads tag:inbox | sed s/^thread:// > EXPECTED test_expect_equal_file OUTPUT EXPECTED @@ -23,7 +23,7 @@ db = notmuch.Database(mode=notmuch.Database.MODE.READ_ONLY) q_new = notmuch.Query(db, 'tag:inbox') q_new.set_sort(notmuch.Query.SORT.OLDEST_FIRST) for m in q_new.search_messages(): - print m.get_message_id() + print (m.get_message_id()) EOF notmuch search --sort=oldest-first --output=messages tag:inbox | sed s/^id:// > EXPECTED test_expect_equal_file OUTPUT EXPECTED @@ -32,7 +32,7 @@ test_begin_subtest "get non-existent file" test_python < EXPECTED +test_expect_equal_file OUTPUT EXPECTED + +test_begin_subtest "compare message ids" +test_ruby <<"EOF" +require 'notmuch' +$maildir = ENV['MAIL_DIR'] +if not $maildir then + abort('environment variable MAIL_DIR must be set') +end +@db = Notmuch::Database.new($maildir) +@q = @db.query('tag:inbox') +@q.sort = Notmuch::SORT_OLDEST_FIRST +for m in @q.search_messages do + print m.message_id, "\n" +end +EOF +notmuch search --sort=oldest-first --output=messages tag:inbox | sed s/^id:// > EXPECTED +test_expect_equal_file OUTPUT EXPECTED + +test_begin_subtest "get non-existent file" +test_ruby <<"EOF" +require 'notmuch' +$maildir = ENV['MAIL_DIR'] +if not $maildir then + abort('environment variable MAIL_DIR must be set') +end +@db = Notmuch::Database.new($maildir) +result = @db.find_message_by_filename('i-dont-exist') +print (result == nil) +EOF +test_expect_equal "$(cat OUTPUT)" "true" + +test_begin_subtest "count messages" +test_ruby <<"EOF" +require 'notmuch' +$maildir = ENV['MAIL_DIR'] +if not $maildir then + abort('environment variable MAIL_DIR must be set') +end +@db = Notmuch::Database.new($maildir) +@q = @db.query('tag:inbox') +print @q.count_messages(),"\n" +EOF +notmuch count --output=messages tag:inbox > EXPECTED +test_expect_equal_file OUTPUT EXPECTED + +test_begin_subtest "count threads" +test_ruby <<"EOF" +require 'notmuch' +$maildir = ENV['MAIL_DIR'] +if not $maildir then + abort('environment variable MAIL_DIR must be set') +end +@db = Notmuch::Database.new($maildir) +@q = @db.query('tag:inbox') +print @q.count_threads(),"\n" +EOF +notmuch count --output=threads tag:inbox > EXPECTED +test_expect_equal_file OUTPUT EXPECTED + +test_done diff --git a/test/T560-lib-error.sh b/test/T560-lib-error.sh index c99b17ed..b1e77aa0 100755 --- a/test/T560-lib-error.sh +++ b/test/T560-lib-error.sh @@ -35,7 +35,7 @@ Error: Cannot open a database for a NULL path. EOF test_expect_equal_file EXPECTED OUTPUT -test_begin_subtest "Open nonexistent database" +test_begin_subtest "Open relative path" test_C <<'EOF' #include #include @@ -49,7 +49,43 @@ EOF cat <<'EOF' >EXPECTED == stdout == == stderr == -Error opening database at ./nonexistent/foo/.notmuch: No such file or directory +Error: Database path must be absolute. +EOF +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "Create database in relative path" +test_C <<'EOF' +#include +#include +int main (int argc, char** argv) +{ + notmuch_database_t *db; + notmuch_status_t stat; + stat = notmuch_database_create ("./nonexistent/foo", &db); +} +EOF +cat <<'EOF' >EXPECTED +== stdout == +== stderr == +Error: Database path must be absolute. +EOF +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "Open nonexistent database" +test_C ${PWD}/nonexistent/foo <<'EOF' +#include +#include +int main (int argc, char** argv) +{ + notmuch_database_t *db; + notmuch_status_t stat; + stat = notmuch_database_open (argv[1], 0, 0); +} +EOF +cat <<'EOF' >EXPECTED +== stdout == +== stderr == +Error opening database at CWD/nonexistent/foo/.notmuch: No such file or directory EOF test_expect_equal_file EXPECTED OUTPUT @@ -70,21 +106,21 @@ Error: Cannot create a database for a NULL path. EOF test_expect_equal_file EXPECTED OUTPUT -test_begin_subtest "Create database in non-existant directory" -test_C <<'EOF' +test_begin_subtest "Create database in nonexistent directory" +test_C ${PWD}/nonexistent/foo<<'EOF' #include #include int main (int argc, char** argv) { notmuch_database_t *db; notmuch_status_t stat; - stat = notmuch_database_create ("./nonexistent/foo", &db); + stat = notmuch_database_create (argv[1], &db); } EOF cat <<'EOF' >EXPECTED == stdout == == stderr == -Error: Cannot create database at ./nonexistent/foo: No such file or directory. +Error: Cannot create database at CWD/nonexistent/foo: No such file or directory. EOF test_expect_equal_file EXPECTED OUTPUT diff --git a/test/random-corpus.c b/test/random-corpus.c index 790193d2..b377eb40 100644 --- a/test/random-corpus.c +++ b/test/random-corpus.c @@ -114,6 +114,23 @@ random_utf8_string (void *ctx, size_t char_count) return buf; } +/* stubs since we cannot link with notmuch.o */ +const notmuch_opt_desc_t notmuch_shared_options[] = { + { 0, 0, 0, 0, 0 } +}; + +void +notmuch_process_shared_options (unused (const char *dummy)) +{ +} + +int +notmuch_minimal_options (unused (const char *subcommand), + unused (int argc), + unused (char **argv)) +{ + return 0; +} int main (int argc, char **argv) diff --git a/test/symbol-test.cc b/test/symbol-test.cc index f17ddc85..fb77b418 100644 --- a/test/symbol-test.cc +++ b/test/symbol-test.cc @@ -4,18 +4,18 @@ #include -int main() { +int main(int argc, char** argv) { notmuch_database_t *notmuch; char *message = NULL; - if (notmuch_database_open_verbose ("fakedb", NOTMUCH_DATABASE_MODE_READ_ONLY, ¬much, &message)) + if (notmuch_database_open_verbose (argv[1], NOTMUCH_DATABASE_MODE_READ_ONLY, ¬much, &message)) if (message) { fputs (message, stderr); free (message); } try { - (void) new Xapian::WritableDatabase("./nonexistent", Xapian::DB_OPEN); + (void) new Xapian::WritableDatabase(argv[2], Xapian::DB_OPEN); } catch (const Xapian::Error &error) { printf("caught %s\n", error.get_msg().c_str()); return 0; diff --git a/test/test-lib.sh b/test/test-lib.sh index 486d1c43..3466e9cf 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -1163,6 +1163,11 @@ test_python() { | $cmd - } +test_ruby() { + export LD_LIBRARY_PATH=$TEST_DIRECTORY/../lib + MAIL_DIR=$MAIL_DIR ruby -I $TEST_DIRECTORY/../bindings/ruby> OUTPUT +} + test_C () { exec_file="test${test_count}" test_file="${exec_file}.c" @@ -1231,7 +1236,7 @@ emacs_generate_script # Use -P to resolve symlinks in our working directory so that the cwd # in subprocesses like git equals our $PWD (for pathname comparisons). -cd -P "$test" || error "Cannot setup test environment" +cd -P "$test" || error "Cannot set up test environment" if test "$verbose" = "t" then