[RFC PATCH 4/5] cli: use homebrew scandir in notmuch new add_files
authorJani Nikula <jani@nikula.org>
Fri, 15 Apr 2016 19:29:18 +0000 (22:29 +0300)
committerW. Trevor King <wking@tremily.us>
Sat, 20 Aug 2016 23:21:39 +0000 (16:21 -0700)
55/94e10a4c78b63c8bf98df4d9c8fb58b2a995ce [new file with mode: 0644]

diff --git a/55/94e10a4c78b63c8bf98df4d9c8fb58b2a995ce b/55/94e10a4c78b63c8bf98df4d9c8fb58b2a995ce
new file mode 100644 (file)
index 0000000..c05af71
--- /dev/null
@@ -0,0 +1,498 @@
+Return-Path: <jani@nikula.org>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+ by arlo.cworth.org (Postfix) with ESMTP id 195076DE0360\r
+ for <notmuch@notmuchmail.org>; Fri, 15 Apr 2016 12:31:02 -0700 (PDT)\r
+X-Virus-Scanned: Debian amavisd-new at cworth.org\r
+X-Amavis-Alert: BAD HEADER SECTION, Duplicate header field: "References"\r
+X-Spam-Flag: NO\r
+X-Spam-Score: -0.557\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=-0.557 tagged_above=-999 required=5 tests=[AWL=0.163,\r
+  DKIM_SIGNED=0.1, DKIM_VALID=-0.1, RCVD_IN_DNSWL_LOW=-0.7,\r
+ RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01] autolearn=disabled\r
+Received: from arlo.cworth.org ([127.0.0.1])\r
+ by localhost (arlo.cworth.org [127.0.0.1]) (amavisd-new, port 10024)\r
+ with ESMTP id KjVxt2cZ6-O5 for <notmuch@notmuchmail.org>;\r
+ Fri, 15 Apr 2016 12:30:54 -0700 (PDT)\r
+Received: from mail-wm0-f67.google.com (mail-wm0-f67.google.com\r
+ [74.125.82.67]) by arlo.cworth.org (Postfix) with ESMTPS id 8B0FA6DE02C6 for\r
+ <notmuch@notmuchmail.org>; Fri, 15 Apr 2016 12:30:42 -0700 (PDT)\r
+Received: by mail-wm0-f67.google.com with SMTP id n3so8629843wmn.1\r
+ for <notmuch@notmuchmail.org>; Fri, 15 Apr 2016 12:30:42 -0700 (PDT)\r
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;\r
+ d=nikula-org.20150623.gappssmtp.com; s=20150623;\r
+ h=from:to:cc:subject:date:message-id:in-reply-to:references\r
+ :in-reply-to:references:mime-version:content-transfer-encoding;\r
+ bh=Xp5rFuNK1ThMoMMAw6AJlxsBrC7L1Et3ctHWU3hNPDE=;\r
+ b=QFJ5fn9fxYCv4VvdbDI6s009HxS2Bfsre0L8s7aX+MhpSC7u4VCD7tU+0ScofCIiJn\r
+ senN73w3W+7UDlNlwIaUquxjqosBWx1m41tG9zFjkZacQE456xRKYpzaM4C6UE+bF6xa\r
+ t7CQtl5xZoZR1GmZkNaOsuZPmzVqPQrsKqRRmDJz7Io4vKclsa5W6o/FTE6srlWaR7r3\r
+ 3zFrt1KFOUoIAFFLbUB6+mrfFs4Z2kX6UbQSRO1IrAorF9Pah9HjmjwAyh4N1idEM7mZ\r
+ CI9cc/iH2bJVZ6qn4TF0E+sOhmIfjHZUPDYQ9PdjDHv+cOWAfVmEDaRjImUO3Uh1j6OP\r
+ 27/Q==\r
+X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;\r
+ d=1e100.net; s=20130820;\r
+ h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\r
+ :references:in-reply-to:references:mime-version\r
+ :content-transfer-encoding;\r
+ bh=Xp5rFuNK1ThMoMMAw6AJlxsBrC7L1Et3ctHWU3hNPDE=;\r
+ b=ai2j4a1fyp5znB5uQhUhlkEtR7TipHCFKMIWY0T8o1a3b+a3Iek87M7v15KPZnyNP0\r
+ 0mFu16vAbqMdlurwU9n+Ta8x736NBg0kpMToB8q+3YHepYhM7fhol5/5WSWv2idEaUPm\r
+ MOA4vKvtAtG+m1Br3RHRKvtXNAVMrbXuLmmfcqCAF0LiRi8wYN0YuzO4q1M0VtQLKfxd\r
+ 6ObMsjBKYnoiLrK1TA45l47Ao8g/n4tjGKMl6Zafl610vkbeNDMPhEChrxI8eqbgdEQu\r
+ FKHazxlQNJCmZunmDlndizEpEbqhRBSjYtl5u4ANtfMcrBwFOymuSF41gbWKliV3VBE7\r
+ D1Dg==\r
+X-Gm-Message-State:\r
+ AOPr4FVnZF7kdP15dOCMpRIQ2wuAk2jCXpl7CJhfX3miCDSvcPzah2IJArqzZsRtSfNOjQ==\r
+X-Received: by 10.195.13.135 with SMTP id ey7mr23435188wjd.161.1460748641236; \r
+ Fri, 15 Apr 2016 12:30:41 -0700 (PDT)\r
+Received: from localhost (mobile-access-bcee7f-102.dhcp.inet.fi.\r
+ [188.238.127.102])\r
+ by smtp.gmail.com with ESMTPSA id 202sm20676452wmw.5.2016.04.15.12.30.40\r
+ (version=TLSv1/SSLv3 cipher=OTHER);\r
+ Fri, 15 Apr 2016 12:30:40 -0700 (PDT)\r
+From: Jani Nikula <jani@nikula.org>\r
+To: notmuch@notmuchmail.org\r
+Subject: [RFC PATCH 4/5] cli: use homebrew scandir in notmuch new add_files\r
+Date: Fri, 15 Apr 2016 22:29:18 +0300\r
+Message-Id:\r
+ <6e67ca7c973419213f2b9d47711b24cf7cb35d7a.1460748142.git.jani@nikula.org>\r
+X-Mailer: git-send-email 2.1.4\r
+In-Reply-To: <cover.1460748142.git.jani@nikula.org>\r
+References: <cover.1460748142.git.jani@nikula.org>\r
+In-Reply-To: <cover.1460748142.git.jani@nikula.org>\r
+References: <cover.1460748142.git.jani@nikula.org>\r
+MIME-Version: 1.0\r
+Content-Type: text/plain; charset=UTF-8\r
+Content-Transfer-Encoding: 8bit\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.20\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+ <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <https://notmuchmail.org/mailman/options/notmuch>,\r
+ <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch/>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <https://notmuchmail.org/mailman/listinfo/notmuch>,\r
+ <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Fri, 15 Apr 2016 19:31:02 -0000\r
+\r
+Split the scanning to files and subdirectories. Filter out ignored\r
+files etc. in the scandir filter callback.\r
+\r
+The end result is perhaps slightly easier to follow than before and it\r
+should be easier to add clever ignore mechanisms like globbing in the\r
+scandir filter callback, but it's not at all certain if this is worth\r
+the trouble.\r
+\r
+Some tests will fail due to changes in where files are ignored and\r
+what debug logging is done; I didn't bother updating the logging or\r
+the tests much at this point.\r
+---\r
+ notmuch-new.c | 275 +++++++++++++++++++++++++++++++---------------------------\r
+ 1 file changed, 148 insertions(+), 127 deletions(-)\r
+\r
+diff --git a/notmuch-new.c b/notmuch-new.c\r
+index 930cbbc9b86f..262895d466ae 100644\r
+--- a/notmuch-new.c\r
++++ b/notmuch-new.c\r
+@@ -20,6 +20,7 @@\r
\r
+ #include "notmuch-client.h"\r
+ #include "tag-util.h"\r
++#include "scandir.h"\r
\r
+ #include <unistd.h>\r
\r
+@@ -151,9 +152,12 @@ generic_print_progress (const char *action, const char *object,\r
+ }\r
\r
+ static int\r
+-dirent_sort_strcmp_name (const struct dirent **a, const struct dirent **b)\r
++dirent_sort_strcmp_name (const void *_a, const void *_b)\r
+ {\r
+-    return strcmp ((*a)->d_name, (*b)->d_name);\r
++    char * const *a = _a;\r
++    char * const *b = _b;\r
++\r
++    return strcmp (*a, *b);\r
+ }\r
\r
+ /* Return the type of a directory entry relative to path as a stat(2)\r
+@@ -206,18 +210,14 @@ dirent_type (const char *path, const struct dirent *entry)\r
+  * Return 1 if the directory looks like a Maildir and 0 otherwise.\r
+  */\r
+ static int\r
+-_entries_resemble_maildir (const char *path, struct dirent **entries, int count)\r
++_entries_resemble_maildir (char **subdirs, int count)\r
+ {\r
+     int i, found = 0;\r
\r
+     for (i = 0; i < count; i++) {\r
+-      if (dirent_type (path, entries[i]) != S_IFDIR)\r
+-          continue;\r
+-\r
+-      if (strcmp(entries[i]->d_name, "new") == 0 ||\r
+-          strcmp(entries[i]->d_name, "cur") == 0 ||\r
+-          strcmp(entries[i]->d_name, "tmp") == 0)\r
+-      {\r
++      if (strcmp(subdirs[i], "new") == 0 ||\r
++          strcmp(subdirs[i], "cur") == 0 ||\r
++          strcmp(subdirs[i], "tmp") == 0) {\r
+           found++;\r
+           if (found == 3)\r
+               return 1;\r
+@@ -241,6 +241,49 @@ _entry_in_ignore_list (const char *entry, add_files_state_t *state)\r
+     return FALSE;\r
+ }\r
\r
++struct filter {\r
++    const char *path;\r
++    add_files_state_t *state;\r
++    int entry_type;\r
++};\r
++\r
++static int filter_fn (const struct dirent *entry, void *context)\r
++{\r
++    struct filter *filter = context;\r
++    int entry_type;\r
++\r
++    /* Ignore any files/directories the user has configured to\r
++     * ignore.  We do this before dirent_type both for performance\r
++     * and because we don't care if dirent_type fails on entries\r
++     * that are explicitly ignored.\r
++     */\r
++    if (_entry_in_ignore_list (entry->d_name, filter->state)) {\r
++      if (filter->state->debug)\r
++          printf ("(D) add_files, pass 1: explicitly ignoring %s/%s\n",\r
++                  filter->path, entry->d_name);\r
++      return 0;\r
++    }\r
++\r
++    /* We only want to descend into directories (and symlinks to\r
++     * directories). */\r
++    entry_type = dirent_type (filter->path, entry);\r
++    if (entry_type == -1) {\r
++      /* Be pessimistic, e.g. so we don't lose lots of mail just\r
++       * because a user broke a symlink. */\r
++      fprintf (stderr, "Error reading file %s/%s: %s\n",\r
++               filter->path, entry->d_name, strerror (errno));\r
++      return -1;\r
++    }\r
++\r
++    if (entry_type != filter->entry_type)\r
++      return 0;\r
++\r
++    if (strcmp (entry->d_name, ".") == 0 || strcmp (entry->d_name, "..") == 0)\r
++      return 0;\r
++\r
++    return 1;\r
++}\r
++\r
+ /* Add a single file to the database. */\r
+ static notmuch_status_t\r
+ add_file (notmuch_database_t *notmuch, const char *filename,\r
+@@ -345,18 +388,22 @@ add_files (notmuch_database_t *notmuch,\r
+          const char *path,\r
+          add_files_state_t *state)\r
+ {\r
+-    struct dirent *entry = NULL;\r
+     char *next = NULL;\r
+     time_t fs_mtime, db_mtime;\r
+     notmuch_status_t status, ret = NOTMUCH_STATUS_SUCCESS;\r
+-    struct dirent **fs_entries = NULL;\r
+-    int i, num_fs_entries = 0, entry_type;\r
++    char **fs_files = NULL, **fs_subdirs = NULL;\r
++    int num_fs_files = 0, num_fs_subdirs = 0;\r
++    int i;\r
+     notmuch_directory_t *directory;\r
+     notmuch_filenames_t *db_files = NULL;\r
+     notmuch_filenames_t *db_subdirs = NULL;\r
+     time_t stat_time;\r
+     struct stat st;\r
+     notmuch_bool_t is_maildir;\r
++    struct filter filter = {\r
++      .path = path,\r
++      .state = state,\r
++    };\r
\r
+     if (stat (path, &st)) {\r
+       fprintf (stderr, "Error reading directory %s: %s\n",\r
+@@ -410,11 +457,11 @@ add_files (notmuch_database_t *notmuch,\r
\r
+     /* If the database knows about this directory, then we sort based\r
+      * on strcmp to match the database sorting. */\r
+-    num_fs_entries = scandir (path, &fs_entries, 0,\r
+-                            directory ?\r
+-                            dirent_sort_strcmp_name : NULL);\r
+-\r
+-    if (num_fs_entries == -1) {\r
++    filter.entry_type = S_IFDIR;\r
++    num_fs_subdirs = scandirx (path, &fs_subdirs, filter_fn,\r
++                             directory ? dirent_sort_strcmp_name : NULL,\r
++                             &filter);\r
++    if (num_fs_subdirs == -1) {\r
+       fprintf (stderr, "Error opening directory %s: %s\n",\r
+                path, strerror (errno));\r
+       /* We consider this a fatal error because, if a user moved a\r
+@@ -426,50 +473,20 @@ add_files (notmuch_database_t *notmuch,\r
+     }\r
\r
+     /* Pass 1: Recurse into all sub-directories. */\r
+-    is_maildir = _entries_resemble_maildir (path, fs_entries, num_fs_entries);\r
+-\r
+-    for (i = 0; i < num_fs_entries; i++) {\r
+-      if (interrupted)\r
+-          break;\r
++    is_maildir = _entries_resemble_maildir (fs_subdirs, num_fs_subdirs);\r
\r
+-      entry = fs_entries[i];\r
++    for (i = 0; i < num_fs_subdirs && !interrupted; i++) {\r
++      const char *name = fs_subdirs[i];\r
\r
+-      /* Ignore any files/directories the user has configured to\r
+-       * ignore.  We do this before dirent_type both for performance\r
+-       * and because we don't care if dirent_type fails on entries\r
+-       * that are explicitly ignored.\r
++      /*\r
++       * Ignore the .notmuch directory and any "tmp" directory that\r
++       * appears within a maildir.\r
+        */\r
+-      if (_entry_in_ignore_list (entry->d_name, state)) {\r
+-          if (state->debug)\r
+-              printf ("(D) add_files, pass 1: explicitly ignoring %s/%s\n",\r
+-                      path, entry->d_name);\r
++      if ((is_maildir && strcmp (name, "tmp") == 0) ||\r
++          strcmp (name, ".notmuch") == 0)\r
+           continue;\r
+-      }\r
\r
+-      /* We only want to descend into directories (and symlinks to\r
+-       * directories). */\r
+-      entry_type = dirent_type (path, entry);\r
+-      if (entry_type == -1) {\r
+-          /* Be pessimistic, e.g. so we don't lose lots of mail just\r
+-           * because a user broke a symlink. */\r
+-          fprintf (stderr, "Error reading file %s/%s: %s\n",\r
+-                   path, entry->d_name, strerror (errno));\r
+-          return NOTMUCH_STATUS_FILE_ERROR;\r
+-      } else if (entry_type != S_IFDIR) {\r
+-          continue;\r
+-      }\r
+-\r
+-      /* Ignore special directories to avoid infinite recursion.\r
+-       * Also ignore the .notmuch directory and any "tmp" directory\r
+-       * that appears within a maildir.\r
+-       */\r
+-      if (strcmp (entry->d_name, ".") == 0 ||\r
+-          strcmp (entry->d_name, "..") == 0 ||\r
+-          (is_maildir && strcmp (entry->d_name, "tmp") == 0) ||\r
+-          strcmp (entry->d_name, ".notmuch") == 0)\r
+-          continue;\r
+-\r
+-      next = talloc_asprintf (notmuch, "%s/%s", path, entry->d_name);\r
++      next = talloc_asprintf (notmuch, "%s/%s", path, name);\r
+       status = add_files (notmuch, next, state);\r
+       if (status) {\r
+           ret = status;\r
+@@ -498,27 +515,71 @@ add_files (notmuch_database_t *notmuch,\r
+       db_subdirs = notmuch_directory_get_child_directories (directory);\r
+     }\r
\r
+-    /* Pass 2: Scan for new files, removed files, and removed directories. */\r
+-    for (i = 0; i < num_fs_entries; i++)\r
+-    {\r
+-      if (interrupted)\r
+-          break;\r
++    /* Pass 1½: Scan for removed directories. */\r
++    for (i = 0; i < num_fs_subdirs && !interrupted; i++) {\r
++      const char *name = fs_subdirs[i];\r
\r
+-        entry = fs_entries[i];\r
++      /* Check if we've walked past any names in db_subdirs. If so,\r
++       * these have been deleted. */\r
++      while (notmuch_filenames_valid (db_subdirs) &&\r
++             strcmp (notmuch_filenames_get (db_subdirs), name) < 0) {\r
++          char *absolute = talloc_asprintf (state->removed_directories,\r
++                                            "%s/%s", path,\r
++                                            notmuch_filenames_get (db_subdirs));\r
\r
+-      /* Ignore files & directories user has configured to be ignored */\r
+-      if (_entry_in_ignore_list (entry->d_name, state)) {\r
+           if (state->debug)\r
+-              printf ("(D) add_files, pass 2: explicitly ignoring %s/%s\n",\r
+-                      path, entry->d_name);\r
+-          continue;\r
++              printf ("(D) add_files, pass 2: queuing passed directory %s for deletion from database\n",\r
++                      absolute);\r
++\r
++          _filename_list_add (state->removed_directories, absolute);\r
++\r
++          notmuch_filenames_move_to_next (db_subdirs);\r
+       }\r
\r
+-      /* Check if we've walked past any names in db_files or\r
+-       * db_subdirs. If so, these have been deleted. */\r
++      if (notmuch_filenames_valid (db_subdirs) &&\r
++          strcmp (notmuch_filenames_get (db_subdirs), name) == 0)\r
++          notmuch_filenames_move_to_next (db_subdirs);\r
++    }\r
++\r
++    /* Now that we've walked the whole filesystem subdir list, subdirs\r
++     * left over in the database list have been deleted. */\r
++    while (notmuch_filenames_valid (db_subdirs)) {\r
++      char *absolute = talloc_asprintf (state->removed_directories,\r
++                                        "%s/%s", path,\r
++                                        notmuch_filenames_get (db_subdirs));\r
++\r
++      if (state->debug)\r
++          printf ("(D) add_files, pass 3: queuing leftover directory %s for deletion from database\n",\r
++                  absolute);\r
++\r
++      _filename_list_add (state->removed_directories, absolute);\r
++\r
++      notmuch_filenames_move_to_next (db_subdirs);\r
++    }\r
++\r
++    /* Pass 2: Scan for new and removed files. */\r
++    filter.entry_type = S_IFREG;\r
++    num_fs_files = scandirx (path, &fs_files, filter_fn,\r
++                           directory ? dirent_sort_strcmp_name : NULL,\r
++                           &filter);\r
++    if (num_fs_files == -1) {\r
++      fprintf (stderr, "Error opening directory %s: %s\n",\r
++               path, strerror (errno));\r
++      /* We consider this a fatal error because, if a user moved a\r
++       * message from another directory that we were able to scan\r
++       * into this directory, skipping this directory will cause\r
++       * that message to be lost. */\r
++      ret = NOTMUCH_STATUS_FILE_ERROR;\r
++      goto DONE;\r
++    }\r
++\r
++    for (i = 0; i < num_fs_files && !interrupted; i++) {\r
++      const char *name = fs_files[i];\r
++\r
++      /* Check if we've walked past any names in db_files. If so,\r
++       * these have been deleted. */\r
+       while (notmuch_filenames_valid (db_files) &&\r
+-             strcmp (notmuch_filenames_get (db_files), entry->d_name) < 0)\r
+-      {\r
++             strcmp (notmuch_filenames_get (db_files), name) < 0) {\r
+           char *absolute = talloc_asprintf (state->removed_files,\r
+                                             "%s/%s", path,\r
+                                             notmuch_filenames_get (db_files));\r
+@@ -532,46 +593,16 @@ add_files (notmuch_database_t *notmuch,\r
+           notmuch_filenames_move_to_next (db_files);\r
+       }\r
\r
+-      while (notmuch_filenames_valid (db_subdirs) &&\r
+-             strcmp (notmuch_filenames_get (db_subdirs), entry->d_name) <= 0)\r
+-      {\r
+-          const char *filename = notmuch_filenames_get (db_subdirs);\r
+-\r
+-          if (strcmp (filename, entry->d_name) < 0)\r
+-          {\r
+-              char *absolute = talloc_asprintf (state->removed_directories,\r
+-                                                "%s/%s", path, filename);\r
+-              if (state->debug)\r
+-                  printf ("(D) add_files, pass 2: queuing passed directory %s for deletion from database\n",\r
+-                      absolute);\r
+-\r
+-              _filename_list_add (state->removed_directories, absolute);\r
+-          }\r
+-\r
+-          notmuch_filenames_move_to_next (db_subdirs);\r
+-      }\r
+-\r
+-      /* Only add regular files (and symlinks to regular files). */\r
+-      entry_type = dirent_type (path, entry);\r
+-      if (entry_type == -1) {\r
+-          fprintf (stderr, "Error reading file %s/%s: %s\n",\r
+-                   path, entry->d_name, strerror (errno));\r
+-          return NOTMUCH_STATUS_FILE_ERROR;\r
+-      } else if (entry_type != S_IFREG) {\r
+-          continue;\r
+-      }\r
+-\r
+       /* Don't add a file that we've added before. */\r
+       if (notmuch_filenames_valid (db_files) &&\r
+-          strcmp (notmuch_filenames_get (db_files), entry->d_name) == 0)\r
+-      {\r
++          strcmp (notmuch_filenames_get (db_files), name) == 0) {\r
+           notmuch_filenames_move_to_next (db_files);\r
+           continue;\r
+       }\r
\r
+       /* We're now looking at a regular file that doesn't yet exist\r
+        * in the database, so add it. */\r
+-      next = talloc_asprintf (notmuch, "%s/%s", path, entry->d_name);\r
++      next = talloc_asprintf (notmuch, "%s/%s", path, name);\r
\r
+       state->processed_files++;\r
\r
+@@ -605,10 +636,9 @@ add_files (notmuch_database_t *notmuch,\r
+     if (interrupted)\r
+       goto DONE;\r
\r
+-    /* Now that we've walked the whole filesystem list, anything left\r
+-     * over in the database lists has been deleted. */\r
+-    while (notmuch_filenames_valid (db_files))\r
+-    {\r
++    /* Now that we've walked the whole filesystem file list, files\r
++     * left over in the database list have been deleted. */\r
++    while (notmuch_filenames_valid (db_files)) {\r
+       char *absolute = talloc_asprintf (state->removed_files,\r
+                                         "%s/%s", path,\r
+                                         notmuch_filenames_get (db_files));\r
+@@ -621,21 +651,6 @@ add_files (notmuch_database_t *notmuch,\r
+       notmuch_filenames_move_to_next (db_files);\r
+     }\r
\r
+-    while (notmuch_filenames_valid (db_subdirs))\r
+-    {\r
+-      char *absolute = talloc_asprintf (state->removed_directories,\r
+-                                        "%s/%s", path,\r
+-                                        notmuch_filenames_get (db_subdirs));\r
+-\r
+-      if (state->debug)\r
+-          printf ("(D) add_files, pass 3: queuing leftover directory %s for deletion from database\n",\r
+-                  absolute);\r
+-\r
+-      _filename_list_add (state->removed_directories, absolute);\r
+-\r
+-      notmuch_filenames_move_to_next (db_subdirs);\r
+-    }\r
+-\r
+     /* If the directory's mtime is the same as the wall-clock time\r
+      * when we stat'ed the directory, we skip updating the mtime in\r
+      * the database because a message could be delivered later in this\r
+@@ -647,11 +662,17 @@ add_files (notmuch_database_t *notmuch,\r
+   DONE:\r
+     if (next)\r
+       talloc_free (next);\r
+-    if (fs_entries) {\r
+-      for (i = 0; i < num_fs_entries; i++)\r
+-          free (fs_entries[i]);\r
++    if (fs_subdirs) {\r
++      for (i = 0; i < num_fs_subdirs; i++)\r
++          free (fs_subdirs[i]);\r
++\r
++      free (fs_subdirs);\r
++    }\r
++    if (fs_files) {\r
++      for (i = 0; i < num_fs_files; i++)\r
++          free (fs_files[i]);\r
\r
+-      free (fs_entries);\r
++      free (fs_files);\r
+     }\r
+     if (db_subdirs)\r
+       notmuch_filenames_destroy (db_subdirs);\r
+-- \r
+2.1.4\r
+\r