From: Carl Worth Date: Fri, 23 Oct 2009 22:48:05 +0000 (-0700) Subject: Add new "notmuch new" command. X-Git-Tag: 0.1~743 X-Git-Url: https://git.notmuchmail.org/git?p=notmuch;a=commitdiff_plain;h=491ece966fbc0e8bcd14f2db87b10a6af82814cc Add new "notmuch new" command. Finally, I can get new messages into my notmuch database without having to run a complete "notmuch setup" again. This takes advantage of the recent timestamp capabilities in the database to avoid looking into directories that haven't changed since the last time "notmuch new" was run. --- diff --git a/notmuch.c b/notmuch.c index bd739c85..ed087a07 100644 --- a/notmuch.c +++ b/notmuch.c @@ -121,8 +121,23 @@ add_files_print_progress (add_files_state_t *state) fflush (stdout); } -/* Recursively find all regular files in 'path' and add them to the - * database. */ +/* Examine 'path' recursively as follows: + * + * o Ask the filesystem for the mtime of 'path' (path_mtime) + * + * o Ask the database for its timestamp of 'path' (path_dbtime) + * + * o If 'path_mtime' > 'path_dbtime' + * + * o For each regular file in 'path' with mtime newer than the + * 'path_dbtime' call add_message to add the file to the + * database. + * + * o For each sub-directory of path, recursively call into this + * same function. + * + * o Tell the database to update its time of 'path' to 'path_mtime' + */ notmuch_status_t add_files (notmuch_database_t *notmuch, const char *path, add_files_state_t *state) @@ -133,11 +148,27 @@ add_files (notmuch_database_t *notmuch, const char *path, int err; char *next = NULL; struct stat st; + time_t path_mtime, path_dbtime; notmuch_status_t status, ret = NOTMUCH_STATUS_SUCCESS; + if (stat (path, &st)) { + fprintf (stderr, "Error reading directory %s: %s\n", + path, strerror (errno)); + return NOTMUCH_STATUS_FILE_ERROR; + } + + if (! S_ISDIR (st.st_mode)) { + fprintf (stderr, "Error: %s is not a directory.\n", path); + return NOTMUCH_STATUS_FILE_ERROR; + } + + path_mtime = st.st_mtime; + + path_dbtime = notmuch_database_get_timestamp (notmuch, path); + dir = opendir (path); if (dir == NULL) { - fprintf (stderr, "Warning: failed to open directory %s: %s\n", + fprintf (stderr, "Error opening directory %s: %s\n", path, strerror (errno)); ret = NOTMUCH_STATUS_FILE_ERROR; goto DONE; @@ -159,6 +190,12 @@ add_files (notmuch_database_t *notmuch, const char *path, if (e == NULL) break; + /* If this directory hasn't been modified since the last + * add_files, then we only need to look further for + * sub-directories. */ + if (path_mtime <= path_dbtime && entry->d_type != DT_DIR) + continue; + /* Ignore special directories to avoid infinite recursion. * Also ignore the .notmuch directory. */ @@ -181,16 +218,38 @@ add_files (notmuch_database_t *notmuch, const char *path, } if (S_ISREG (st.st_mode)) { - state->processed_files++; - status = notmuch_database_add_message (notmuch, next); - if (status == NOTMUCH_STATUS_FILE_NOT_EMAIL) { - fprintf (stderr, "Note: Ignoring non-mail file: %s\n", - next); - } else if (status != NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID) { - state->added_messages++; + /* If the file hasn't been modified since the last + * add_files, then we need not look at it. */ + if (st.st_mtime > path_dbtime) { + state->processed_files++; + + status = notmuch_database_add_message (notmuch, next); + switch (status) { + /* success */ + case NOTMUCH_STATUS_SUCCESS: + state->added_messages++; + break; + /* Non-fatal issues (go on to next file) */ + case NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID: + /* Stay silent on this one. */ + break; + case NOTMUCH_STATUS_FILE_NOT_EMAIL: + fprintf (stderr, "Note: Ignoring non-mail file: %s\n", + next); + break; + /* Fatal issues. Don't process anymore. */ + case NOTMUCH_STATUS_XAPIAN_EXCEPTION: + fprintf (stderr, "A Xapian error was encountered. Halting processing.\n"); + ret = status; + goto DONE; + default: + fprintf (stderr, "Internal error: add_message returned unexpected value: %d\n", status); + ret = status; + goto DONE; + } + if (state->processed_files % 1000 == 0) + add_files_print_progress (state); } - if (state->processed_files % 1000 == 0) - add_files_print_progress (state); } else if (S_ISDIR (st.st_mode)) { status = add_files (notmuch, next, state); if (status && ret == NOTMUCH_STATUS_SUCCESS) @@ -201,6 +260,8 @@ add_files (notmuch_database_t *notmuch, const char *path, next = NULL; } + notmuch_database_set_timestamp (notmuch, path, path_mtime); + DONE: if (next) free (next); @@ -398,6 +459,59 @@ setup_command (int argc, char *argv[]) return ret; } +int +new_command (int argc, char *argv[]) +{ + notmuch_database_t *notmuch; + const char *mail_directory; + add_files_state_t add_files_state; + double elapsed; + struct timeval tv_now; + int ret = 0; + + notmuch = notmuch_database_open (NULL); + if (notmuch == NULL) { + ret = 1; + goto DONE; + } + + mail_directory = notmuch_database_get_path (notmuch); + + add_files_state.total_files = 0; + add_files_state.processed_files = 0; + add_files_state.added_messages = 0; + gettimeofday (&add_files_state.tv_start, NULL); + + ret = add_files (notmuch, mail_directory, &add_files_state); + + gettimeofday (&tv_now, NULL); + elapsed = tv_elapsed (add_files_state.tv_start, + tv_now); + if (add_files_state.processed_files) { + printf ("Processed %d total files in ", add_files_state.processed_files); + print_formatted_seconds (elapsed); + printf (" (%d files/sec.). \n", + (int) (add_files_state.processed_files / elapsed)); + } + if (add_files_state.added_messages) { + printf ("Added %d new messages to the database (not much, really).\n", + add_files_state.added_messages); + } else { + printf ("No new mail---and that's not much!.\n"); + } + + if (ret) { + printf ("Note: At least one error was encountered: %s\n", + notmuch_status_to_string (ret)); + } + + DONE: + if (notmuch) + notmuch_database_close (notmuch); + + return ret; +} + int search_command (int argc, char *argv[]) { @@ -599,6 +713,8 @@ command_t commands[] = { "Interactively setup notmuch for first use.\n" "\t\tInvoking notmuch with no command argument will run setup if\n" "\t\tthe setup command has not previously been completed." }, + { "new", new_command, + "Find and import any new messages."}, { "search", search_command, " [...]\n\n" "\t\tSearch for threads matching the given search terms.\n"