Add 'cat' subcommand

This command outputs a raw message matched by search term to the
standard output. It allows MUAs to access the messages for piping,
attachment manipulation, etc. by running notmuch cat rather then
directly access the file. This will simplify the MUAs when they need
to operate on a remote database.

Edited-by: Carl Worth <cworth@cworth.org>: Remove trailing whitespace,
add missing "test_done" to new test script to avoid "Unexpected exit"
error.
This commit is contained in:
Michal Sojka 2010-10-22 11:28:03 +02:00 committed by Carl Worth
parent 1fe7483d8d
commit d39d0e55f0
6 changed files with 135 additions and 1 deletions

View file

@ -110,6 +110,9 @@ notmuch_tag_command (void *ctx, int argc, char *argv[]);
int int
notmuch_search_tags_command (void *ctx, int argc, char *argv[]); notmuch_search_tags_command (void *ctx, int argc, char *argv[]);
int
notmuch_cat_command (void *ctx, int argc, char *argv[]);
int int
notmuch_part_command (void *ctx, int argc, char *argv[]); notmuch_part_command (void *ctx, int argc, char *argv[]);

View file

@ -631,6 +631,89 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[]))
return 0; return 0;
} }
int
notmuch_cat_command (void *ctx, unused (int argc), unused (char *argv[]))
{
notmuch_config_t *config;
notmuch_database_t *notmuch;
notmuch_query_t *query;
notmuch_messages_t *messages;
notmuch_message_t *message;
char *query_string;
int i;
const char *filename;
FILE *file;
size_t size;
char buf[4096];
for (i = 0; i < argc && argv[i][0] == '-'; i++) {
fprintf (stderr, "Unrecognized option: %s\n", argv[i]);
return 1;
}
config = notmuch_config_open (ctx, NULL, NULL);
if (config == NULL)
return 1;
query_string = query_string_from_args (ctx, argc, argv);
if (query_string == NULL) {
fprintf (stderr, "Out of memory\n");
return 1;
}
if (*query_string == '\0') {
fprintf (stderr, "Error: notmuch cat requires at least one search term.\n");
return 1;
}
notmuch = notmuch_database_open (notmuch_config_get_database_path (config),
NOTMUCH_DATABASE_MODE_READ_ONLY);
if (notmuch == NULL)
return 1;
query = notmuch_query_create (notmuch, query_string);
if (query == NULL) {
fprintf (stderr, "Error: Out of memory\n");
return 1;
}
if (notmuch_query_count_messages (query) != 1) {
fprintf (stderr, "Error: search term did not match precisely one message.\n");
return 1;
}
messages = notmuch_query_search_messages (query);
message = notmuch_messages_get (messages);
if (message == NULL) {
fprintf (stderr, "Error: Cannot find matching message.\n");
return 1;
}
filename = notmuch_message_get_filename (message);
if (filename == NULL) {
fprintf (stderr, "Error: Cannot message filename.\n");
return 1;
}
file = fopen (filename, "r");
if (file == NULL) {
fprintf (stderr, "Error: Cannot open file %s: %s\n", filename, strerror (errno));
return 1;
}
while (!feof (file)) {
size = fread (buf, 1, sizeof (buf), file);
fwrite (buf, size, 1, stdout);
}
fclose (file);
notmuch_query_destroy (query);
notmuch_database_close (notmuch);
return 0;
}
int int
notmuch_part_command (void *ctx, unused (int argc), unused (char *argv[])) notmuch_part_command (void *ctx, unused (int argc), unused (char *argv[]))
{ {

View file

@ -300,6 +300,10 @@ See the
section below for details of the supported syntax for <search-terms>. section below for details of the supported syntax for <search-terms>.
.RE .RE
.TP .TP
.BR cat " <search-term>..."
Output raw content of a single message matched by the search term.
.TP
.BR count " <search-term>..." .BR count " <search-term>..."
Count messages matching the search terms. Count messages matching the search terms.

View file

@ -343,6 +343,10 @@ command_t commands[] = {
"\tcontain tags only from messages that match the search-term(s).\n" "\tcontain tags only from messages that match the search-term(s).\n"
"\n" "\n"
"\tIn both cases the list will be alphabetically sorted." }, "\tIn both cases the list will be alphabetically sorted." },
{ "cat", notmuch_cat_command,
"<search-terms>",
"Output raw content of a single message matched by the search term.",
"" },
{ "part", notmuch_part_command, { "part", notmuch_part_command,
"--part=<num> <search-terms>", "--part=<num> <search-terms>",
"Output a single MIME part of a message.", "Output a single MIME part of a message.",

40
test/cat Executable file
View file

@ -0,0 +1,40 @@
#!/bin/bash
test_description='notmuch cat'
. ./test-lib.sh
test_begin_subtest "Generate some messages"
generate_message
generate_message
output=$(NOTMUCH_NEW)
test_expect_equal "$output" "Added 2 new messages to the database."
test_begin_subtest "Without arguments"
output=$(notmuch cat 2>&1)
test_expect_equal "$output" "Error: notmuch cat requires at least one search term."
test_begin_subtest "Attempt to cat multiple messages"
output=$(notmuch cat "*" 2>&1)
test_expect_equal "$output" "Error: search term did not match precisely one message."
test_begin_subtest "Cat a message"
output=$(notmuch cat id:msg-001@notmuch-test-suite)
test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
To: Notmuch Test Suite <test_suite@notmuchmail.org>
Message-Id: <msg-001@notmuch-test-suite>
Subject: Test message #1
Date: Tue, 05 Jan 2001 15:43:57 -0000
This is just a test message (#1)"
test_begin_subtest "Cat another message"
output=$(notmuch cat id:msg-002@notmuch-test-suite)
test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
To: Notmuch Test Suite <test_suite@notmuchmail.org>
Message-Id: <msg-002@notmuch-test-suite>
Subject: Test message #2
Date: Tue, 05 Jan 2001 15:43:57 -0000
This is just a test message (#2)"
test_done

View file

@ -16,7 +16,7 @@ fi
cd $(dirname "$0") cd $(dirname "$0")
TESTS="basic new search json thread-naming reply dump-restore uuencode thread-order author-order from-guessing long-id encoding emacs" TESTS="basic new search json thread-naming reply cat dump-restore uuencode thread-order author-order from-guessing long-id encoding emacs"
# Clean up any results from a previous run # Clean up any results from a previous run
rm -r test-results >/dev/null 2>/dev/null rm -r test-results >/dev/null 2>/dev/null