]> git.notmuchmail.org Git - notmuch/blobdiff - notmuch.c
notmuch: Ignore files that don't look like email messages.
[notmuch] / notmuch.c
index 73d9a9acf2483e711415ba4330a977d5c9688882..01000c2a823c8edc815addca4534e2a8b44ff962 100644 (file)
--- a/notmuch.c
+++ b/notmuch.c
  * Author: Carl Worth <cworth@cworth.org>
  */
 
  * Author: Carl Worth <cworth@cworth.org>
  */
 
+#include "notmuch.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/time.h>
 #include <unistd.h>
 #include <dirent.h>
 #include <errno.h>
 
 #include <unistd.h>
 #include <dirent.h>
 #include <errno.h>
 
-#include <glib.h>
+#include <glib.h> /* GIOChannel */
 
 #define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr[0]))
 
 
 #define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr[0]))
 
@@ -75,6 +78,137 @@ read_line (void)
     return result;
 }
 
     return result;
 }
 
+typedef struct {
+    int total_messages;
+    int count;
+    struct timeval tv_start;
+} add_files_state_t;
+
+/* Compute the number of seconds elapsed from start to end. */
+double
+tv_elapsed (struct timeval start, struct timeval end)
+{
+    return ((end.tv_sec - start.tv_sec) +
+           (end.tv_usec - start.tv_usec) / 1e6);
+}
+
+void
+print_formatted_seconds (double seconds)
+{
+    int hours;
+    int minutes;
+
+    if (seconds > 3600) {
+       hours = (int) seconds / 3600;
+       printf ("%dh ", hours);
+       seconds -= hours * 3600;
+    }
+
+    if (seconds > 60) {
+       minutes = (int) seconds / 60;
+       printf ("%dm ", minutes);
+       seconds -= minutes * 60;
+    }
+
+    printf ("%02ds", (int) seconds);
+}
+
+void
+add_files_print_progress (add_files_state_t *state)
+{
+    struct timeval tv_now;
+    double elapsed_overall, rate_overall;
+
+    gettimeofday (&tv_now, NULL);
+
+    elapsed_overall = tv_elapsed (state->tv_start, tv_now);
+    rate_overall = (state->count) / elapsed_overall;
+
+    printf ("Added %d of %d messages (",
+           state->count, state->total_messages);
+    print_formatted_seconds ((state->total_messages - state->count) /
+                            rate_overall);
+    printf (" remaining).\r");
+
+    fflush (stdout);
+}
+
+/* Recursively find all regular files in 'path' and add them to the
+ * database. */
+void
+add_files (notmuch_database_t *notmuch, const char *path,
+          add_files_state_t *state)
+{
+    DIR *dir;
+    struct dirent *entry, *e;
+    int entry_length;
+    int err;
+    char *next;
+    struct stat st;
+    notmuch_status_t status;
+
+    dir = opendir (path);
+
+    if (dir == NULL) {
+       fprintf (stderr, "Warning: failed to open directory %s: %s\n",
+                path, strerror (errno));
+       return;
+    }
+
+    entry_length = offsetof (struct dirent, d_name) +
+       pathconf (path, _PC_NAME_MAX) + 1;
+    entry = malloc (entry_length);
+
+    while (1) {
+       err = readdir_r (dir, entry, &e);
+       if (err) {
+           fprintf (stderr, "Error reading directory: %s\n",
+                    strerror (errno));
+           free (entry);
+           return;
+       }
+
+       if (e == NULL)
+           break;
+
+       /* Ignore special directories to avoid infinite recursion.
+        * Also ignore the .notmuch directory.
+        */
+       /* XXX: Eventually we'll want more sophistication to let the
+        * user specify files to be ignored. */
+       if (strcmp (entry->d_name, ".") == 0 ||
+           strcmp (entry->d_name, "..") == 0 ||
+           strcmp (entry->d_name, ".notmuch") ==0)
+       {
+           continue;
+       }
+
+       next = g_strdup_printf ("%s/%s", path, entry->d_name);
+
+       stat (next, &st);
+
+       if (S_ISREG (st.st_mode)) {
+           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 {
+               state->count++;
+           }
+           if (state->count % 1000 == 0)
+               add_files_print_progress (state);
+       } else if (S_ISDIR (st.st_mode)) {
+           add_files (notmuch, next, state);
+       }
+
+       free (next);
+    }
+
+    free (entry);
+
+    closedir (dir);
+}
+
 /* 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
 /* 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
@@ -113,9 +247,14 @@ count_files (const char *path, int *count)
        if (e == NULL)
            break;
 
        if (e == NULL)
            break;
 
-       /* Skip these special directories to avoid infinite recursion. */
+       /* Ignore special directories to avoid infinite recursion.
+        * Also ignore the .notmuch directory.
+        */
+       /* XXX: Eventually we'll want more sophistication to let the
+        * user specify files to be ignored. */
        if (strcmp (entry->d_name, ".") == 0 ||
        if (strcmp (entry->d_name, ".") == 0 ||
-           strcmp (entry->d_name, "..") == 0)
+           strcmp (entry->d_name, "..") == 0 ||
+           strcmp (entry->d_name, ".notmuch") == 0)
        {
            continue;
        }
        {
            continue;
        }
@@ -124,14 +263,14 @@ count_files (const char *path, int *count)
 
        stat (next, &st);
 
 
        stat (next, &st);
 
-       if (S_ISREG (st.st_mode))
+       if (S_ISREG (st.st_mode)) {
            *count = *count + 1;
            *count = *count + 1;
-       else if (S_ISDIR (st.st_mode))
+           if (*count % 1000 == 0) {
+               printf ("Found %d files so far.\r", *count);
+               fflush (stdout);
+           }
+       } else if (S_ISDIR (st.st_mode)) {
            count_files (next, count);
            count_files (next, count);
-
-       if (*count % 1000 == 0) {
-           printf ("Found %d files so far.\r", *count);
-           fflush (stdout);
        }
 
        free (next);
        }
 
        free (next);
@@ -145,8 +284,12 @@ count_files (const char *path, int *count)
 int
 setup_command (int argc, char *argv[])
 {
 int
 setup_command (int argc, char *argv[])
 {
+    notmuch_database_t *notmuch;
     char *mail_directory;
     int count;
     char *mail_directory;
     int count;
+    add_files_state_t add_files_state;
+    double elapsed;
+    struct timeval tv_now;
 
     printf ("Welcome to notmuch!\n\n");
 
 
     printf ("Welcome to notmuch!\n\n");
 
@@ -156,9 +299,10 @@ setup_command (int argc, char *argv[])
     printf ("Notmuch needs to know the top-level directory of your email archive,\n"
            "(where you already have mail stored and where messages will be delivered\n"
            "in the future). This directory can contain any number of sub-directories\n"
     printf ("Notmuch needs to know the top-level directory of your email archive,\n"
            "(where you already have mail stored and where messages will be delivered\n"
            "in the future). This directory can contain any number of sub-directories\n"
-           "but the only files it contains should be individual email messages.\n"
-           "Either maildir or mh format directories are fine, but you will want to\n"
-           "move away any auxiliary files maintained by other email programs.\n\n");
+           "and primarily just files with indvidual email messages (eg. maildir or mh\n"
+           "archives are perfect). If there are other, non-email files (such as\n"
+           "indexes maintained by other email programs) then notmuch will do its\n"
+           "best to detect those and ignore them.\n\n");
 
     printf ("Mail storage that uses mbox format, (where one mbox file contains many\n"
            "messages), will not work with notmuch. If that's how your mail is currently\n"
 
     printf ("Mail storage that uses mbox format, (where one mbox file contains many\n"
            "messages), will not work with notmuch. If that's how your mail is currently\n"
@@ -187,13 +331,38 @@ setup_command (int argc, char *argv[])
        mail_directory = g_strdup_printf ("%s/mail", home);
     }
 
        mail_directory = g_strdup_printf ("%s/mail", home);
     }
 
+    notmuch = notmuch_database_create (mail_directory);
+    if (notmuch == NULL) {
+       fprintf (stderr, "Failed to create new notmuch database at %s\n",
+                mail_directory);
+       free (mail_directory);
+       return 1;
+    }
+
     printf ("OK. Let's take a look at the mail we can find in the directory\n");
     printf ("%s ...\n", mail_directory);
 
     count = 0;
     count_files (mail_directory, &count);
 
     printf ("OK. Let's take a look at the mail we can find in the directory\n");
     printf ("%s ...\n", mail_directory);
 
     count = 0;
     count_files (mail_directory, &count);
 
-    printf ("Found %d total files. That's not much mail.\n", count);
+    printf ("Found %d total files. That's not much mail.\n\n", count);
+
+    printf ("Next, we'll inspect the messages and create a database of threads:\n");
+
+    add_files_state.total_messages = count;
+    add_files_state.count = 0;
+    gettimeofday (&add_files_state.tv_start, NULL);
+
+    add_files (notmuch, mail_directory, &add_files_state);
+
+    gettimeofday (&tv_now, NULL);
+    elapsed = tv_elapsed (add_files_state.tv_start,
+                         tv_now);
+    printf ("Added %d total messages in ", add_files_state.count);
+    print_formatted_seconds (elapsed);
+    printf (" (%d messages/sec.).                 \n", (int) (add_files_state.count / elapsed));
+
+    notmuch_database_close (notmuch);
 
     free (mail_directory);
     
 
     free (mail_directory);