]> git.notmuchmail.org Git - notmuch/blobdiff - notmuch-new.c
lib: Treat NULL as a valid (and empty) notmuch_filenames_t iterator.
[notmuch] / notmuch-new.c
index f8fe66a3b153c58e2bb1e0652d97623853cd0576..7d15fe9184183401ab96556ff9f6d7c6168be8c9 100644 (file)
@@ -229,15 +229,18 @@ add_files_recursive (notmuch_database_t *notmuch,
        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;
-    }
+    /* This is not an error since we may have recursed based on a
+     * symlink to a regular file, not a directory, and we don't know
+     * that until this stat. */
+    if (! S_ISDIR (st.st_mode))
+       return NOTMUCH_STATUS_SUCCESS;
 
     fs_mtime = st.st_mtime;
 
     directory = notmuch_database_get_directory (notmuch, path);
     db_mtime = notmuch_directory_get_mtime (directory);
+    db_files = notmuch_directory_get_child_files (directory);
+    db_subdirs = notmuch_directory_get_child_directories (directory);
 
     /* If the database knows about this directory, then we sort based
      * on strcmp to match the database sorting. Otherwise, we can do
@@ -262,7 +265,7 @@ add_files_recursive (notmuch_database_t *notmuch,
 
        entry = fs_entries[i];
 
-       if (entry->d_type != DT_DIR)
+       if (entry->d_type != DT_DIR && entry->d_type != DT_LNK)
            continue;
 
        /* Ignore special directories to avoid infinite recursion.
@@ -293,9 +296,6 @@ add_files_recursive (notmuch_database_t *notmuch,
        goto DONE;
 
     /* Pass 2: Scan for new files, removed files, and removed directories. */
-    db_files = notmuch_directory_get_child_files (directory);
-    db_subdirs = notmuch_directory_get_child_directories (directory);
-
     for (i = 0; i < num_fs_entries; i++)
     {
        if (interrupted)
@@ -333,8 +333,27 @@ add_files_recursive (notmuch_database_t *notmuch,
            notmuch_filenames_advance (db_subdirs);
        }
 
-       if (entry->d_type != DT_REG)
+       /* If we're looking at a symlink, we only want to add it if it
+        * links to a regular file, (and not to a directory, say). */
+       if (entry->d_type == DT_LNK) {
+           int err;
+
+           next = talloc_asprintf (notmuch, "%s/%s", path, entry->d_name);
+           err = stat (next, &st);
+           talloc_free (next);
+           next = NULL;
+
+           /* Don't emit an error for a link pointing nowhere, since
+            * the directory-traversal pass will have already done
+            * that. */
+           if (err)
+               continue;
+
+           if (! S_ISREG (st.st_mode))
+               continue;
+       } else if (entry->d_type != DT_REG) {
            continue;
+       }
 
        /* Don't add a file that we've added before. */
        if (notmuch_filenames_has_more (db_files) &&
@@ -344,7 +363,7 @@ add_files_recursive (notmuch_database_t *notmuch,
            continue;
        }
 
-       /* We're not looking at a regular file that doesn't yet exist
+       /* We're now looking at a regular file that doesn't yet exist
         * in the database, so add it. */
        next = talloc_asprintf (notmuch, "%s/%s", path, entry->d_name);
 
@@ -410,6 +429,30 @@ add_files_recursive (notmuch_database_t *notmuch,
        next = NULL;
     }
 
+    /* Now that we've walked the whole filesystem list, anything left
+     * over in the database lists has been deleted. */
+    while (notmuch_filenames_has_more (db_files))
+    {
+       char *absolute = talloc_asprintf (state->removed_files,
+                                         "%s/%s", path,
+                                         notmuch_filenames_get (db_files));
+
+       _filename_list_add (state->removed_files, absolute);
+
+       notmuch_filenames_advance (db_files);
+    }
+
+    while (notmuch_filenames_has_more (db_subdirs))
+    {
+       char *absolute = talloc_asprintf (state->removed_directories,
+                                         "%s/%s", path,
+                                         notmuch_filenames_get (db_subdirs));
+
+       _filename_list_add (state->removed_directories, absolute);
+
+       notmuch_filenames_advance (db_subdirs);
+    }
+
     if (! interrupted) {
        status = notmuch_directory_set_mtime (directory, fs_mtime);
        if (status && ret == NOTMUCH_STATUS_SUCCESS)
@@ -447,6 +490,7 @@ add_files (notmuch_database_t *notmuch,
     struct sigaction action;
     struct itimerval timerval;
     notmuch_bool_t timer_is_active = FALSE;
+    struct stat st;
 
     if (state->output_is_a_tty && ! debugger_is_active () && ! state->verbose) {
        /* Setup our handler for SIGALRM */
@@ -466,6 +510,17 @@ add_files (notmuch_database_t *notmuch,
        timer_is_active = TRUE;
     }
 
+    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;
+    }
+
     status = add_files_recursive (notmuch, path, state);
 
     if (timer_is_active) {
@@ -567,6 +622,8 @@ notmuch_new_command (void *ctx, int argc, char *argv[])
     char *dot_notmuch_path;
     struct sigaction action;
     _filename_node_t *f;
+    int renamed_files, removed_files;
+    notmuch_status_t status;
     int i;
 
     add_files_state.verbose = 0;
@@ -628,12 +685,39 @@ notmuch_new_command (void *ctx, int argc, char *argv[])
 
     ret = add_files (notmuch, db_path, &add_files_state);
 
+    removed_files = 0;
+    renamed_files = 0;
     for (f = add_files_state.removed_files->head; f; f = f->next) {
-       printf ("Detected removed file: %s\n", f->filename);
+       status = notmuch_database_remove_message (notmuch, f->filename);
+       if (status == NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID)
+           renamed_files++;
+       else
+           removed_files++;
     }
 
     for (f = add_files_state.removed_directories->head; f; f = f->next) {
-       printf ("Detected removed directory: %s\n", f->filename);
+       notmuch_directory_t *directory;
+       notmuch_filenames_t *files;
+
+       directory = notmuch_database_get_directory (notmuch, f->filename);
+
+       for (files = notmuch_directory_get_child_files (directory);
+            notmuch_filenames_has_more (files);
+            notmuch_filenames_advance (files))
+       {
+           char *absolute;
+
+           absolute = talloc_asprintf (ctx, "%s/%s", f->filename,
+                                       notmuch_filenames_get (files));
+           status = notmuch_database_remove_message (notmuch, absolute);
+           if (status == NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID)
+               renamed_files++;
+           else
+               removed_files++;
+           talloc_free (absolute);
+       }
+
+       notmuch_directory_destroy (directory);
     }
 
     talloc_free (add_files_state.removed_files);
@@ -642,6 +726,7 @@ notmuch_new_command (void *ctx, int argc, char *argv[])
     gettimeofday (&tv_now, NULL);
     elapsed = notmuch_time_elapsed (add_files_state.tv_start,
                                    tv_now);
+
     if (add_files_state.processed_files) {
        printf ("Processed %d %s in ", add_files_state.processed_files,
                add_files_state.processed_files == 1 ?
@@ -654,15 +739,30 @@ notmuch_new_command (void *ctx, int argc, char *argv[])
            printf (".                    \n");
        }
     }
+
     if (add_files_state.added_messages) {
-       printf ("Added %d new %s to the database.\n",
+       printf ("Added %d new %s to the database.",
                add_files_state.added_messages,
                add_files_state.added_messages == 1 ?
                "message" : "messages");
     } else {
-       printf ("No new mail.\n");
+       printf ("No new mail.");
+    }
+
+    if (removed_files) {
+       printf (" Removed %d %s.",
+               removed_files,
+               removed_files == 1 ? "message" : "messages");
     }
 
+    if (renamed_files) {
+       printf (" Detected %d file %s.",
+               renamed_files,
+               renamed_files == 1 ? "rename" : "renames");
+    }
+
+    printf ("\n");
+
     if (ret) {
        printf ("\nNote: At least one error was encountered: %s\n",
                notmuch_status_to_string (ret));