Example #1
0
static char *
_optimize_tag_query (void *ctx, const char *orig_query_string,
		     const tag_op_list_t *list)
{
    /* This is subtler than it looks.  Xapian ignores the '-' operator
     * at the beginning both queries and parenthesized groups and,
     * furthermore, the presence of a '-' operator at the beginning of
     * a group can inhibit parsing of the previous operator.  Hence,
     * the user-provided query MUST appear first, but it is safe to
     * parenthesize and the exclusion part of the query must not use
     * the '-' operator (though the NOT operator is fine). */

    char *escaped = NULL;
    size_t escaped_len = 0;
    char *query_string;
    const char *join = "";
    size_t i;

    /* Don't optimize if there are no tag changes. */
    if (tag_op_list_size (list) == 0)
	return talloc_strdup (ctx, orig_query_string);

    /* Build the new query string */
    if (strcmp (orig_query_string, "*") == 0)
	query_string = talloc_strdup (ctx, "(");
    else
	query_string = talloc_asprintf (ctx, "( %s ) and (", orig_query_string);

    for (i = 0; i < tag_op_list_size (list) && query_string; i++) {
	/* XXX in case of OOM, query_string will be deallocated when
	 * ctx is, which might be at shutdown */
	if (make_boolean_term (ctx,
			       "tag", tag_op_list_tag (list, i),
			       &escaped, &escaped_len))
	    return NULL;

	query_string = talloc_asprintf_append_buffer (
	    query_string, "%s%s%s", join,
	    tag_op_list_isremove (list, i) ? "" : "not ",
	    escaped);
	join = " or ";
    }

    if (query_string)
	query_string = talloc_strdup_append_buffer (query_string, ")");

    talloc_free (escaped);
    return query_string;
}
Example #2
0
/* Non-zero return indicates an error in retrieving the message,
 * or in applying the tags.  Missing messages are reported, but not
 * considered errors.
 */
static int
tag_message (unused (void *ctx),
	     notmuch_database_t *notmuch,
	     const char *message_id,
	     tag_op_list_t *tag_ops,
	     tag_op_flag_t flags)
{
    notmuch_status_t status;
    notmuch_message_t *message = NULL;
    int ret = 0;

    status = notmuch_database_find_message (notmuch, message_id, &message);
    if (status) {
	fprintf (stderr, "Error applying tags to message %s: %s\n",
		 message_id, notmuch_status_to_string (status));
	return 1;
    }
    if (message == NULL) {
	fprintf (stderr, "Warning: cannot apply tags to missing message: %s\n",
		 message_id);
	/* We consider this a non-fatal error. */
	return 0;
    }

    /* In order to detect missing messages, this check/optimization is
     * intentionally done *after* first finding the message. */
    if ((flags & TAG_FLAG_REMOVE_ALL) || tag_op_list_size (tag_ops))
	ret = tag_op_list_apply (message, tag_ops, flags);

    notmuch_message_destroy (message);

    return ret;
}
Example #3
0
int
notmuch_tag_command (notmuch_config_t *config, int argc, char *argv[])
{
    tag_op_list_t *tag_ops = NULL;
    char *query_string = NULL;
    notmuch_database_t *notmuch;
    struct sigaction action;
    tag_op_flag_t tag_flags = TAG_FLAG_NONE;
    notmuch_bool_t batch = FALSE;
    notmuch_bool_t remove_all = FALSE;
    FILE *input = stdin;
    char *input_file_name = NULL;
    int opt_index;
    int ret = 0;

    /* Setup our handler for SIGINT */
    memset (&action, 0, sizeof (struct sigaction));
    action.sa_handler = handle_sigint;
    sigemptyset (&action.sa_mask);
    action.sa_flags = SA_RESTART;
    sigaction (SIGINT, &action, NULL);

    notmuch_opt_desc_t options[] = {
	{ NOTMUCH_OPT_BOOLEAN, &batch, "batch", 0, 0 },
	{ NOTMUCH_OPT_STRING, &input_file_name, "input", 'i', 0 },
	{ NOTMUCH_OPT_BOOLEAN, &remove_all, "remove-all", 0, 0 },
	{ 0, 0, 0, 0, 0 }
    };

    opt_index = parse_arguments (argc, argv, options, 1);
    if (opt_index < 0)
	return 1;

    if (input_file_name) {
	batch = TRUE;
	input = fopen (input_file_name, "r");
	if (input == NULL) {
	    fprintf (stderr, "Error opening %s for reading: %s\n",
		     input_file_name, strerror (errno));
	    return 1;
	}
    }

    if (batch) {
	if (opt_index != argc) {
	    fprintf (stderr, "Can't specify both cmdline and stdin!\n");
	    return 1;
	}
	if (remove_all) {
	    fprintf (stderr, "Can't specify both --remove-all and --batch\n");
	    return 1;
	}
    } else {
	tag_ops = tag_op_list_create (config);
	if (tag_ops == NULL) {
	    fprintf (stderr, "Out of memory.\n");
	    return 1;
	}

	if (parse_tag_command_line (config, argc - opt_index, argv + opt_index,
				    &query_string, tag_ops))
	    return 1;

	if (tag_op_list_size (tag_ops) == 0 && ! remove_all) {
	    fprintf (stderr, "Error: 'notmuch tag' requires at least one tag to add or remove.\n");
	    return 1;
	}
    }

    if (notmuch_database_open (notmuch_config_get_database_path (config),
			       NOTMUCH_DATABASE_MODE_READ_WRITE, &notmuch))
	return 1;

    if (notmuch_config_get_maildir_synchronize_flags (config))
	tag_flags |= TAG_FLAG_MAILDIR_SYNC;

    if (remove_all)
	tag_flags |= TAG_FLAG_REMOVE_ALL;

    if (batch)
	ret = tag_file (config, notmuch, tag_flags, input);
    else
	ret = tag_query (config, notmuch, query_string, tag_ops, tag_flags);

    notmuch_database_destroy (notmuch);

    if (input != stdin)
	fclose (input);

    return ret || interrupted;
}