1 /* notmuch - Not much of an email program, (just index and search)
3 * Copyright © 2009 Carl Worth
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see http://www.gnu.org/licenses/ .
18 * Author: Carl Worth <cworth@cworth.org>
21 #include "notmuch-client.h"
23 /* Recursively count all regular files in path and all sub-direcotries
24 * of path. The result is added to *count (which should be
25 * initialized to zero by the top-level caller before calling
28 count_files (const char *path, int *count)
31 struct dirent *e, *entry = NULL;
40 fprintf (stderr, "Warning: failed to open directory %s: %s\n",
41 path, strerror (errno));
45 entry_length = offsetof (struct dirent, d_name) +
46 pathconf (path, _PC_NAME_MAX) + 1;
47 entry = malloc (entry_length);
50 err = readdir_r (dir, entry, &e);
52 fprintf (stderr, "Error reading directory: %s\n",
61 /* Ignore special directories to avoid infinite recursion.
62 * Also ignore the .notmuch directory.
64 /* XXX: Eventually we'll want more sophistication to let the
65 * user specify files to be ignored. */
66 if (strcmp (entry->d_name, ".") == 0 ||
67 strcmp (entry->d_name, "..") == 0 ||
68 strcmp (entry->d_name, ".notmuch") == 0)
73 if (asprintf (&next, "%s/%s", path, entry->d_name) == -1) {
75 fprintf (stderr, "Error descending from %s to %s: Out of memory\n",
82 if (S_ISREG (st.st_mode)) {
84 if (*count % 1000 == 0) {
85 printf ("Found %d files so far.\r", *count);
88 } else if (S_ISDIR (st.st_mode)) {
89 count_files (next, count);
103 notmuch_setup_command (unused (void *ctx),
104 unused (int argc), unused (char *argv[]))
106 notmuch_database_t *notmuch = NULL;
107 char *default_path, *mail_directory = NULL;
110 add_files_state_t add_files_state;
112 struct timeval tv_now;
113 notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS;
115 printf ("Welcome to notmuch!\n\n");
117 printf ("The goal of notmuch is to help you manage and search your collection of\n"
118 "email, and to efficiently keep up with the flow of email as it comes in.\n\n");
120 printf ("Notmuch needs to know the top-level directory of your email archive,\n"
121 "(where you already have mail stored and where messages will be delivered\n"
122 "in the future). This directory can contain any number of sub-directories\n"
123 "and primarily just files with indvidual email messages (eg. maildir or mh\n"
124 "archives are perfect). If there are other, non-email files (such as\n"
125 "indexes maintained by other email programs) then notmuch will do its\n"
126 "best to detect those and ignore them.\n\n");
128 printf ("Mail storage that uses mbox format, (where one mbox file contains many\n"
129 "messages), will not work with notmuch. If that's how your mail is currently\n"
130 "stored, we recommend you first convert it to maildir format with a utility\n"
131 "such as mb2md. In that case, press Control-C now and run notmuch again\n"
132 "once the conversion is complete.\n\n");
135 default_path = notmuch_database_default_path ();
136 printf ("Top-level mail directory [%s]: ", default_path);
139 getline (&mail_directory, &line_size, stdin);
140 chomp_newline (mail_directory);
144 if (mail_directory == NULL || strlen (mail_directory) == 0) {
146 free (mail_directory);
147 mail_directory = default_path;
149 /* XXX: Instead of telling the user to use an environment
150 * variable here, we should really be writing out a configuration
151 * file and loading that on the next run. */
152 if (strcmp (mail_directory, default_path)) {
153 printf ("Note: Since you are not using the default path, you will want to set\n"
154 "the NOTMUCH_BASE environment variable to %s so that\n"
155 "future calls to notmuch commands will know where to find your mail.\n",
157 printf ("For example, if you are using bash for your shell, add:\n\n");
158 printf ("\texport NOTMUCH_BASE=%s\n\n", mail_directory);
159 printf ("to your ~/.bashrc file.\n\n");
164 /* Coerce the directory into an absolute directory name. */
165 if (*mail_directory != '/') {
166 char *cwd, *absolute_mail_directory;
168 cwd = getcwd (NULL, 0);
170 fprintf (stderr, "Out of memory.\n");
174 if (asprintf (&absolute_mail_directory, "%s/%s",
175 cwd, mail_directory) < 0)
177 fprintf (stderr, "Out of memory.\n");
182 free (mail_directory);
183 mail_directory = absolute_mail_directory;
186 notmuch = notmuch_database_create (mail_directory);
187 if (notmuch == NULL) {
188 fprintf (stderr, "Failed to create new notmuch database at %s\n",
190 ret = NOTMUCH_STATUS_FILE_ERROR;
194 printf ("OK. Let's take a look at the mail we can find in the directory\n");
195 printf ("%s ...\n", mail_directory);
198 count_files (mail_directory, &count);
200 printf ("Found %d total files. That's not much mail.\n\n", count);
202 printf ("Next, we'll inspect the messages and create a database of threads:\n");
204 add_files_state.ignore_read_only_directories = FALSE;
205 add_files_state.saw_read_only_directory = FALSE;
206 add_files_state.total_files = count;
207 add_files_state.processed_files = 0;
208 add_files_state.added_messages = 0;
209 add_files_state.callback = NULL;
210 gettimeofday (&add_files_state.tv_start, NULL);
212 ret = add_files (notmuch, mail_directory, &add_files_state);
214 gettimeofday (&tv_now, NULL);
215 elapsed = notmuch_time_elapsed (add_files_state.tv_start,
217 printf ("Processed %d %s in ", add_files_state.processed_files,
218 add_files_state.processed_files == 1 ?
219 "file" : "total files");
220 notmuch_time_print_formatted_seconds (elapsed);
222 printf (" (%d files/sec.). \n",
223 (int) (add_files_state.processed_files / elapsed));
227 if (add_files_state.added_messages) {
228 printf ("Added %d %s to the database.\n\n",
229 add_files_state.added_messages,
230 add_files_state.added_messages == 1 ?
231 "message" : "unique messages");
234 printf ("When new mail is delivered to %s in the future,\n"
235 "run \"notmuch new\" to add it to the database.\n\n",
239 printf ("Note: At least one error was encountered: %s\n",
240 notmuch_status_to_string (ret));
245 free (mail_directory);
247 notmuch_database_close (notmuch);