aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Carl Worth <cworth@cworth.org>2009-10-29 08:51:12 -0700
committerGravatar Carl Worth <cworth@cworth.org>2009-10-29 08:51:12 -0700
commitbf78a89196b251c2465f6cefa8198f22c87ff23d (patch)
tree3728d25ec1ec9317cb4aca314ab7f8fa932f15d4
parentb39ebca8c97fb19c52e46a2b9565c2d9fa0707d0 (diff)
notmuch show: Initial implementation (headers only)
We're using a delimiter syntax that Keith is optimistic about being able to easily parse in emacs. Note: We're not escaping any occurrence of the delimiters in the message yet, so we'll need to fix that.
-rw-r--r--message-file.c68
-rw-r--r--message.cc43
-rw-r--r--notmuch-private.h11
-rw-r--r--notmuch.c65
-rw-r--r--notmuch.h29
-rw-r--r--query.cc2
6 files changed, 193 insertions, 25 deletions
diff --git a/message-file.c b/message-file.c
index 18275fbf..e8e2843e 100644
--- a/message-file.c
+++ b/message-file.c
@@ -39,6 +39,7 @@ struct _notmuch_message_file {
GHashTable *headers;
int broken_headers;
int good_headers;
+ size_t header_size; /* Length of full message header in bytes. */
/* Parsing state */
char *line;
@@ -204,6 +205,9 @@ copy_header_unfolding (header_value_closure_t *value,
}
}
+/* As a special-case, a value of NULL for header_desired will force
+ * the entire header to be parsed if it is not parsed already. This is
+ * used by the _notmuch_message_file_get_headers_end function. */
const char *
notmuch_message_file_get_header (notmuch_message_file_t *message,
const char *header_desired)
@@ -215,9 +219,13 @@ notmuch_message_file_get_header (notmuch_message_file_t *message,
message->parsing_started = 1;
- contains = g_hash_table_lookup_extended (message->headers,
- header_desired, NULL,
- (gpointer *) &value);
+ if (header_desired == NULL)
+ contains = 0;
+ else
+ contains = g_hash_table_lookup_extended (message->headers,
+ header_desired, NULL,
+ (gpointer *) &value);
+
if (contains && value)
return value;
@@ -225,7 +233,7 @@ notmuch_message_file_get_header (notmuch_message_file_t *message,
return NULL;
#define NEXT_HEADER_LINE(closure) \
- do { \
+ while (1) { \
ssize_t bytes_read = getline (&message->line, \
&message->line_size, \
message->file); \
@@ -242,7 +250,11 @@ notmuch_message_file_get_header (notmuch_message_file_t *message,
{ \
copy_header_unfolding ((closure), message->line); \
} \
- } while (*message->line == ' ' || *message->line == '\t');
+ if (*message->line == ' ' || *message->line == '\t') \
+ message->header_size += strlen (message->line); \
+ else \
+ break; \
+ }
if (message->line == NULL)
NEXT_HEADER_LINE (NULL);
@@ -268,6 +280,8 @@ notmuch_message_file_get_header (notmuch_message_file_t *message,
continue;
}
+ message->header_size += strlen (message->line);
+
message->good_headers++;
header = xstrndup (message->line, colon - message->line);
@@ -290,7 +304,10 @@ notmuch_message_file_get_header (notmuch_message_file_t *message,
NEXT_HEADER_LINE (&message->value);
- match = (strcasecmp (header, header_desired) == 0);
+ if (header_desired == 0)
+ match = 0;
+ else
+ match = (strcasecmp (header, header_desired) == 0);
value = xstrdup (message->value.str);
@@ -314,7 +331,7 @@ notmuch_message_file_get_header (notmuch_message_file_t *message,
/* We've parsed all headers and never found the one we're looking
* for. It's probably just not there, but let's check that we
* didn't make a mistake preventing us from seeing it. */
- if (message->restrict_headers &&
+ if (message->restrict_headers && header_desired &&
! g_hash_table_lookup_extended (message->headers,
header_desired, NULL, NULL))
{
@@ -325,3 +342,40 @@ notmuch_message_file_get_header (notmuch_message_file_t *message,
return NULL;
}
+
+static size_t
+_notmuch_message_file_get_header_size (notmuch_message_file_t *message)
+{
+ if (! message->parsing_finished)
+ notmuch_message_file_get_header (message, NULL);
+
+ if (! message->parsing_finished)
+ INTERNAL_ERROR ("Parsing for NULL header did not force parsing to finish.\n");
+
+ return message->header_size;
+}
+
+const char *
+notmuch_message_file_get_all_headers (notmuch_message_file_t *message)
+{
+ char *headers = NULL;
+ size_t header_size = _notmuch_message_file_get_header_size (message);
+
+ if (header_size == 0)
+ return "";
+
+ headers = talloc_size (message, header_size + 1);
+ if (unlikely (headers == NULL))
+ return NULL;
+
+ rewind (message->file);
+ if (fread (headers, 1, header_size, message->file) != header_size) {
+ fprintf (stderr, "Error: Short read occurred trying to read message header.\n");
+ talloc_free (headers);
+ return NULL;
+ }
+
+ headers[header_size] = '\0';
+
+ return headers;
+}
diff --git a/message.cc b/message.cc
index 169b20c1..dfd55d59 100644
--- a/message.cc
+++ b/message.cc
@@ -232,26 +232,39 @@ notmuch_message_get_message_id (notmuch_message_t *message)
return message->message_id;
}
-const char *
-_notmuch_message_get_subject (notmuch_message_t *message)
+static void
+_notmuch_message_ensure_message_file (notmuch_message_t *message)
{
- if (! message->message_file) {
- notmuch_message_file_t *message_file;
- const char *filename;
+ const char *filename;
- filename = notmuch_message_get_filename (message);
- if (unlikely (filename == NULL))
- return NULL;
+ if (message->message_file)
+ return;
- message_file = _notmuch_message_file_open_ctx (message, filename);
- if (unlikely (message_file == NULL))
- return NULL;
+ filename = notmuch_message_get_filename (message);
+ if (unlikely (filename == NULL))
+ return;
- message->message_file = message_file;
- }
+ message->message_file = _notmuch_message_file_open_ctx (message, filename);
+}
+
+const char *
+notmuch_message_get_header (notmuch_message_t *message, const char *header)
+{
+ _notmuch_message_ensure_message_file (message);
+ if (message->message_file == NULL)
+ return NULL;
+
+ return notmuch_message_file_get_header (message->message_file, header);
+}
+
+const char *
+notmuch_message_get_all_headers (notmuch_message_t *message)
+{
+ _notmuch_message_ensure_message_file (message);
+ if (message->message_file == NULL)
+ return NULL;
- return notmuch_message_file_get_header (message->message_file,
- "subject");
+ return notmuch_message_file_get_all_headers (message->message_file);
}
const char *
diff --git a/notmuch-private.h b/notmuch-private.h
index da36c300..39d5e757 100644
--- a/notmuch-private.h
+++ b/notmuch-private.h
@@ -280,6 +280,17 @@ const char *
notmuch_message_file_get_header (notmuch_message_file_t *message,
const char *header);
+/* Get the entire set of headers from an email message as a string.
+ *
+ * The returned value is owned by the notmuch message and is valid
+ * only until the message is closed. The caller should copy it if
+ * needing to modify the value or to hold onto it for longer.
+ *
+ * Returns NULL in the case of any error.
+ */
+const char *
+notmuch_message_file_get_all_headers (notmuch_message_file_t *message);
+
/* date.c */
/* Parse an RFC 8222 date string to a time_t value.
diff --git a/notmuch.c b/notmuch.c
index 757f09d7..66b615b2 100644
--- a/notmuch.c
+++ b/notmuch.c
@@ -800,8 +800,69 @@ search_command (int argc, char *argv[])
static int
show_command (unused (int argc), unused (char *argv[]))
{
- fprintf (stderr, "Error: show is not implemented yet.\n");
- return 1;
+ void *local = talloc_new (NULL);
+ char *query_string;
+ notmuch_database_t *notmuch = NULL;
+ notmuch_query_t *query = NULL;
+ notmuch_message_results_t *messages;
+ notmuch_message_t *message;
+ int ret = 0;
+
+ if (argc != 1) {
+ fprintf (stderr, "Error: \"notmuch show\" requires exactly one thread-ID argument.\n");
+ ret = 1;
+ goto DONE;
+ }
+
+ notmuch = notmuch_database_open (NULL);
+ if (notmuch == NULL) {
+ ret = 1;
+ goto DONE;
+ }
+
+ query_string = talloc_asprintf (local, "thread:%s", argv[0]);
+ if (query_string == NULL) {
+ fprintf (stderr, "Out of memory\n");
+ ret = 1;
+ goto DONE;
+ }
+
+ query = notmuch_query_create (notmuch, query_string);
+ if (query == NULL) {
+ fprintf (stderr, "Out of memory\n");
+ ret = 1;
+ goto DONE;
+ }
+
+ for (messages = notmuch_query_search_messages (query);
+ notmuch_message_results_has_more (messages);
+ notmuch_message_results_advance (messages))
+ {
+ message = notmuch_message_results_get (messages);
+
+ printf ("%%message{\n");
+
+ printf ("%%header{\n");
+
+ printf ("%s", notmuch_message_get_all_headers (message));
+
+ printf ("%%header}\n");
+ printf ("%%message}\n");
+
+ notmuch_message_destroy (message);
+ }
+
+ DONE:
+ if (local)
+ talloc_free (local);
+
+ if (query)
+ notmuch_query_destroy (query);
+
+ if (notmuch)
+ notmuch_database_close (notmuch);
+
+ return ret;
}
static int
diff --git a/notmuch.h b/notmuch.h
index 165fe852..a17c7020 100644
--- a/notmuch.h
+++ b/notmuch.h
@@ -617,6 +617,35 @@ notmuch_message_get_thread_id (notmuch_message_t *message);
const char *
notmuch_message_get_filename (notmuch_message_t *message);
+/* Get the value of the specified header from 'message'.
+ *
+ * The value will be read from the actual message file, not from the
+ * notmuch database. The header name is case insensitive.
+ *
+ * The returned string belongs to the message so should not be
+ * modified or freed by the caller (nor should it be referenced after
+ * the message is destroyed).
+ *
+ * Returns NULL if the message does not contain a header line matching
+ * 'header' of if any error occurs.
+ */
+const char *
+notmuch_message_get_header (notmuch_message_t *message, const char *header);
+
+/* Get the entire set of headers from an email message as a string.
+ *
+ * The value will be read from the actual message file, not from the
+ * notmuch database.
+ *
+ * The returned value is owned by the notmuch message and is valid
+ * only until the message is closed. The caller should copy it if
+ * needing to modify the value or to hold onto it for longer.
+ *
+ * Returns NULL in the case of any error.
+ */
+const char *
+notmuch_message_get_all_headers (notmuch_message_t *message);
+
/* Get the tags for 'message', returning a notmuch_tags_t object which
* can be used to iterate over all tags.
*
diff --git a/query.cc b/query.cc
index 5fac024e..170ab752 100644
--- a/query.cc
+++ b/query.cc
@@ -212,7 +212,7 @@ notmuch_query_search_threads (notmuch_query_t *query)
thread = _notmuch_thread_create (query, query->notmuch,
thread_id);
- subject = _notmuch_message_get_subject (message);
+ subject = notmuch_message_get_header (message, "subject");
_notmuch_thread_set_subject (thread, subject);