- int *add_tags, *remove_tags;
- int add_tags_count = 0;
- int remove_tags_count = 0;
- char *query_string;
- notmuch_database_t *notmuch = NULL;
- notmuch_query_t *query;
- notmuch_message_results_t *results;
- notmuch_message_t *message;
- int ret = 0;
- int i;
-
- local = talloc_new (NULL);
- if (local == NULL) {
- ret = 1;
- goto DONE;
- }
-
- add_tags = talloc_size (local, argc * sizeof (int));
- if (add_tags == NULL) {
- ret = 1;
- goto DONE;
- }
-
- remove_tags = talloc_size (local, argc * sizeof (int));
- if (remove_tags == NULL) {
- ret = 1;
- goto DONE;
- }
-
- for (i = 0; i < argc; i++) {
- if (strcmp (argv[i], "--") == 0) {
- i++;
- break;
- }
- if (argv[i][0] == '+') {
- add_tags[add_tags_count++] = i;
- } else if (argv[i][0] == '-') {
- remove_tags[remove_tags_count++] = i;
- } else {
- break;
- }
- }
-
- if (add_tags_count == 0 && remove_tags_count == 0) {
- fprintf (stderr, "Error: 'notmuch tag' requires at least one tag to add or remove.\n");
- ret = 1;
- goto DONE;
- }
-
- if (i == argc) {
- fprintf (stderr, "Error: 'notmuch tag' requires at least one search term.\n");
- ret = 1;
- goto DONE;
- }
-
- notmuch = notmuch_database_open (NULL);
- if (notmuch == NULL) {
- ret = 1;
- goto DONE;
- }
-
- query_string = query_string_from_args (local, argc - i, &argv[i]);
-
- query = notmuch_query_create (notmuch, query_string);
- if (query == NULL) {
- fprintf (stderr, "Out of memory.\n");
- ret = 1;
- goto DONE;
- }
-
- for (results = notmuch_query_search_messages (query);
- notmuch_message_results_has_more (results);
- notmuch_message_results_advance (results))
- {
- message = notmuch_message_results_get (results);
-
- notmuch_message_freeze (message);
-
- for (i = 0; i < remove_tags_count; i++)
- notmuch_message_remove_tag (message,
- argv[remove_tags[i]] + 1);
-
- for (i = 0; i < add_tags_count; i++)
- notmuch_message_add_tag (message, argv[add_tags[i]] + 1);
-
- notmuch_message_thaw (message);
-
- notmuch_message_destroy (message);
- }
-
- notmuch_query_destroy (query);
-
- DONE:
- if (notmuch)
- notmuch_database_close (notmuch);
-
- talloc_free (local);
-
- return ret;
-}
-
-static int
-dump_command (int argc, char *argv[])
-{
- FILE *output = NULL;
- notmuch_database_t *notmuch = NULL;
- notmuch_query_t *query;
- notmuch_message_results_t *results;
- notmuch_message_t *message;
- notmuch_tags_t *tags;
- int ret = 0;
-
- if (argc) {
- output = fopen (argv[0], "w");
- if (output == NULL) {
- fprintf (stderr, "Error opening %s for writing: %s\n",
- argv[0], strerror (errno));
- ret = 1;
- goto DONE;
- }
- } else {
- output = stdout;
- }
-
- notmuch = notmuch_database_open (NULL);
- if (notmuch == NULL) {
- ret = 1;
- goto DONE;
- }
-
- query = notmuch_query_create (notmuch, "");
- if (query == NULL) {
- fprintf (stderr, "Out of memory\n");
- ret = 1;
- goto DONE;
- }
-
- notmuch_query_set_sort (query, NOTMUCH_SORT_MESSAGE_ID);
-
- for (results = notmuch_query_search_messages (query);
- notmuch_message_results_has_more (results);
- notmuch_message_results_advance (results))
- {
- int first = 1;
- message = notmuch_message_results_get (results);
-
- fprintf (output,
- "%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)
- fprintf (output, " ");
-
- fprintf (output, "%s", notmuch_tags_get (tags));
-
- first = 0;
- }
-
- fprintf (output, ")\n");
-
- notmuch_message_destroy (message);
- }
-
- notmuch_query_destroy (query);
-
- DONE:
- if (notmuch)
- notmuch_database_close (notmuch);
- if (output && output != stdout)
- fclose (output);
-
- return ret;
-}
-
-static int
-restore_command (int argc, char *argv[])
-{
- FILE *input = NULL;
- notmuch_database_t *notmuch = NULL;
- char *line = NULL;
- size_t line_size;
- ssize_t line_len;
- regex_t regex;
- int rerr;
- int ret = 0;
-
- if (argc) {
- input = fopen (argv[0], "r");
- if (input == NULL) {
- fprintf (stderr, "Error opening %s for reading: %s\n",
- argv[0], strerror (errno));
- ret = 1;
- goto DONE;
- }
- } else {
- printf ("No filename given. Reading dump from stdin.\n");
- input = stdin;
- }
-
- notmuch = notmuch_database_open (NULL);
- if (notmuch == NULL) {
- ret = 1;
- goto DONE;
- }
-
- /* Dump output is one line per message. We match a sequence of
- * non-space characters for the message-id, then one or more
- * spaces, then a list of space-separated tags as a sequence of
- * characters within literal '(' and ')'. */
- xregcomp (®ex,
- "^([^ ]+) \\(([^)]*)\\)$",
- REG_EXTENDED);
-
- while ((line_len = getline (&line, &line_size, input)) != -1) {
- regmatch_t match[3];
- char *message_id, *tags, *tag, *next;
- notmuch_message_t *message;
- notmuch_status_t status;
-
- chomp_newline (line);
-
- rerr = xregexec (®ex, line, 3, match, 0);
- if (rerr == REG_NOMATCH)
- {
- fprintf (stderr, "Warning: Ignoring invalid input line: %s\n",
- line);
- continue;
- }
-
- message_id = xstrndup (line + match[1].rm_so,
- match[1].rm_eo - match[1].rm_so);
- tags = xstrndup (line + match[2].rm_so,
- match[2].rm_eo - match[2].rm_so);
-
- if (strlen (tags)) {
-
- message = notmuch_database_find_message (notmuch, message_id);
- if (message == NULL) {
- fprintf (stderr, "Warning: Cannot apply tags to missing message: %s\n",
- message_id);
- goto NEXT_LINE;
- }
-
- notmuch_message_freeze (message);
-
- notmuch_message_remove_all_tags (message);
-
- next = tags;
- while (next) {
- tag = strsep (&next, " ");
- if (*tag == '\0')
- continue;
- status = notmuch_message_add_tag (message, tag);
- if (status) {
- fprintf (stderr,
- "Error applying tag %s to message %s:\n",
- tag, message_id);
- fprintf (stderr, "%s\n",
- notmuch_status_to_string (status));
- }
- }
-
- notmuch_message_thaw (message);
- notmuch_message_destroy (message);
- }
- NEXT_LINE:
- free (message_id);
- free (tags);
- }
-
- regfree (®ex);
-
- DONE:
- if (line)
- free (line);
- if (notmuch)
- notmuch_database_close (notmuch);
- if (input && input != stdin)
- fclose (input);
-
- return ret;
-}
-
-command_t commands[] = {
- { "setup", setup_command,
- "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.\n\n"
- "\t\tScans all sub-directories of the database, adding new messages\n"
- "\t\tthat are found. Each new message will be tagges as both\n"
- "\t\t\"inbox\" and \"unread\".\n"
- "\n"
- "\t\tNote: \"notmuch new\" will skip any read-only directories,\n"
- "\t\tso you can use that to mark tdirectories that will not\n"
- "\t\treceive any new mail (and make \"notmuch new\" faster)." },
- { "search", search_command,
- "<search-term> [...]\n\n"
- "\t\tSearch for threads matching the given search terms.\n"
- "\t\tOnce we actually implement search we'll document the\n"
- "\t\tsyntax here." },
- { "show", show_command,
- "<thread-id>\n\n"
- "\t\tShow the thread with the given thread ID (see 'search')." },
- { "tag", tag_command,
- "+<tag>|-<tag> [...] [--] <search-term> [...]\n\n"
- "\t\tAdd or remove the specified tags to all messages matching\n"
- "\t\tthe specified search terms. The search terms are handled\n"
- "\t\texactly as in 'search' so one can use that command first\n"
- "\t\tto see what will be modified.\n\n"
- "\t\tTags prefixed by '+' are added while those prefixed by '-' are\n"
- "\t\tremoved. For each message, tag removal is before tag addition.\n\n"
- "\t\tThe beginning of <search-terms> is recognized by the first\n"
- "\t\targument that begins with neither '+' nor '-'. Support for\n"
- "\t\tan initial search term beginning with '+' or '-' is provided\n"
- "\t\tby allowing the user to specify a \"--\" argument to separate\n"
- "\t\tthe tags from the search terms." },
- { "dump", dump_command,
- "[<filename>]\n\n"
- "\t\tCreate a plain-text dump of the tags for each message\n"
- "\t\twriting to the given filename, if any, or to stdout.\n"
- "\t\tThese tags are the only data in the notmuch database\n"
- "\t\tthat can't be recreated from the messages themselves.\n"
- "\t\tThe output of notmuch dump is therefore the only\n"
- "\t\tcritical thing to backup (and much more friendly to\n"
- "\t\tincremental backup than the native database files." },
- { "restore", restore_command,
- "<filename>\n\n"
- "\t\tRestore the tags from the given dump file (see 'dump')." }
-};
-
-static void
-usage (void)
-{