+static const char *
+skip_re_in_subject (const char *subject)
+{
+ const char *s = subject;
+
+ while (*s) {
+ while (*s && isspace (*s))
+ s++;
+ if (strncasecmp (s, "re:", 3) == 0)
+ s += 3;
+ else
+ break;
+ }
+
+ return s;
+}
+
+Xapian::Document
+find_message (Xapian::Database db, const char *message_id)
+{
+ Xapian::PostingIterator i;
+ char *term;
+
+ term = g_strdup_printf ("%s%s", find_prefix ("msgid"), message_id);
+ i = db.postlist_begin (term);
+ if (i != db.postlist_end (term))
+ return db.get_document (*i);
+ else
+ return Xapian::Document ();
+}
+
+static char *
+find_thread_id (Xapian::Database db, GPtrArray *parents)
+{
+ Xapian::Document doc;
+ GHashTable *thread_ids;
+ GList *keys, *l;
+ GString *result = NULL;
+ unsigned int i;
+ string value_string;
+ const char *value;
+
+ thread_ids = g_hash_table_new (g_str_hash, g_str_equal);
+
+ for (i = 0; i < parents->len; i++) {
+ doc = find_message (db, (char *) g_ptr_array_index (parents, i));
+ value_string = doc.get_value (NOTMUCH_VALUE_THREAD);
+ value = value_string.c_str();
+ if (strlen (value))
+ g_hash_table_insert (thread_ids, strdup (value), NULL);
+ }
+
+ keys = g_hash_table_get_keys (thread_ids);
+ for (l = keys; l; l = l->next) {
+ char *id = (char *) l->data;
+ if (result == NULL) {
+ result = g_string_new (id);
+ } else {
+ g_string_append_printf (result, ",%s", id);
+ }
+ free (id);
+ }
+
+ if (result)
+ return g_string_free (result, FALSE);
+ else
+ return NULL;
+}
+
+/* Add a term for each message-id in the References header of the
+ * message. */
+static void
+parse_references (GPtrArray *array,
+ const char *refs)
+{
+ const char *end, *next;
+
+ if (refs == NULL)
+ return;
+
+ while (*refs) {
+ while (*refs && isspace (*refs))
+ refs++;
+ if (*refs == '<')
+ refs++;
+ end = refs;
+ while (*end && !isspace (*end))
+ end++;
+ next = end;
+ end--;
+ if (end > refs && *end == '>')
+ end--;
+ if (end > refs) {
+ g_ptr_array_add (array, g_strndup (refs, end - refs + 1));
+ }
+ refs = next;
+ }
+}
+
+/* Generate terms for the body of a message, given the filename of the
+ * message and the offset at which the headers of the message end,
+ * (and hence the body begins). */
+static void
+gen_terms_body (Xapian::TermGenerator term_gen,
+ const char * filename,
+ gint64 body_offset)
+{
+ GIOChannel *channel;
+ GIOStatus gio_status;
+ GError *error = NULL;
+ char *p, *body_line = NULL, *prev_line = NULL;
+
+ channel = g_io_channel_new_file (filename, "r", &error);
+ if (channel == NULL) {
+ fprintf (stderr, "Error: %s\n", error->message);
+ exit (1);
+ }
+
+ gio_status = g_io_channel_seek_position (channel, body_offset,
+ G_SEEK_SET, &error);
+ if (gio_status != G_IO_STATUS_NORMAL) {
+ fprintf (stderr, "Error: %s\n", error->message);
+ exit (1);
+ }
+
+ while (1) {
+ if (body_line)
+ g_free (body_line);
+
+ gio_status = g_io_channel_read_line (channel, &body_line,
+ NULL, NULL, &error);
+ if (gio_status == G_IO_STATUS_EOF)
+ break;
+ if (gio_status != G_IO_STATUS_NORMAL) {
+ fprintf (stderr, "Error: %s\n", error->message);
+ exit (1);
+ }
+
+ if (strlen (body_line) == 0)
+ continue;
+
+ /* If the line looks like it might be introducing a quote,
+ * save it until we see if the next line begins a quote. */
+ p = body_line + strlen (body_line) - 1;
+ while (p > body_line and isspace (*p))
+ p--;
+ if (*p == ':') {
+ prev_line = body_line;
+ body_line = NULL;
+ continue;
+ }
+
+ /* Skip quoted lines, (and previous lines that introduced them) */
+ if (body_line[0] == '>') {
+ if (prev_line) {
+ g_free (prev_line);
+ prev_line = NULL;
+ }
+ continue;
+ }
+
+ /* Now that we're not looking at a quote we can add the prev_line */
+ if (prev_line) {
+ gen_terms (term_gen, "body", prev_line);
+ g_free (prev_line);
+ prev_line = NULL;
+ }
+
+ /* Skip signatures */
+ /* XXX: Should only do this if "near" the end of the message. */
+ if (strncmp (body_line, "-- ", 3) == 0 ||
+ strncmp (body_line, "----------", 10) == 0 ||
+ strncmp (body_line, "__________", 10) == 0)
+ break;
+
+ gen_terms (term_gen, "body", body_line);
+ }
+
+ if (body_line)
+ g_free (body_line);
+
+ g_io_channel_close (channel);
+}
+
+static void
+index_file (Xapian::WritableDatabase db,
+ Xapian::TermGenerator term_gen,
+ const char *filename)