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.
This commit is contained in:
Carl Worth 2009-10-23 15:48:05 -07:00
parent 9577e8a105
commit 491ece966f

140
notmuch.c
View file

@ -121,8 +121,23 @@ add_files_print_progress (add_files_state_t *state)
fflush (stdout); fflush (stdout);
} }
/* Recursively find all regular files in 'path' and add them to the /* Examine 'path' recursively as follows:
* database. */ *
* 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 notmuch_status_t
add_files (notmuch_database_t *notmuch, const char *path, add_files (notmuch_database_t *notmuch, const char *path,
add_files_state_t *state) add_files_state_t *state)
@ -133,11 +148,27 @@ add_files (notmuch_database_t *notmuch, const char *path,
int err; int err;
char *next = NULL; char *next = NULL;
struct stat st; struct stat st;
time_t path_mtime, path_dbtime;
notmuch_status_t status, ret = NOTMUCH_STATUS_SUCCESS; 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); dir = opendir (path);
if (dir == NULL) { if (dir == NULL) {
fprintf (stderr, "Warning: failed to open directory %s: %s\n", fprintf (stderr, "Error opening directory %s: %s\n",
path, strerror (errno)); path, strerror (errno));
ret = NOTMUCH_STATUS_FILE_ERROR; ret = NOTMUCH_STATUS_FILE_ERROR;
goto DONE; goto DONE;
@ -159,6 +190,12 @@ add_files (notmuch_database_t *notmuch, const char *path,
if (e == NULL) if (e == NULL)
break; 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. /* Ignore special directories to avoid infinite recursion.
* Also ignore the .notmuch directory. * Also ignore the .notmuch directory.
*/ */
@ -181,16 +218,38 @@ add_files (notmuch_database_t *notmuch, const char *path,
} }
if (S_ISREG (st.st_mode)) { if (S_ISREG (st.st_mode)) {
state->processed_files++; /* If the file hasn't been modified since the last
status = notmuch_database_add_message (notmuch, next); * add_files, then we need not look at it. */
if (status == NOTMUCH_STATUS_FILE_NOT_EMAIL) { if (st.st_mtime > path_dbtime) {
fprintf (stderr, "Note: Ignoring non-mail file: %s\n", state->processed_files++;
next);
} else if (status != NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID) { status = notmuch_database_add_message (notmuch, next);
state->added_messages++; 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)) { } else if (S_ISDIR (st.st_mode)) {
status = add_files (notmuch, next, state); status = add_files (notmuch, next, state);
if (status && ret == NOTMUCH_STATUS_SUCCESS) if (status && ret == NOTMUCH_STATUS_SUCCESS)
@ -201,6 +260,8 @@ add_files (notmuch_database_t *notmuch, const char *path,
next = NULL; next = NULL;
} }
notmuch_database_set_timestamp (notmuch, path, path_mtime);
DONE: DONE:
if (next) if (next)
free (next); free (next);
@ -398,6 +459,59 @@ setup_command (int argc, char *argv[])
return ret; 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 int
search_command (int argc, char *argv[]) search_command (int argc, char *argv[])
{ {
@ -599,6 +713,8 @@ command_t commands[] = {
"Interactively setup notmuch for first use.\n" "Interactively setup notmuch for first use.\n"
"\t\tInvoking notmuch with no command argument will run setup if\n" "\t\tInvoking notmuch with no command argument will run setup if\n"
"\t\tthe setup command has not previously been completed." }, "\t\tthe setup command has not previously been completed." },
{ "new", new_command,
"Find and import any new messages."},
{ "search", search_command, { "search", search_command,
"<search-term> [...]\n\n" "<search-term> [...]\n\n"
"\t\tSearch for threads matching the given search terms.\n" "\t\tSearch for threads matching the given search terms.\n"