]> git.notmuchmail.org Git - notmuch/blobdiff - notmuch.c
Move read-only-archive hint from "notmuch setup" to "notmuch new"
[notmuch] / notmuch.c
index ed087a070150e5d1771968a8833587010bc8efcf..f16192fb6b5f2a1fea206fde94b202e7eb409aca 100644 (file)
--- a/notmuch.c
+++ b/notmuch.c
@@ -54,6 +54,9 @@ typedef struct command {
 } command_t;
 
 typedef struct {
+    int ignore_read_only_directories;
+    int saw_read_only_directory;
+
     int total_files;
     int processed_files;
     int added_messages;
@@ -81,6 +84,11 @@ print_formatted_seconds (double seconds)
     int hours;
     int minutes;
 
+    if (seconds < 1) {
+       printf ("almost no time");
+       return;
+    }
+
     if (seconds > 3600) {
        hours = (int) seconds / 3600;
        printf ("%dh ", hours);
@@ -137,32 +145,36 @@ add_files_print_progress (add_files_state_t *state)
  *         same function.
  *
  *   o Tell the database to update its time of 'path' to 'path_mtime'
+ *
+ * The 'struct stat *st' must point to a structure that has already
+ * been initialized for 'path' by calling stat().
  */
 notmuch_status_t
-add_files (notmuch_database_t *notmuch, const char *path,
-          add_files_state_t *state)
+add_files_recursive (notmuch_database_t *notmuch,
+                    const char *path,
+                    struct stat *st,
+                    add_files_state_t *state)
 {
     DIR *dir = NULL;
     struct dirent *e, *entry = NULL;
     int entry_length;
     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;
+    /* If we're told to, we bail out on encountering a read-only
+     * directory, (with this being a clear clue from the user to
+     * Notmuch that new mail won't be arriving there and we need not
+     * look. */
+    if (state->ignore_read_only_directories &&
+       (st->st_mode & S_IWUSR) == 0)
+    {
+       state->saw_read_only_directory = TRUE;
+       goto DONE;
     }
 
-    path_mtime = st.st_mtime;
+    path_mtime = st->st_mtime;
 
     path_dbtime = notmuch_database_get_timestamp (notmuch, path);
 
@@ -210,17 +222,17 @@ add_files (notmuch_database_t *notmuch, const char *path,
 
        next = g_strdup_printf ("%s/%s", path, entry->d_name);
 
-       if (stat (next, &st)) {
+       if (stat (next, st)) {
            fprintf (stderr, "Error reading %s: %s\n",
                     next, strerror (errno));
            ret = NOTMUCH_STATUS_FILE_ERROR;
            continue;
        }
 
-       if (S_ISREG (st.st_mode)) {
+       if (S_ISREG (st->st_mode)) {
            /* 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) {
+           if (st->st_mtime > path_dbtime) {
                state->processed_files++;
 
                status = notmuch_database_add_message (notmuch, next);
@@ -250,8 +262,8 @@ add_files (notmuch_database_t *notmuch, const char *path,
                if (state->processed_files % 1000 == 0)
                    add_files_print_progress (state);
            }
-       } else if (S_ISDIR (st.st_mode)) {
-           status = add_files (notmuch, next, state);
+       } else if (S_ISDIR (st->st_mode)) {
+           status = add_files_recursive (notmuch, next, st, state);
            if (status && ret == NOTMUCH_STATUS_SUCCESS)
                ret = status;
        }
@@ -260,7 +272,9 @@ add_files (notmuch_database_t *notmuch, const char *path,
        next = NULL;
     }
 
-    notmuch_database_set_timestamp (notmuch, path, path_mtime);
+    status = notmuch_database_set_timestamp (notmuch, path, path_mtime);
+    if (status && ret == NOTMUCH_STATUS_SUCCESS)
+       ret = status;
 
   DONE:
     if (next)
@@ -273,6 +287,32 @@ add_files (notmuch_database_t *notmuch, const char *path,
     return ret;
 }
 
+/* This is the top-level entry point for add_files. It does a couple
+ * of error checks, and then calls into the recursive function,
+ * (avoiding the repeating of these error checks at every
+ * level---which would be useless becaues we already do a stat() at
+ * the level above). */
+static notmuch_status_t
+add_files (notmuch_database_t *notmuch,
+          const char *path,
+          add_files_state_t *state)
+{
+    struct stat st;
+
+    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;
+    }
+
+    return add_files_recursive (notmuch, path, &st, state);
+}
+
 /* Recursively count all regular files in path and all sub-direcotries
  * of path.  The result is added to *count (which should be
  * initialized to zero by the top-level caller before calling
@@ -424,6 +464,8 @@ setup_command (int argc, char *argv[])
 
     printf ("Next, we'll inspect the messages and create a database of threads:\n");
 
+    add_files_state.ignore_read_only_directories = FALSE;
+    add_files_state.saw_read_only_directory = FALSE;
     add_files_state.total_files = count;
     add_files_state.processed_files = 0;
     add_files_state.added_messages = 0;
@@ -434,15 +476,25 @@ setup_command (int argc, char *argv[])
     gettimeofday (&tv_now, NULL);
     elapsed = tv_elapsed (add_files_state.tv_start,
                          tv_now);
-    printf ("Processed %d total files in ", add_files_state.processed_files);
+    printf ("Processed %d %s in ", add_files_state.processed_files,
+           add_files_state.processed_files == 1 ?
+           "file" : "total files");
     print_formatted_seconds (elapsed);
-    printf (" (%d files/sec.).                 \n",
-           (int) (add_files_state.processed_files / elapsed));
-    printf ("Added %d unique messages to the database.\n\n",
-           add_files_state.added_messages);
+    if (elapsed > 1) {
+       printf (" (%d files/sec.).                 \n",
+               (int) (add_files_state.processed_files / elapsed));
+    } else {
+       printf (".                    \n");
+    }
+    if (add_files_state.added_messages) {
+       printf ("Added %d %s to the database.\n\n",
+               add_files_state.added_messages,
+               add_files_state.added_messages == 1 ?
+               "message" : "unique messages");
+    }
 
     printf ("When new mail is delivered to %s in the future,\n"
-           "run \"notmuch new\" to add it to the database.\n",
+           "run \"notmuch new\" to add it to the database.\n\n",
            mail_directory);
 
     if (ret) {
@@ -477,6 +529,8 @@ new_command (int argc, char *argv[])
 
     mail_directory = notmuch_database_get_path (notmuch);
 
+    add_files_state.ignore_read_only_directories = TRUE;
+    add_files_state.saw_read_only_directory = FALSE;
     add_files_state.total_files = 0;
     add_files_state.processed_files = 0;
     add_files_state.added_messages = 0;
@@ -488,20 +542,35 @@ new_command (int argc, char *argv[])
     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);
+       printf ("Processed %d %s in ", add_files_state.processed_files,
+               add_files_state.processed_files == 1 ?
+               "file" : "total files");
        print_formatted_seconds (elapsed);
-       printf (" (%d files/sec.).                 \n",
-               (int) (add_files_state.processed_files / elapsed));
+       if (elapsed > 1) {
+           printf (" (%d files/sec.).                 \n",
+                   (int) (add_files_state.processed_files / elapsed));
+       } else {
+           printf (".                    \n");
+       }
     }
     if (add_files_state.added_messages) {
-       printf ("Added %d new messages to the database (not much, really).\n",
-               add_files_state.added_messages);
+       printf ("Added %d new %s to the database (not much, really).\n",
+               add_files_state.added_messages,
+               add_files_state.added_messages == 1 ?
+               "message" : "messages");
     } else {
-       printf ("No new mail---and that's not much!.\n");
+       printf ("No new mail---and that's not much.\n");
+    }
+
+    if (elapsed > 1 && ! add_files_state.saw_read_only_directory) {
+       printf ("\nTip: If you have any sub-directories that are archives (that is,\n"
+               "they will never receive new mail), marking these directores as\n"
+               "read-only (chmod u-w /path/to/dir) will make \"notmuch new\"\n"
+               "much more efficient (it won't even look in those directories).\n");
     }
 
     if (ret) {
-       printf ("Note: At least one error was encountered: %s\n",
+       printf ("\nNote: At least one error was encountered: %s\n",
                notmuch_status_to_string (ret));
     }
 
@@ -515,8 +584,73 @@ new_command (int argc, char *argv[])
 int
 search_command (int argc, char *argv[])
 {
-    fprintf (stderr, "Error: search is not implemented yet.\n");
-    return 1;
+    void *local = talloc_new (NULL);
+    notmuch_database_t *notmuch = NULL;
+    notmuch_query_t *query;
+    notmuch_results_t *results;
+    notmuch_message_t *message;
+    notmuch_tags_t *tags;
+    char *query_str;
+    int i;
+    notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS;
+
+    notmuch = notmuch_database_open (NULL);
+    if (notmuch == NULL) {
+       ret = 1;
+       goto DONE;
+    }
+
+    /* XXX: Should add xtalloc wrappers here and use them. */
+    query_str = talloc_strdup (local, "");
+
+    for (i = 0; i < argc; i++) {
+       if (i != 0)
+           query_str = talloc_asprintf_append (query_str, " ");
+
+       query_str = talloc_asprintf_append (query_str, "%s", argv[i]);
+    }
+
+    query = notmuch_query_create (notmuch, query_str);
+    if (query == NULL) {
+       fprintf (stderr, "Out of memory\n");
+       ret = 1;
+       goto DONE;
+    }
+
+    for (results = notmuch_query_search (query);
+        notmuch_results_has_more (results);
+        notmuch_results_advance (results))
+    {
+       int first = 1;
+       message = notmuch_results_get (results);
+
+       printf ("%s (", notmuch_message_get_message_id (message));
+
+       for (tags = notmuch_message_get_tags (message);
+            notmuch_tags_has_more (tags);
+            notmuch_tags_advance (tags))
+       {
+           if (! first)
+               printf (" ");
+
+           printf ("%s", notmuch_tags_get (tags));
+
+           first = 0;
+       }
+
+       printf (")\n");
+
+       notmuch_message_destroy (message);
+    }
+
+    notmuch_query_destroy (query);
+
+  DONE:
+    if (notmuch)
+       notmuch_database_close (notmuch);
+    talloc_free (local);
+
+    return ret;
 }
 
 int
@@ -530,7 +664,7 @@ int
 dump_command (int argc, char *argv[])
 {
     FILE *output;
-    notmuch_database_t *notmuch;
+    notmuch_database_t *notmuch = NULL;
     notmuch_query_t *query;
     notmuch_results_t *results;
     notmuch_message_t *message;
@@ -606,7 +740,7 @@ int
 restore_command (int argc, char *argv[])
 {
     FILE *input;
-    notmuch_database_t *notmuch;
+    notmuch_database_t *notmuch = NULL;
     char *line = NULL;
     size_t line_size, line_len;
     regex_t regex;
@@ -710,11 +844,15 @@ restore_command (int argc, char *argv[])
 
 command_t commands[] = {
     { "setup", setup_command,
-      "Interactively setup notmuch for first use.\n"
+      "Interactively setup notmuch for first use.\n\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."},
+      "Find and import any new messages.\n\n"
+      "\t\tScans all sub-directories of the database, adding new files\n"
+      "\t\tthat are found. Note: \"notmuch new\" will skip any\n"
+      "\t\tread-only directories, so you can use that to mark\n"
+      "\t\tdirectories that will not receive any new mail."},
     { "search", search_command,
       "<search-term> [...]\n\n"
       "\t\tSearch for threads matching the given search terms.\n"