[PATCH 1/1] NEWS: correction to section 0.15 for markdown processing
At the end of section 0.15 the strings TEST_EMACS & TEST_EMACSCLIENT are now put inside ``:s for better output after markdown processing. --- NEWS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 1cb52dd..9d6bd19 100644 --- a/NEWS +++ b/NEWS @@ -182,8 +182,8 @@ Internal test framework changes The emacsclient binary is now user-configurable - The test framework now accepts TEST_EMACSCLIENT in addition to - TEST_EMACS for configuring the emacsclient to use. This is + The test framework now accepts `TEST_EMACSCLIENT` in addition to + `TEST_EMACS` for configuring the emacsclient to use. This is necessary to avoid using an old emacsclient with a new emacs, which can result in buggy behavior. -- 1.8.0
[PATCH 1/1] test/test-lib.sh: use $test_subtest_name in all tests
Set the variable '$test_subtest_name' in all functions which starts a new test and use that variable in all functions that output test results. Additionally output the latest '$test_subtest_name' in case of abnormal exit, to avoid confusion. --- The main reason to do this change is to get latest '$test_subtest_name' printed in case of abnormal exit. I cherry-picked this change from a larger work-in-progress patch set that adds 'set -e -o pipefail' support... I am pretty sure I got all the cases covered. If not, we'll notice it later when some test fail in a way I could not anticipate. Anyway, tests success & fail as they used to be. test/test-lib.sh | 52 +--- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/test/test-lib.sh b/test/test-lib.sh index 6ce3b31..58e7b93 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -197,7 +197,11 @@ die () { then exit $code else - echo >&5 "FATAL: Unexpected exit with code $code" + exec 5>&1 + say_color error '%-6s' FATAL + echo " $test_subtest_name" + echo + echo "Unexpected exit while executing $0. Exit code $code." exit 1 fi } @@ -494,12 +498,12 @@ test_expect_equal () if ! test_skip "$test_subtest_name" then if [ "$output" = "$expected" ]; then - test_ok_ "$test_subtest_name" + test_ok_ else testname=$this_test.$test_count echo "$expected" > $testname.expected echo "$output" > $testname.output - test_failure_ "$test_subtest_name" "$(diff -u $testname.expected $testname.output)" + test_failure_ "$(diff -u $testname.expected $testname.output)" fi fi } @@ -520,12 +524,12 @@ test_expect_equal_file () if ! test_skip "$test_subtest_name" then if diff -q "$file1" "$file2" >/dev/null ; then - test_ok_ "$test_subtest_name" + test_ok_ else testname=$this_test.$test_count cp "$file1" "$testname.$basename1" cp "$file2" "$testname.$basename2" - test_failure_ "$test_subtest_name" "$(diff -u "$testname.$basename1" "$testname.$basename2")" + test_failure_ "$(diff -u "$testname.$basename1" "$testname.$basename2")" fi fi } @@ -563,9 +567,9 @@ test_emacs_expect_t () { result=$(cat OUTPUT) if [ "$result" = t ] then - test_ok_ "$test_subtest_name" + test_ok_ else - test_failure_ "$test_subtest_name" "${result}" + test_failure_ "${result}" fi else # Restore state after the (non) test. @@ -666,12 +670,12 @@ test_require_external_prereq () { test_ok_ () { if test "$test_subtest_known_broken_" = "t"; then - test_known_broken_ok_ "$@" + test_known_broken_ok_ return fi test_success=$(($test_success + 1)) say_color pass "%-6s" "PASS" - echo " $@" + echo " $test_subtest_name" } test_failure_ () { @@ -680,7 +684,7 @@ test_failure_ () { return fi test_failure=$(($test_failure + 1)) - test_failure_message_ "FAIL" "$@" + test_failure_message_ "FAIL" "$test_subtest_name" "$@" test "$immediate" = "" || { GIT_EXIT_OK=t; exit 1; } return 1 } @@ -697,13 +701,13 @@ test_known_broken_ok_ () { test_reset_state_ test_fixed=$(($test_fixed+1)) say_color pass "%-6s" "FIXED" - echo " $@" + echo " $test_subtest_name" } test_known_broken_failure_ () { test_reset_state_ test_broken=$(($test_broken+1)) - test_failure_message_ "BROKEN" "$@" + test_failure_message_ "BROKEN" "$test_subtest_name" "$@" return 1 } @@ -771,6 +775,7 @@ test_expect_success () { test "$#" = 3 && { prereq=$1; shift; } || prereq= test "$#" = 2 || error "bug in the test script: not 2 or 3 parameters to test-expect-success" + test_subtest_name="$1" test_reset_state_ if ! test_skip "$@" then @@ -780,9 +785,9 @@ test_expect_success () { test_check_missing_external_prereqs_ "$@" || if [ "$run_ret" = 0 -a "$eval_ret" = 0 ] then - test_ok_ "$1" + test_ok_ else - test_failure_ "$@" + test_failure_ "$2" fi fi } @@ -791,6 +796,7 @@
[PATCH 5/7] CLI: add --leak-report top level option
On Sat, Jan 19 2013, david at tethera.net wrote: > This roughly mimics the samba4 argument. The presence of the command > line argument overrides any value of NOTMUCH_TALLOC_REPORT in the > environment. > --- > man/man1/notmuch.1 |8 > notmuch.c | 18 +++--- > 2 files changed, 15 insertions(+), 11 deletions(-) > > diff --git a/man/man1/notmuch.1 b/man/man1/notmuch.1 > index 6bf9b2e..5c58c41 100644 > --- a/man/man1/notmuch.1 > +++ b/man/man1/notmuch.1 > @@ -70,6 +70,14 @@ Print a synopsis of available commands and exit. > Print the installed version of notmuch, and exit. > .RE > > +.RS 4 > +.TP 4 > +.BI \-\-leak-report= path > + > +Write a detailed report of all memory allocated via talloc to > +.I path > +.RE Do we really need a command line option for this? Why isn't the env var sufficient? This just seems to me like it clutters the interface, for an option that is purely for debugging and should rarely if ever be used by most users. jamie. -- next part -- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 835 bytes Desc: not available URL: <http://notmuchmail.org/pipermail/notmuch/attachments/20130120/aeb8119f/attachment.pgp>
[PATCH v3 20/20] test: add tests for insert
Add tests for new 'insert' command. --- test/insert | 106 ++ test/notmuch-test | 1 + 2 files changed, 107 insertions(+) create mode 100755 test/insert diff --git a/test/insert b/test/insert new file mode 100755 index 000..a3b6283 --- /dev/null +++ b/test/insert @@ -0,0 +1,106 @@ +#!/usr/bin/env bash +test_description='"notmuch insert"' +. ./test-lib.sh + +# Create directories and database before inserting. +mkdir -p "$MAIL_DIR"/{cur,new,tmp} +mkdir -p "$MAIL_DIR"/Drafts/{cur,new,tmp} +notmuch new > /dev/null + +# We use generate_message to create the temporary message files. +# They happen to be in the mail directory already but that is okay +# since we do not call notmuch new hereafter. + +gen_insert_msg() { +generate_message \ + "[subject]=\"insert-subject\"" \ + "[date]=\"Sat, 01 Jan 2000 12:00:00 -\"" \ + "[body]=\"insert-message\"" +} + +test_begin_subtest "Insert message, copied exactly" +gen_insert_msg +notmuch insert < "$gen_msg_filename" +cur_msg_filename=$(notmuch search --output=files "subject:insert-subject") +test_expect_equal_file "$cur_msg_filename" "$gen_msg_filename" + +test_begin_subtest "Insert message, default tags" +output=$(notmuch show --format=json "subject:insert-subject") +expected='[[[{ + "id": "'"${gen_msg_id}"'", + "match": true, + "excluded": false, + "filename": "'"${cur_msg_filename}"'", + "timestamp": 946728000, + "date_relative": "2000-01-01", + "tags": ["inbox","unread"], + "headers": { + "Subject": "insert-subject", + "From": "Notmuch Test Suite ", + "To": "Notmuch Test Suite ", + "Date": "Sat, 01 Jan 2000 12:00:00 +"}, + "body": [{"id": 1, + "content-type": "text/plain", + "content": "insert-message\n"}]}, + [' +test_expect_equal_json "$output" "$expected" + +test_begin_subtest "Insert message, duplicate message" +notmuch insert < "$gen_msg_filename" +output=$(notmuch search --output=files "subject:insert-subject" | wc -l) +test_expect_equal "$output" 2 + +test_begin_subtest "Insert message, add tag" +gen_insert_msg +notmuch insert +custom < "$gen_msg_filename" +output=$(notmuch count tag:custom) +test_expect_equal "$output" "1" + +test_begin_subtest "Insert message, add/remove tag" +gen_insert_msg +notmuch insert +custom -unread < "$gen_msg_filename" +output=$(notmuch count tag:custom NOT tag:unread) +test_expect_equal "$output" "1" + +test_begin_subtest "Insert message, folder" +gen_insert_msg +notmuch insert --folder=Drafts < "$gen_msg_filename" +output=$(notmuch search --output=files folder:Drafts) +dirname=$(dirname "$output") +test_expect_equal "$dirname" "$MAIL_DIR/Drafts/cur" + +test_begin_subtest "Insert message, folder and tags" +gen_insert_msg +notmuch insert --folder=Drafts +draft -unread < "$gen_msg_filename" +output=$(notmuch count folder:Drafts tag:draft NOT tag:unread) +test_expect_equal "$output" "1" + +gen_insert_msg +test_expect_code 1 "Insert message, non-existent folder" \ +"notmuch insert --folder=nonesuch < $gen_msg_filename" + +test_begin_subtest "Insert message, create folder" +gen_insert_msg +notmuch insert --folder=F --create-folder +folder < "$gen_msg_filename" +output=$(notmuch search --output=files folder:F tag:folder) +basename=$(basename "$output") +test_expect_equal_file "$gen_msg_filename" "$MAIL_DIR/F/cur/${basename}" + +test_begin_subtest "Insert message, create subfolder" +gen_insert_msg +notmuch insert --folder=F/G/H/I/J --create-folder +folder < "$gen_msg_filename" +output=$(notmuch search --output=files folder:F/G/H/I/J tag:folder) +basename=$(basename "$output") +test_expect_equal_file "$gen_msg_filename" "${MAIL_DIR}/F/G/H/I/J/cur/${basename}" + +test_begin_subtest "Insert message, create existing subfolder" +gen_insert_msg +notmuch insert --folder=F/G/H/I/J --create-folder +folder < "$gen_msg_filename" +output=$(notmuch count folder:F/G/H/I/J tag:folder) +test_expect_equal "$output" "2" + +gen_insert_msg +test_expect_code 1 "Insert message, create invalid subfolder" \ +"notmuch insert --folder=../G --create-folder $gen_msg_filename" + +test_done diff --git a/test/notmuch-test b/test/notmuch-test index ca9c3dc..6952f0a 100755 --- a/test/notmuch-test +++ b/test/notmuch-test @@ -22,6 +22,7 @@ TESTS=" config new count + insert search search-output search-by-folder -- 1.7.12.1
[PATCH v3 19/20] man: reference notmuch-insert.1
Add references to notmuch-insert.1 from other man pages. --- man/man1/notmuch-config.1 | 4 ++-- man/man1/notmuch-count.1| 4 ++-- man/man1/notmuch-dump.1 | 4 ++-- man/man1/notmuch-new.1 | 4 ++-- man/man1/notmuch-reply.1| 3 ++- man/man1/notmuch-restore.1 | 3 ++- man/man1/notmuch-search.1 | 3 ++- man/man1/notmuch-show.1 | 3 ++- man/man1/notmuch-tag.1 | 3 ++- man/man1/notmuch.1 | 3 ++- man/man5/notmuch-hooks.5| 4 ++-- man/man7/notmuch-search-terms.7 | 3 ++- 12 files changed, 24 insertions(+), 17 deletions(-) diff --git a/man/man1/notmuch-config.1 b/man/man1/notmuch-config.1 index 557eae5..e6d960d 100644 --- a/man/man1/notmuch-config.1 +++ b/man/man1/notmuch-config.1 @@ -152,7 +152,7 @@ use ${HOME}/.notmuch\-config if this variable is not set. .SH SEE ALSO \fBnotmuch\fR(1), \fBnotmuch-count\fR(1), \fBnotmuch-dump\fR(1), -\fBnotmuch-hooks\fR(5), \fBnotmuch-new\fR(1), \fBnotmuch-reply\fR(1), -\fBnotmuch-restore\fR(1), \fBnotmuch-search\fR(1), +\fBnotmuch-hooks\fR(5), \fBnotmuch-insert\fR(1), \fBnotmuch-new\fR(1), +\fBnotmuch-reply\fR(1), \fBnotmuch-restore\fR(1), \fBnotmuch-search\fR(1), \fBnotmuch-search-terms\fR(7), \fBnotmuch-show\fR(1), \fBnotmuch-tag\fR(1) diff --git a/man/man1/notmuch-count.1 b/man/man1/notmuch-count.1 index d63be99..2651412 100644 --- a/man/man1/notmuch-count.1 +++ b/man/man1/notmuch-count.1 @@ -52,7 +52,7 @@ count (the default) or not. .SH SEE ALSO \fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-dump\fR(1), -\fBnotmuch-hooks\fR(5), \fBnotmuch-new\fR(1), \fBnotmuch-reply\fR(1), -\fBnotmuch-restore\fR(1), \fBnotmuch-search\fR(1), +\fBnotmuch-hooks\fR(5), \fBnotmuch-insert\fR(1), \fBnotmuch-new\fR(1), +\fBnotmuch-reply\fR(1), \fBnotmuch-restore\fR(1), \fBnotmuch-search\fR(1), \fBnotmuch-search-terms\fR(7), \fBnotmuch-show\fR(1), \fBnotmuch-tag\fR(1) diff --git a/man/man1/notmuch-dump.1 b/man/man1/notmuch-dump.1 index 613fd69..5e58cf9 100644 --- a/man/man1/notmuch-dump.1 +++ b/man/man1/notmuch-dump.1 @@ -92,7 +92,7 @@ for details of the supported syntax for . .SH SEE ALSO \fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1), -\fBnotmuch-hooks\fR(5), \fBnotmuch-new\fR(1), \fBnotmuch-reply\fR(1), -\fBnotmuch-restore\fR(1), \fBnotmuch-search\fR(1), +\fBnotmuch-hooks\fR(5), \fBnotmuch-insert\fR(1), \fBnotmuch-new\fR(1), +\fBnotmuch-reply\fR(1), \fBnotmuch-restore\fR(1), \fBnotmuch-search\fR(1), \fBnotmuch-search-terms\fR(7), \fBnotmuch-show\fR(1), \fBnotmuch-tag\fR(1) diff --git a/man/man1/notmuch-new.1 b/man/man1/notmuch-new.1 index 06c4dfa..e0ede8b 100644 --- a/man/man1/notmuch-new.1 +++ b/man/man1/notmuch-new.1 @@ -64,7 +64,7 @@ Prevents hooks from being run. .SH SEE ALSO \fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1), -\fBnotmuch-dump\fR(1), \fBnotmuch-hooks\fR(5), \fBnotmuch-reply\fR(1), -\fBnotmuch-restore\fR(1), \fBnotmuch-search\fR(1), +\fBnotmuch-dump\fR(1), \fBnotmuch-hooks\fR(5), \fBnotmuch-insert\fR(1), +\fBnotmuch-reply\fR(1), \fBnotmuch-restore\fR(1), \fBnotmuch-search\fR(1), \fBnotmuch-search-terms\fR(7), \fBnotmuch-show\fR(1), \fBnotmuch-tag\fR(1) diff --git a/man/man1/notmuch-reply.1 b/man/man1/notmuch-reply.1 index 13e50ad..82437a4 100644 --- a/man/man1/notmuch-reply.1 +++ b/man/man1/notmuch-reply.1 @@ -123,7 +123,8 @@ The requested format version is too new. .SH SEE ALSO \fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1), -\fBnotmuch-dump\fR(1), \fBnotmuch-hooks\fR(5), \fBnotmuch-new\fR(1), +\fBnotmuch-dump\fR(1), \fBnotmuch-hooks\fR(5), +\fBnotmuch-insert\fR(1), \fBnotmuch-new\fR(1), \fBnotmuch-restore\fR(1), \fBnotmuch-search\fR(1), \fBnotmuch-search-terms\fR(7), \fBnotmuch-show\fR(1), \fBnotmuch-tag\fR(1) diff --git a/man/man1/notmuch-restore.1 b/man/man1/notmuch-restore.1 index 895c6d2..65ecfc8 100644 --- a/man/man1/notmuch-restore.1 +++ b/man/man1/notmuch-restore.1 @@ -84,7 +84,8 @@ should be accurate. .SH SEE ALSO \fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1), -\fBnotmuch-dump\fR(1), \fBnotmuch-hooks\fR(5), \fBnotmuch-new\fR(1), +\fBnotmuch-dump\fR(1), \fBnotmuch-hooks\fR(5), +\fBnotmuch-insert\fR(1), \fBnotmuch-new\fR(1), \fBnotmuch-reply\fR(1), \fBnotmuch-search\fR(1), \fBnotmuch-search-terms\fR(7), \fBnotmuch-show\fR(1), \fBnotmuch-tag\fR(1) diff --git a/man/man1/notmuch-search.1 b/man/man1/notmuch-search.1 index 321d779..67fd34b 100644 --- a/man/man1/notmuch-search.1 +++ b/man/man1/notmuch-search.1 @@ -155,7 +155,8 @@ The requested format version is too new. .SH SEE ALSO \fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1), -\fBnotmuch-dump\fR(1), \fBnotmuch-hooks\fR(5), \fBnotmuch-new\fR(1), +\fBnotmuch-dump\fR(1), \fBnotmuch-hooks\fR(5), +\fBnotmuch-insert\fR(1), \fBnotmuch-new\fR(1), \fBnotmuch-reply\fR(1), \fBnotmuch-restore\fR(1), \fBnotmuch-search-terms\fR(7), \fBnotmuch-show\fR(1), \fBnotmuch-tag\fR(1) diff --git a/man/man1/notmuch-show.1
[PATCH v3 18/20] man: document 'insert' command
Add initial documentation for notmuch insert command. --- man/Makefile.local| 1 + man/man1/notmuch-insert.1 | 59 +++ 2 files changed, 60 insertions(+) create mode 100644 man/man1/notmuch-insert.1 diff --git a/man/Makefile.local b/man/Makefile.local index 72e2a18..216aaa0 100644 --- a/man/Makefile.local +++ b/man/Makefile.local @@ -12,6 +12,7 @@ MAN1 := \ $(dir)/man1/notmuch-count.1 \ $(dir)/man1/notmuch-dump.1 \ $(dir)/man1/notmuch-restore.1 \ + $(dir)/man1/notmuch-insert.1 \ $(dir)/man1/notmuch-new.1 \ $(dir)/man1/notmuch-reply.1 \ $(dir)/man1/notmuch-search.1 \ diff --git a/man/man1/notmuch-insert.1 b/man/man1/notmuch-insert.1 new file mode 100644 index 000..4a7cbeb --- /dev/null +++ b/man/man1/notmuch-insert.1 @@ -0,0 +1,59 @@ +.TH NOTMUCH-INSERT 1 2013-xx-xx "Notmuch 0.xx" +.SH NAME +notmuch-insert \- add a message to the maildir and notmuch database +.SH SYNOPSIS + +.B notmuch insert +.RI "[" options "]" +.RI "[ +<" tag> "|\-<" tag "> ... ]" + +.SH DESCRIPTION + +.B notmuch insert +reads a message from standard input +and delivers it to the specified maildir folder, +then incorporates the message into the notmuch database. +It is an alternative to using a separate tool to deliver +the message then running +.B notmuch new +afterwards. + +The new message will be tagged with the tags specified by the +.B new.tags +configuration option, then by operations specified on the command-line: +tags prefixed by '+' are added while +those prefixed by '\-' are removed. + +Option arguments must appear before any tag operation arguments. +Supported options for +.B insert +include +.RS 4 +.TP 4 +.BI "--folder=<" folder ">" + +Deliver the message to the specified folder, +relative to the top-level directory given by the value of +\fBdatabase.path\fR. +The default is to deliver to the top-level directory. + +.RE + +.RS 4 +.TP 4 +.B "--create-folder" + +Try to create the folder named by the +.B "--folder" +option, if it does not exist. +Otherwise the folder must already exist for mail +delivery to succeed. + +.RE +.SH SEE ALSO + +\fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1), +\fBnotmuch-dump\fR(1), \fBnotmuch-hooks\fR(5), \fBnotmuch-reply\fR(1), +\fBnotmuch-restore\fR(1), \fBnotmuch-search\fR(1), +\fBnotmuch-search-terms\fR(7), \fBnotmuch-show\fR(1), +\fBnotmuch-tag\fR(1) -- 1.7.12.1
[PATCH v3 17/20] insert: add copyright line from notmuch-deliver
The 'insert' implementation was based partly on notmuch-deliver. --- notmuch-insert.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/notmuch-insert.c b/notmuch-insert.c index 494a7b0..6b3e380 100644 --- a/notmuch-insert.c +++ b/notmuch-insert.c @@ -2,6 +2,9 @@ * * Copyright ? 2013 Peter Wang * + * Based in part on notmuch-deliver + * Copyright ? 2010 Ali Polatel + * * 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 -- 1.7.12.1
[PATCH v3 16/20] insert: trap SIGINT and clean up
The only potentially long-running part of the 'insert' command should be copying stdin to the 'tmp' file. If SIGINT is received during the copying process, abort and clean up the file in 'tmp'. At all other points, just ignore the signal and continue. --- notmuch-insert.c | 28 ++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/notmuch-insert.c b/notmuch-insert.c index 8012eb4..494a7b0 100644 --- a/notmuch-insert.c +++ b/notmuch-insert.c @@ -25,6 +25,21 @@ #include #include +static volatile sig_atomic_t interrupted; + +static void +handle_sigint (unused (int sig)) +{ +static char msg[] = "Stopping... \n"; + +/* This write is "opportunistic", so it's okay to ignore the + * result. It is not required for correctness, and if it does + * fail or produce a short write, we want to get out of the signal + * handler as quickly as possible, not retry it. */ +IGNORE_RESULT (write (2, msg, sizeof (msg) - 1)); +interrupted = 1; +} + /* Like gethostname but guarantees that a null-terminated hostname is * returned, even if it has to make one up. * Returns true unless hostname contains a slash. */ @@ -258,7 +273,7 @@ copy_stdin (int fdin, int fdout) ssize_t remain; ssize_t written; -for (;;) { +while (! interrupted) { remain = read (fdin, buf, sizeof (buf)); if (remain == 0) break; @@ -287,7 +302,7 @@ copy_stdin (int fdin, int fdout) } while (remain > 0); } -return TRUE; +return ! interrupted; } /* Add the specified message file to the notmuch database, applying tags. @@ -372,6 +387,7 @@ notmuch_insert_command (void *ctx, int argc, char *argv[]) { notmuch_config_t *config; notmuch_database_t *notmuch; +struct sigaction action; const char *db_path; const char **new_tags; size_t new_tags_length; @@ -443,6 +459,14 @@ notmuch_insert_command (void *ctx, int argc, char *argv[]) return 1; } +/* Setup our handler for SIGINT. We do not set SA_RESTART so that copying + * from standard input may be interrupted. */ +memset (, 0, sizeof (struct sigaction)); +action.sa_handler = handle_sigint; +sigemptyset (_mask); +action.sa_flags = 0; +sigaction (SIGINT, , NULL); + if (notmuch_database_open (notmuch_config_get_database_path (config), NOTMUCH_DATABASE_MODE_READ_WRITE, )) return 1; -- 1.7.12.1
[PATCH v3 15/20] insert: fsync parent directory after mkdir
After creating a subdirectory, fsync on its parent directory for durability. --- notmuch-insert.c | 14 +- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/notmuch-insert.c b/notmuch-insert.c index c4d5e75..8012eb4 100644 --- a/notmuch-insert.c +++ b/notmuch-insert.c @@ -82,9 +82,21 @@ sync_dir (const char *dir) static notmuch_bool_t make_directory (char *path, int mode) { +notmuch_bool_t ret; +char *slash; + if (mkdir (path, mode) != 0) return (errno == EEXIST); -return TRUE; + +/* Sync the parent directory for durability. */ +ret = TRUE; +slash = strrchr (path, '/'); +if (slash) { + *slash = '\0'; + ret = sync_dir (path); + *slash = '/'; +} +return ret; } /* Make the given directory including its parent directories as necessary. -- 1.7.12.1
[PATCH v3 14/20] insert: fsync new directory after rename
After moving the file from the 'tmp' to the 'new' directory, fsync on the 'new' directory for durability. --- notmuch-insert.c | 41 ++--- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/notmuch-insert.c b/notmuch-insert.c index 60855c1..c4d5e75 100644 --- a/notmuch-insert.c +++ b/notmuch-insert.c @@ -56,6 +56,28 @@ check_folder_name (const char *folder) } } +/* Call fsync() on a directory path. */ +static notmuch_bool_t +sync_dir (const char *dir) +{ +notmuch_bool_t ret; +int fd; + +ret = TRUE; +fd = open (dir, O_RDONLY); +if (fd == -1) { + fprintf (stderr, "Error: open() dir failed: %s\n", strerror (errno)); + ret = FALSE; +} +if (ret && fsync (fd) != 0) { + fprintf (stderr, "Error: fsync() dir failed: %s\n", strerror (errno)); + ret = FALSE; +} +if (fd != -1) + close (fd); +return ret; +} + /* Make the given directory, succeeding if it already exists. */ static notmuch_bool_t make_directory (char *path, int mode) @@ -140,10 +162,11 @@ maildir_create_folder (void *ctx, const char *dir) /* Open a unique file in the Maildir 'tmp' directory. * Returns the file descriptor on success, or -1 on failure. * On success, file paths for the message in the 'tmp' and 'new' - * directories are returned via tmppath and newpath. */ + * directories are returned via tmppath and newpath, + * and the path of the 'new' directory itself in newdir. */ static int maildir_open_tmp_file (void *ctx, const char *dir, - char **tmppath, char **newpath) + char **tmppath, char **newpath, char **newdir) { pid_t pid; char hostname[256]; @@ -180,8 +203,9 @@ maildir_open_tmp_file (void *ctx, const char *dir, return -1; } +*newdir = talloc_asprintf (ctx, "%s/new", dir); *newpath = talloc_asprintf (ctx, "%s/new/%s", dir, filename); -if (! *newpath) { +if (! *newdir || ! *newpath) { fprintf (stderr, "Out of memory\n"); close (fd); unlink (*tmppath); @@ -201,14 +225,16 @@ maildir_open_tmp_file (void *ctx, const char *dir, * http://wiki.dovecot.org/MailboxFormat/Maildir#Mail_delivery */ static notmuch_bool_t -maildir_move_tmp_to_new (const char *tmppath, const char *newpath) +maildir_move_tmp_to_new (const char *tmppath, const char *newpath, +const char *newdir) { if (rename (tmppath, newpath) != 0) { fprintf (stderr, "Error: rename() failed: %s\n", strerror (errno)); return FALSE; } -return TRUE; +/* Sync the 'new' directory after rename for durability. */ +return sync_dir (newdir); } /* Copy the contents of standard input (fdin) into fdout. */ @@ -297,10 +323,11 @@ insert_message (void *ctx, notmuch_database_t *notmuch, int fdin, { char *tmppath; char *newpath; +char *newdir; int fdout; notmuch_bool_t ret; -fdout = maildir_open_tmp_file (ctx, dir, , ); +fdout = maildir_open_tmp_file (ctx, dir, , , ); if (fdout < 0) { return FALSE; } @@ -311,7 +338,7 @@ insert_message (void *ctx, notmuch_database_t *notmuch, int fdin, } close (fdout); if (ret) { - ret = maildir_move_tmp_to_new (tmppath, newpath); + ret = maildir_move_tmp_to_new (tmppath, newpath, newdir); } if (!ret) { unlink (tmppath); -- 1.7.12.1
[PATCH v3 13/20] insert: fsync after writing tmp file
Flush the tmp file to disk after writing for durability. --- notmuch-insert.c | 4 1 file changed, 4 insertions(+) diff --git a/notmuch-insert.c b/notmuch-insert.c index 85acaea..60855c1 100644 --- a/notmuch-insert.c +++ b/notmuch-insert.c @@ -305,6 +305,10 @@ insert_message (void *ctx, notmuch_database_t *notmuch, int fdin, return FALSE; } ret = copy_stdin (fdin, fdout); +if (ret && fsync (fdout) != 0) { + fprintf (stderr, "Error: fsync failed: %s\n", strerror (errno)); + ret = FALSE; +} close (fdout); if (ret) { ret = maildir_move_tmp_to_new (tmppath, newpath); -- 1.7.12.1
[PATCH v3 12/20] insert: add --create-folder option
Support an option to create a new folder in the maildir. --- notmuch-insert.c | 88 1 file changed, 88 insertions(+) diff --git a/notmuch-insert.c b/notmuch-insert.c index 67ef94a..85acaea 100644 --- a/notmuch-insert.c +++ b/notmuch-insert.c @@ -56,6 +56,87 @@ check_folder_name (const char *folder) } } +/* Make the given directory, succeeding if it already exists. */ +static notmuch_bool_t +make_directory (char *path, int mode) +{ +if (mkdir (path, mode) != 0) + return (errno == EEXIST); +return TRUE; +} + +/* Make the given directory including its parent directories as necessary. + * Return TRUE on success, FALSE on error. */ +static notmuch_bool_t +make_directory_and_parents (char *path, int mode) +{ +struct stat st; +char *start; +char *end; +notmuch_bool_t ret; + +/* First check the common case: directory already exists. */ +if (stat (path, ) == 0) + return S_ISDIR (st.st_mode) ? TRUE : FALSE; + +for (start = path; *start != '\0'; start = end + 1) { + /* start points to the first unprocessed character. +* Find the next slash from start onwards. */ + end = strchr (start, '/'); + + /* If there are no more slashes then all the parent directories +* have been made. Now attempt to make the whole path. */ + if (end == NULL) + return make_directory (path, mode); + + /* Make the path up to the next slash, unless the current +* directory component is actually empty. */ + if (end > start) { + *end = '\0'; + ret = make_directory (path, mode); + *end = '/'; + if (! ret) + return FALSE; + } +} + +return TRUE; +} + +/* Create the given maildir folder, i.e. dir and its subdirectories + * 'cur', 'new', 'tmp'. */ +static notmuch_bool_t +maildir_create_folder (void *ctx, const char *dir) +{ +const int mode = 0700; +char *subdir; +char *tail; + +/* Create 'cur' directory, including parent directories. */ +subdir = talloc_asprintf (ctx, "%s/cur", dir); +if (! subdir) { + fprintf (stderr, "Out of memory.\n"); + return FALSE; +} +if (! make_directory_and_parents (subdir, mode)) + return FALSE; + +tail = subdir + strlen (subdir) - 3; + +/* Create 'new' directory. */ +strcpy (tail, "new"); +if (! make_directory (subdir, mode)) + return FALSE; + +/* Create 'tmp' directory. */ +strcpy (tail, "tmp"); +if (! make_directory (subdir, mode)) + return FALSE; + +talloc_free (subdir); +return TRUE; +} + /* Open a unique file in the Maildir 'tmp' directory. * Returns the file descriptor on success, or -1 on failure. * On success, file paths for the message in the 'tmp' and 'new' @@ -254,6 +335,7 @@ notmuch_insert_command (void *ctx, int argc, char *argv[]) tag_op_list_t *tag_ops; char *query_string = NULL; const char *folder = NULL; +notmuch_bool_t create_folder = FALSE; char *maildir; int opt_index; unsigned int i; @@ -261,6 +343,7 @@ notmuch_insert_command (void *ctx, int argc, char *argv[]) notmuch_opt_desc_t options[] = { { NOTMUCH_OPT_STRING, , "folder", 0, 0 }, + { NOTMUCH_OPT_BOOLEAN, _folder, "create-folder", 0, 0 }, { NOTMUCH_OPT_END, 0, 0, 0, 0 } }; @@ -311,6 +394,11 @@ notmuch_insert_command (void *ctx, int argc, char *argv[]) fprintf (stderr, "Out of memory\n"); return 1; } +if (create_folder && ! maildir_create_folder (ctx, maildir)) { + fprintf (stderr, "Error: creating maildir %s: %s\n", +maildir, strerror (errno)); + return 1; +} if (notmuch_database_open (notmuch_config_get_database_path (config), NOTMUCH_DATABASE_MODE_READ_WRITE, )) -- 1.7.12.1
[PATCH v3 11/20] insert: prevent writes outside Maildir hierarchy
Don't accept a --folder name that contains a ".." component, in order to prevent writing outside of the Maildir hierarchy. --- notmuch-insert.c | 21 + 1 file changed, 21 insertions(+) diff --git a/notmuch-insert.c b/notmuch-insert.c index ba8cf5a..67ef94a 100644 --- a/notmuch-insert.c +++ b/notmuch-insert.c @@ -39,6 +39,23 @@ safe_gethostname (char *hostname, size_t len) return (strchr (hostname, '/') == NULL); } +/* Check the specified folder name does not contain a directory + * component ".." to prevent writes outside of the Maildir hierarchy. */ +static notmuch_bool_t +check_folder_name (const char *folder) +{ +const char *p = folder; + +for (;;) { + if ((p[0] == '.') && (p[1] == '.') && (p[2] == '\0' || p[2] == '/')) + return FALSE; + p = strchr (p, '/'); + if (!p) + return TRUE; + p++; +} +} + /* Open a unique file in the Maildir 'tmp' directory. * Returns the file descriptor on success, or -1 on failure. * On success, file paths for the message in the 'tmp' and 'new' @@ -282,6 +299,10 @@ notmuch_insert_command (void *ctx, int argc, char *argv[]) } if (folder != NULL) { + if (! check_folder_name (folder)) { + fprintf (stderr, "Error: bad folder name: %s\n", folder); + return 1; + } maildir = talloc_asprintf (ctx, "%s/%s", db_path, folder); } else { maildir = talloc_asprintf (ctx, "%s", db_path); -- 1.7.12.1
[PATCH v3 10/20] insert: support --folder option
Allow the new message to be inserted into a folder within the Maildir hierarchy instead of the top-level folder. --- notmuch-insert.c | 22 -- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/notmuch-insert.c b/notmuch-insert.c index b0ac174..ba8cf5a 100644 --- a/notmuch-insert.c +++ b/notmuch-insert.c @@ -236,11 +236,25 @@ notmuch_insert_command (void *ctx, int argc, char *argv[]) size_t new_tags_length; tag_op_list_t *tag_ops; char *query_string = NULL; +const char *folder = NULL; char *maildir; -int opt_index = 1; +int opt_index; unsigned int i; notmuch_bool_t ret; +notmuch_opt_desc_t options[] = { + { NOTMUCH_OPT_STRING, , "folder", 0, 0 }, + { NOTMUCH_OPT_END, 0, 0, 0, 0 } +}; + +opt_index = parse_arguments (argc, argv, options, 1); + +if (opt_index < 0) { + fprintf (stderr, "Error: bad argument to notmuch insert: %s\n", +argv[-opt_index]); + return 1; +} + config = notmuch_config_open (ctx, NULL, NULL); if (config == NULL) return 1; @@ -267,7 +281,11 @@ notmuch_insert_command (void *ctx, int argc, char *argv[]) return 1; } -maildir = talloc_asprintf (ctx, "%s", db_path); +if (folder != NULL) { + maildir = talloc_asprintf (ctx, "%s/%s", db_path, folder); +} else { + maildir = talloc_asprintf (ctx, "%s", db_path); +} if (! maildir) { fprintf (stderr, "Out of memory\n"); return 1; -- 1.7.12.1
[PATCH v3 09/20] insert: parse and apply command-line tag operations
Parse +tag and -tag operations on the 'insert' command-line and apply them to the inserted message in addition to new.tags. --- notmuch-insert.c | 11 +++ 1 file changed, 11 insertions(+) diff --git a/notmuch-insert.c b/notmuch-insert.c index 8388d07..b0ac174 100644 --- a/notmuch-insert.c +++ b/notmuch-insert.c @@ -235,7 +235,9 @@ notmuch_insert_command (void *ctx, int argc, char *argv[]) const char **new_tags; size_t new_tags_length; tag_op_list_t *tag_ops; +char *query_string = NULL; char *maildir; +int opt_index = 1; unsigned int i; notmuch_bool_t ret; @@ -256,6 +258,15 @@ notmuch_insert_command (void *ctx, int argc, char *argv[]) return 1; } +if (parse_tag_command_line (ctx, argc - opt_index, argv + opt_index, + _string, tag_ops)) + return 1; + +if (*query_string != '\0') { + fprintf (stderr, "Error: unexpected query string: %s\n", query_string); + return 1; +} + maildir = talloc_asprintf (ctx, "%s", db_path); if (! maildir) { fprintf (stderr, "Out of memory\n"); -- 1.7.12.1
[PATCH v3 08/20] tag-util: move out 'tag' command-line checks
parse_tag_command_line checked for two error conditions which are specific to the 'tag' command. It can be reused for the notmuch 'insert' command if we move the checks out, into notmuch-tag.c. --- notmuch-tag.c | 10 ++ tag-util.c| 10 -- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/notmuch-tag.c b/notmuch-tag.c index d9daf8f..a901dad 100644 --- a/notmuch-tag.c +++ b/notmuch-tag.c @@ -234,6 +234,16 @@ notmuch_tag_command (void *ctx, int argc, char *argv[]) if (parse_tag_command_line (ctx, argc - opt_index, argv + opt_index, _string, tag_ops)) return 1; + + if (tag_op_list_size (tag_ops) == 0) { + fprintf (stderr, "Error: 'notmuch tag' requires at least one tag to add or remove.\n"); + return 1; + } + + if (*query_string == '\0') { + fprintf (stderr, "Error: notmuch tag requires at least one search term.\n"); + return 1; + } } config = notmuch_config_open (ctx, NULL, NULL); diff --git a/tag-util.c b/tag-util.c index 3f9da05..41f2c09 100644 --- a/tag-util.c +++ b/tag-util.c @@ -186,18 +186,8 @@ parse_tag_command_line (void *ctx, int argc, char **argv, tag_op_list_append (tag_ops, argv[i] + 1, is_remove); } -if (tag_op_list_size (tag_ops) == 0) { - fprintf (stderr, "Error: 'notmuch tag' requires at least one tag to add or remove.\n"); - return TAG_PARSE_INVALID; -} - *query_str = query_string_from_args (ctx, argc - i, [i]); -if (*query_str == NULL || **query_str == '\0') { - fprintf (stderr, "Error: notmuch tag requires at least one search term.\n"); - return TAG_PARSE_INVALID; -} - return TAG_PARSE_SUCCESS; } -- 1.7.12.1
[PATCH v3 07/20] tag-util: do not reset list in parse_tag_command_line
No current callers of parse_tag_command_line require that it clear its tag list argument. The notmuch 'insert' command will be better served if the function modifies a pre-populated list (of new.tags) instead of clobbering it outright. --- tag-util.c | 2 -- tag-util.h | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tag-util.c b/tag-util.c index 701d329..3f9da05 100644 --- a/tag-util.c +++ b/tag-util.c @@ -165,8 +165,6 @@ parse_tag_command_line (void *ctx, int argc, char **argv, int i; -tag_op_list_reset (tag_ops); - for (i = 0; i < argc; i++) { if (strcmp (argv[i], "--") == 0) { i++; diff --git a/tag-util.h b/tag-util.h index 246de85..4628f16 100644 --- a/tag-util.h +++ b/tag-util.h @@ -81,6 +81,8 @@ parse_tag_line (void *ctx, char *line, * Output Parameters: * ops contains a list of tag operations * query_str the search terms. + * + * The ops argument is not cleared. */ tag_parse_status_t -- 1.7.12.1
[PATCH v3 06/20] insert: apply default tags to new message
Apply the new.tags to messages added by 'insert'. This mirrors the behaviour if the message were delivered by a separate tool followed by 'notmuch new'. --- notmuch-insert.c | 29 +++-- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/notmuch-insert.c b/notmuch-insert.c index 498421d..8388d07 100644 --- a/notmuch-insert.c +++ b/notmuch-insert.c @@ -19,6 +19,7 @@ */ #include "notmuch-client.h" +#include "tag-util.h" #include #include @@ -153,10 +154,11 @@ copy_stdin (int fdin, int fdout) return TRUE; } -/* Add the specified message file to the notmuch database. +/* Add the specified message file to the notmuch database, applying tags. * The file is renamed to encode notmuch tags as maildir flags. */ static notmuch_bool_t -add_file_to_database (notmuch_database_t *notmuch, const char *path) +add_file_to_database (notmuch_database_t *notmuch, const char *path, + tag_op_list_t *tag_ops) { notmuch_message_t *message; notmuch_status_t status; @@ -184,7 +186,7 @@ add_file_to_database (notmuch_database_t *notmuch, const char *path) return FALSE; } -notmuch_message_tags_to_maildir_flags (message); +tag_op_list_apply (message, tag_ops, TAG_FLAG_MAILDIR_SYNC); notmuch_message_destroy (message); @@ -193,7 +195,7 @@ add_file_to_database (notmuch_database_t *notmuch, const char *path) static notmuch_bool_t insert_message (void *ctx, notmuch_database_t *notmuch, int fdin, - const char *dir) + const char *dir, tag_op_list_t *tag_ops) { char *tmppath; char *newpath; @@ -214,7 +216,7 @@ insert_message (void *ctx, notmuch_database_t *notmuch, int fdin, return FALSE; } -ret = add_file_to_database (notmuch, newpath); +ret = add_file_to_database (notmuch, newpath, tag_ops); if (!ret) { /* XXX maybe there should be an option to keep the file in maildir? */ unlink (newpath); @@ -230,7 +232,11 @@ notmuch_insert_command (void *ctx, int argc, char *argv[]) notmuch_config_t *config; notmuch_database_t *notmuch; const char *db_path; +const char **new_tags; +size_t new_tags_length; +tag_op_list_t *tag_ops; char *maildir; +unsigned int i; notmuch_bool_t ret; config = notmuch_config_open (ctx, NULL, NULL); @@ -238,6 +244,17 @@ notmuch_insert_command (void *ctx, int argc, char *argv[]) return 1; db_path = notmuch_config_get_database_path (config); +new_tags = notmuch_config_get_new_tags (config, _tags_length); + +tag_ops = tag_op_list_create (ctx); +if (tag_ops == NULL) { + fprintf (stderr, "Out of memory.\n"); + return 1; +} +for (i = 0; i < new_tags_length; i++) { + if (tag_op_list_append (tag_ops, new_tags[i], FALSE)) + return 1; +} maildir = talloc_asprintf (ctx, "%s", db_path); if (! maildir) { @@ -249,7 +266,7 @@ notmuch_insert_command (void *ctx, int argc, char *argv[]) NOTMUCH_DATABASE_MODE_READ_WRITE, )) return 1; -ret = insert_message (ctx, notmuch, STDIN_FILENO, maildir); +ret = insert_message (ctx, notmuch, STDIN_FILENO, maildir, tag_ops); notmuch_database_destroy (notmuch); -- 1.7.12.1
[PATCH v3 05/20] insert: add new message to database
Add the new message to the notmuch database, renaming the file to encode notmuch tags as maildir flags. --- notmuch-insert.c | 49 - 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/notmuch-insert.c b/notmuch-insert.c index c0289d9..498421d 100644 --- a/notmuch-insert.c +++ b/notmuch-insert.c @@ -153,6 +153,44 @@ copy_stdin (int fdin, int fdout) return TRUE; } +/* Add the specified message file to the notmuch database. + * The file is renamed to encode notmuch tags as maildir flags. */ +static notmuch_bool_t +add_file_to_database (notmuch_database_t *notmuch, const char *path) +{ +notmuch_message_t *message; +notmuch_status_t status; + +status = notmuch_database_add_message (notmuch, path, ); +switch (status) { +case NOTMUCH_STATUS_SUCCESS: + break; +case NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID: + fprintf (stderr, "Warning: duplicate message.\n"); + break; +default: +case NOTMUCH_STATUS_FILE_NOT_EMAIL: +case NOTMUCH_STATUS_READ_ONLY_DATABASE: +case NOTMUCH_STATUS_XAPIAN_EXCEPTION: +case NOTMUCH_STATUS_OUT_OF_MEMORY: +case NOTMUCH_STATUS_FILE_ERROR: +case NOTMUCH_STATUS_NULL_POINTER: +case NOTMUCH_STATUS_TAG_TOO_LONG: +case NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW: +case NOTMUCH_STATUS_UNBALANCED_ATOMIC: +case NOTMUCH_STATUS_LAST_STATUS: + fprintf (stderr, "Error: failed to add `%s' to notmuch database: %s\n", +path, notmuch_status_to_string (status)); + return FALSE; +} + +notmuch_message_tags_to_maildir_flags (message); + +notmuch_message_destroy (message); + +return TRUE; +} + static notmuch_bool_t insert_message (void *ctx, notmuch_database_t *notmuch, int fdin, const char *dir) @@ -173,8 +211,17 @@ insert_message (void *ctx, notmuch_database_t *notmuch, int fdin, } if (!ret) { unlink (tmppath); + return FALSE; } -return ret; + +ret = add_file_to_database (notmuch, newpath); +if (!ret) { + /* XXX maybe there should be an option to keep the file in maildir? */ + unlink (newpath); + return FALSE; +} + +return TRUE; } int -- 1.7.12.1
[PATCH v3 04/20] insert: move file from Maildir tmp to new
Atomically move the new message file from the Maildir 'tmp' directory to 'new'. --- notmuch-insert.c | 21 + 1 file changed, 21 insertions(+) diff --git a/notmuch-insert.c b/notmuch-insert.c index 38e815f..c0289d9 100644 --- a/notmuch-insert.c +++ b/notmuch-insert.c @@ -94,6 +94,24 @@ maildir_open_tmp_file (void *ctx, const char *dir, return fd; } +/* Atomically move the new message file from the Maildir 'tmp' directory + * to the 'new' directory. + * + * We follow the Dovecot recommendation to simply use rename() + * instead of link() and unlink(). See also: + * http://wiki.dovecot.org/MailboxFormat/Maildir#Mail_delivery + */ +static notmuch_bool_t +maildir_move_tmp_to_new (const char *tmppath, const char *newpath) +{ +if (rename (tmppath, newpath) != 0) { + fprintf (stderr, "Error: rename() failed: %s\n", strerror (errno)); + return FALSE; +} + +return TRUE; +} + /* Copy the contents of standard input (fdin) into fdout. */ static notmuch_bool_t copy_stdin (int fdin, int fdout) @@ -150,6 +168,9 @@ insert_message (void *ctx, notmuch_database_t *notmuch, int fdin, } ret = copy_stdin (fdin, fdout); close (fdout); +if (ret) { + ret = maildir_move_tmp_to_new (tmppath, newpath); +} if (!ret) { unlink (tmppath); } -- 1.7.12.1
[PATCH v3 03/20] insert: copy stdin to Maildir tmp file
Read the new message from standard input into the Maildir tmp file. --- notmuch-insert.c | 51 +++ 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/notmuch-insert.c b/notmuch-insert.c index 7d100ae..38e815f 100644 --- a/notmuch-insert.c +++ b/notmuch-insert.c @@ -94,6 +94,47 @@ maildir_open_tmp_file (void *ctx, const char *dir, return fd; } +/* Copy the contents of standard input (fdin) into fdout. */ +static notmuch_bool_t +copy_stdin (int fdin, int fdout) +{ +char buf[4096]; +char *p; +ssize_t remain; +ssize_t written; + +for (;;) { + remain = read (fdin, buf, sizeof (buf)); + if (remain == 0) + break; + if (remain < 0) { + if (errno == EINTR) + continue; + fprintf (stderr, "Error: reading from standard input: %s\n", +strerror (errno)); + return FALSE; + } + + p = buf; + do { + written = write (fdout, p, remain); + if (written == 0) + return FALSE; + if (written < 0) { + if (errno == EINTR) + continue; + fprintf (stderr, "Error: writing to temporary file: %s", +strerror (errno)); + return FALSE; + } + p += written; + remain -= written; + } while (remain > 0); +} + +return TRUE; +} + static notmuch_bool_t insert_message (void *ctx, notmuch_database_t *notmuch, int fdin, const char *dir) @@ -101,16 +142,18 @@ insert_message (void *ctx, notmuch_database_t *notmuch, int fdin, char *tmppath; char *newpath; int fdout; +notmuch_bool_t ret; fdout = maildir_open_tmp_file (ctx, dir, , ); if (fdout < 0) { return FALSE; } - -/* For now we just delete the tmp file immediately. */ +ret = copy_stdin (fdin, fdout); close (fdout); -unlink (tmppath); -return FALSE; +if (!ret) { + unlink (tmppath); +} +return ret; } int -- 1.7.12.1
[PATCH v3 02/20] insert: open Maildir tmp file
Open a unique file in the Maildir tmp directory. The new message is not yet copied into the file. --- notmuch-insert.c | 105 ++- 1 file changed, 104 insertions(+), 1 deletion(-) diff --git a/notmuch-insert.c b/notmuch-insert.c index c1d359c..7d100ae 100644 --- a/notmuch-insert.c +++ b/notmuch-insert.c @@ -20,12 +20,107 @@ #include "notmuch-client.h" +#include +#include +#include + +/* Like gethostname but guarantees that a null-terminated hostname is + * returned, even if it has to make one up. + * Returns true unless hostname contains a slash. */ +static notmuch_bool_t +safe_gethostname (char *hostname, size_t len) +{ +if (gethostname (hostname, len) == -1) { + strncpy (hostname, "unknown", len); +} +hostname[len - 1] = '\0'; + +return (strchr (hostname, '/') == NULL); +} + +/* Open a unique file in the Maildir 'tmp' directory. + * Returns the file descriptor on success, or -1 on failure. + * On success, file paths for the message in the 'tmp' and 'new' + * directories are returned via tmppath and newpath. */ +static int +maildir_open_tmp_file (void *ctx, const char *dir, + char **tmppath, char **newpath) +{ +pid_t pid; +char hostname[256]; +struct timeval tv; +char *filename; +int fd = -1; + +/* We follow the Dovecot file name generation algorithm. */ +pid = getpid (); +if (! safe_gethostname (hostname, sizeof (hostname))) { + fprintf (stderr, "Error: invalid host name.\n"); + return -1; +} +do { + gettimeofday (, NULL); + filename = talloc_asprintf (ctx, "%ld.M%ldP%d.%s", + tv.tv_sec, tv.tv_usec, pid, hostname); + if (! filename) { + fprintf (stderr, "Out of memory\n"); + return -1; + } + + *tmppath = talloc_asprintf (ctx, "%s/tmp/%s", dir, filename); + if (! *tmppath) { + fprintf (stderr, "Out of memory\n"); + return -1; + } + + fd = open (*tmppath, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600); +} while (fd == -1 && errno == EEXIST); + +if (fd == -1) { + fprintf (stderr, "Error: opening %s: %s\n", *tmppath, strerror (errno)); + return -1; +} + +*newpath = talloc_asprintf (ctx, "%s/new/%s", dir, filename); +if (! *newpath) { + fprintf (stderr, "Out of memory\n"); + close (fd); + unlink (*tmppath); + return -1; +} + +talloc_free (filename); + +return fd; +} + +static notmuch_bool_t +insert_message (void *ctx, notmuch_database_t *notmuch, int fdin, + const char *dir) +{ +char *tmppath; +char *newpath; +int fdout; + +fdout = maildir_open_tmp_file (ctx, dir, , ); +if (fdout < 0) { + return FALSE; +} + +/* For now we just delete the tmp file immediately. */ +close (fdout); +unlink (tmppath); +return FALSE; +} + int notmuch_insert_command (void *ctx, int argc, char *argv[]) { notmuch_config_t *config; notmuch_database_t *notmuch; const char *db_path; +char *maildir; +notmuch_bool_t ret; config = notmuch_config_open (ctx, NULL, NULL); if (config == NULL) @@ -33,11 +128,19 @@ notmuch_insert_command (void *ctx, int argc, char *argv[]) db_path = notmuch_config_get_database_path (config); +maildir = talloc_asprintf (ctx, "%s", db_path); +if (! maildir) { + fprintf (stderr, "Out of memory\n"); + return 1; +} + if (notmuch_database_open (notmuch_config_get_database_path (config), NOTMUCH_DATABASE_MODE_READ_WRITE, )) return 1; +ret = insert_message (ctx, notmuch, STDIN_FILENO, maildir); + notmuch_database_destroy (notmuch); -return 1; +return (ret) ? 0 : 1; } -- 1.7.12.1
[PATCH v3 00/20] insert command
This mainly addresses review comments from v2 and rebases on top of tag-util.c. Peter Wang (20): cli: add stub for insert command insert: open Maildir tmp file insert: copy stdin to Maildir tmp file insert: move file from Maildir tmp to new insert: add new message to database insert: apply default tags to new message tag-util: do not reset list in parse_tag_command_line tag-util: move out 'tag' command-line checks insert: parse and apply command-line tag operations insert: support --folder option insert: prevent writes outside Maildir hierarchy insert: add --create-folder option insert: fsync after writing tmp file insert: fsync new directory after rename insert: fsync parent directory after mkdir insert: trap SIGINT and clean up insert: add copyright line from notmuch-deliver man: document 'insert' command man: reference notmuch-insert.1 test: add tests for insert Makefile.local | 1 + man/Makefile.local | 1 + man/man1/notmuch-config.1 | 4 +- man/man1/notmuch-count.1| 4 +- man/man1/notmuch-dump.1 | 4 +- man/man1/notmuch-insert.1 | 59 + man/man1/notmuch-new.1 | 4 +- man/man1/notmuch-reply.1| 3 +- man/man1/notmuch-restore.1 | 3 +- man/man1/notmuch-search.1 | 3 +- man/man1/notmuch-show.1 | 3 +- man/man1/notmuch-tag.1 | 3 +- man/man1/notmuch.1 | 3 +- man/man5/notmuch-hooks.5| 4 +- man/man7/notmuch-search-terms.7 | 3 +- notmuch-client.h| 3 + notmuch-insert.c| 482 notmuch-tag.c | 10 + notmuch.c | 3 + tag-util.c | 12 - tag-util.h | 2 + test/insert | 106 + test/notmuch-test | 1 + 23 files changed, 692 insertions(+), 29 deletions(-) create mode 100644 man/man1/notmuch-insert.1 create mode 100644 notmuch-insert.c create mode 100755 test/insert -- 1.7.12.1
[PATCH 1/1] test/test-lib.sh: use $test_subtest_name in all tests
Set the variable '$test_subtest_name' in all functions which starts a new test and use that variable in all functions that output test results. Additionally output the latest '$test_subtest_name' in case of abnormal exit, to avoid confusion. --- The main reason to do this change is to get latest '$test_subtest_name' printed in case of abnormal exit. I cherry-picked this change from a larger work-in-progress patch set that adds 'set -e -o pipefail' support... I am pretty sure I got all the cases covered. If not, we'll notice it later when some test fail in a way I could not anticipate. Anyway, tests success fail as they used to be. test/test-lib.sh | 52 +--- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/test/test-lib.sh b/test/test-lib.sh index 6ce3b31..58e7b93 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -197,7 +197,11 @@ die () { then exit $code else - echo 5 FATAL: Unexpected exit with code $code + exec 51 + say_color error '%-6s' FATAL + echo $test_subtest_name + echo + echo Unexpected exit while executing $0. Exit code $code. exit 1 fi } @@ -494,12 +498,12 @@ test_expect_equal () if ! test_skip $test_subtest_name then if [ $output = $expected ]; then - test_ok_ $test_subtest_name + test_ok_ else testname=$this_test.$test_count echo $expected $testname.expected echo $output $testname.output - test_failure_ $test_subtest_name $(diff -u $testname.expected $testname.output) + test_failure_ $(diff -u $testname.expected $testname.output) fi fi } @@ -520,12 +524,12 @@ test_expect_equal_file () if ! test_skip $test_subtest_name then if diff -q $file1 $file2 /dev/null ; then - test_ok_ $test_subtest_name + test_ok_ else testname=$this_test.$test_count cp $file1 $testname.$basename1 cp $file2 $testname.$basename2 - test_failure_ $test_subtest_name $(diff -u $testname.$basename1 $testname.$basename2) + test_failure_ $(diff -u $testname.$basename1 $testname.$basename2) fi fi } @@ -563,9 +567,9 @@ test_emacs_expect_t () { result=$(cat OUTPUT) if [ $result = t ] then - test_ok_ $test_subtest_name + test_ok_ else - test_failure_ $test_subtest_name ${result} + test_failure_ ${result} fi else # Restore state after the (non) test. @@ -666,12 +670,12 @@ test_require_external_prereq () { test_ok_ () { if test $test_subtest_known_broken_ = t; then - test_known_broken_ok_ $@ + test_known_broken_ok_ return fi test_success=$(($test_success + 1)) say_color pass %-6s PASS - echo $@ + echo $test_subtest_name } test_failure_ () { @@ -680,7 +684,7 @@ test_failure_ () { return fi test_failure=$(($test_failure + 1)) - test_failure_message_ FAIL $@ + test_failure_message_ FAIL $test_subtest_name $@ test $immediate = || { GIT_EXIT_OK=t; exit 1; } return 1 } @@ -697,13 +701,13 @@ test_known_broken_ok_ () { test_reset_state_ test_fixed=$(($test_fixed+1)) say_color pass %-6s FIXED - echo $@ + echo $test_subtest_name } test_known_broken_failure_ () { test_reset_state_ test_broken=$(($test_broken+1)) - test_failure_message_ BROKEN $@ + test_failure_message_ BROKEN $test_subtest_name $@ return 1 } @@ -771,6 +775,7 @@ test_expect_success () { test $# = 3 { prereq=$1; shift; } || prereq= test $# = 2 || error bug in the test script: not 2 or 3 parameters to test-expect-success + test_subtest_name=$1 test_reset_state_ if ! test_skip $@ then @@ -780,9 +785,9 @@ test_expect_success () { test_check_missing_external_prereqs_ $@ || if [ $run_ret = 0 -a $eval_ret = 0 ] then - test_ok_ $1 + test_ok_ else - test_failure_ $@ + test_failure_ $2 fi fi } @@ -791,6 +796,7 @@ test_expect_code () { test $# = 4 { prereq=$1; shift; } || prereq= test $# = 3 || error bug in the test script: not 3 or 4
[PATCH 1/1] NEWS: correction to section 0.15 for markdown processing
At the end of section 0.15 the strings TEST_EMACS TEST_EMACSCLIENT are now put inside ``:s for better output after markdown processing. --- NEWS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 1cb52dd..9d6bd19 100644 --- a/NEWS +++ b/NEWS @@ -182,8 +182,8 @@ Internal test framework changes The emacsclient binary is now user-configurable - The test framework now accepts TEST_EMACSCLIENT in addition to - TEST_EMACS for configuring the emacsclient to use. This is + The test framework now accepts `TEST_EMACSCLIENT` in addition to + `TEST_EMACS` for configuring the emacsclient to use. This is necessary to avoid using an old emacsclient with a new emacs, which can result in buggy behavior. -- 1.8.0 ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH 5/7] CLI: add --leak-report top level option
On Sat, Jan 19 2013, da...@tethera.net wrote: This roughly mimics the samba4 argument. The presence of the command line argument overrides any value of NOTMUCH_TALLOC_REPORT in the environment. --- man/man1/notmuch.1 |8 notmuch.c | 18 +++--- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/man/man1/notmuch.1 b/man/man1/notmuch.1 index 6bf9b2e..5c58c41 100644 --- a/man/man1/notmuch.1 +++ b/man/man1/notmuch.1 @@ -70,6 +70,14 @@ Print a synopsis of available commands and exit. Print the installed version of notmuch, and exit. .RE +.RS 4 +.TP 4 +.BI \-\-leak-report= path + +Write a detailed report of all memory allocated via talloc to +.I path +.RE Do we really need a command line option for this? Why isn't the env var sufficient? This just seems to me like it clutters the interface, for an option that is purely for debugging and should rarely if ever be used by most users. jamie. pgpZZK2xTThm3.pgp Description: PGP signature ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
Re: [PATCH 5/7] CLI: add --leak-report top level option
On Sun, Jan 20 2013, Jameson Graef Rollins jroll...@finestructure.net wrote: On Sat, Jan 19 2013, da...@tethera.net wrote: This roughly mimics the samba4 argument. The presence of the command line argument overrides any value of NOTMUCH_TALLOC_REPORT in the environment. --- man/man1/notmuch.1 |8 notmuch.c | 18 +++--- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/man/man1/notmuch.1 b/man/man1/notmuch.1 index 6bf9b2e..5c58c41 100644 --- a/man/man1/notmuch.1 +++ b/man/man1/notmuch.1 @@ -70,6 +70,14 @@ Print a synopsis of available commands and exit. Print the installed version of notmuch, and exit. .RE +.RS 4 +.TP 4 +.BI \-\-leak-report= path + +Write a detailed report of all memory allocated via talloc to +.I path +.RE Do we really need a command line option for this? Why isn't the env var sufficient? This just seems to me like it clutters the interface, for an option that is purely for debugging and should rarely if ever be used by most users. Jameson does have a point. Now that we already have that environment variable and it can be used in shipped notmuch 0.15 it is perhaps simplest just to stick with that. My thoughts after brief first visit to the patche series has so far being either make the command line usage 1:1 compatible with samba or use option like --leak-report-output=..., --leak-report-file=... or --leak-report-to=... (and attempt to deprecate the env var...) That said, I withdraw my previous suggestion of the command line option... The other changes in this patch series looks initially good -- and changes that drop deprecated features should be get in as early after last release as possible. jamie. Tomi ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH 2/2] test/test-lib.sh: separate signaled exit
When execution of tests is interrupted by signal coming outside of the test system itself, output just one line interrupted by signal num message to standard output. This distinguishes the case from internal exit and reduces noise. --- test/test-lib.sh | 15 ++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/test/test-lib.sh b/test/test-lib.sh index 0098bfd..e717c52 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -190,9 +190,15 @@ test_fixed=0 test_broken=0 test_success=0 -die () { +_die_common () { code=$? + trap - EXIT + set +ex rm -rf $TEST_TMPDIR +} + +die () { + _die_common if test -n $GIT_EXIT_OK then exit $code @@ -206,10 +212,17 @@ die () { fi } +die_signal () { + _die_common + echo 5 FATAL: $0: interrupted by signal $((code - 128)) + exit $code +} + GIT_EXIT_OK= # Note: TEST_TMPDIR *NOT* exported! TEST_TMPDIR=$(mktemp -d ${TMPDIR:-/tmp}/notmuch-test-$$.XX) trap 'die' EXIT +trap 'die_signal' HUP INT TERM test_decode_color () { sed -e 's/.\[1m/WHITE/g' \ -- 1.8.0 ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH 1/2] test/test-lib.sh: use $test_subtest_name in all tests
Set the variable '$test_subtest_name' in all functions which starts a new test and use that variable in all functions that output test results. Additionally output the latest '$test_subtest_name' in case of abnormal exit, to avoid confusion. --- This obsoletes id:1358717806-11376-1-git-send-email-tomi.oll...@iki.fi which had fd:s in function 'die' wrong order in 'exec [1]5' line. I did plenty of (ad hoc) hand-testing for this but failed to notice that messages weren't always as verbose as those should have been. The wip patch set mentioned below has it right but this was hand-rewritten as this is somewhat different here... The main reason to do this change is to get latest '$test_subtest_name' printed in case of abnormal exit. I cherry-picked this change from a larger work-in-progress patch set that adds 'set -e -o pipefail' support... I am pretty sure I got all the cases covered. If not, we'll notice it later when some test fail in a way I could not anticipate. Anyway, tests success fail as they used to be. test/test-lib.sh | 52 +--- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/test/test-lib.sh b/test/test-lib.sh index 6ce3b31..0098bfd 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -197,7 +197,11 @@ die () { then exit $code else - echo 5 FATAL: Unexpected exit with code $code + exec 5 + say_color error '%-6s' FATAL + echo $test_subtest_name + echo + echo Unexpected exit while executing $0. Exit code $code. exit 1 fi } @@ -494,12 +498,12 @@ test_expect_equal () if ! test_skip $test_subtest_name then if [ $output = $expected ]; then - test_ok_ $test_subtest_name + test_ok_ else testname=$this_test.$test_count echo $expected $testname.expected echo $output $testname.output - test_failure_ $test_subtest_name $(diff -u $testname.expected $testname.output) + test_failure_ $(diff -u $testname.expected $testname.output) fi fi } @@ -520,12 +524,12 @@ test_expect_equal_file () if ! test_skip $test_subtest_name then if diff -q $file1 $file2 /dev/null ; then - test_ok_ $test_subtest_name + test_ok_ else testname=$this_test.$test_count cp $file1 $testname.$basename1 cp $file2 $testname.$basename2 - test_failure_ $test_subtest_name $(diff -u $testname.$basename1 $testname.$basename2) + test_failure_ $(diff -u $testname.$basename1 $testname.$basename2) fi fi } @@ -563,9 +567,9 @@ test_emacs_expect_t () { result=$(cat OUTPUT) if [ $result = t ] then - test_ok_ $test_subtest_name + test_ok_ else - test_failure_ $test_subtest_name ${result} + test_failure_ ${result} fi else # Restore state after the (non) test. @@ -666,12 +670,12 @@ test_require_external_prereq () { test_ok_ () { if test $test_subtest_known_broken_ = t; then - test_known_broken_ok_ $@ + test_known_broken_ok_ return fi test_success=$(($test_success + 1)) say_color pass %-6s PASS - echo $@ + echo $test_subtest_name } test_failure_ () { @@ -680,7 +684,7 @@ test_failure_ () { return fi test_failure=$(($test_failure + 1)) - test_failure_message_ FAIL $@ + test_failure_message_ FAIL $test_subtest_name $@ test $immediate = || { GIT_EXIT_OK=t; exit 1; } return 1 } @@ -697,13 +701,13 @@ test_known_broken_ok_ () { test_reset_state_ test_fixed=$(($test_fixed+1)) say_color pass %-6s FIXED - echo $@ + echo $test_subtest_name } test_known_broken_failure_ () { test_reset_state_ test_broken=$(($test_broken+1)) - test_failure_message_ BROKEN $@ + test_failure_message_ BROKEN $test_subtest_name $@ return 1 } @@ -771,6 +775,7 @@ test_expect_success () { test $# = 3 { prereq=$1; shift; } || prereq= test $# = 2 || error bug in the test script: not 2 or 3 parameters to test-expect-success + test_subtest_name=$1 test_reset_state_ if ! test_skip $@ then @@ -780,9 +785,9 @@ test_expect_success () { test_check_missing_external_prereqs_ $@ || if [ $run_ret = 0 -a $eval_ret = 0 ]