static int parse_sup_line (void *ctx, char *line, char **query_str, tag_op_list_t *tag_ops) { regmatch_t match[3]; char *file_tags; int rerr; tag_op_list_reset (tag_ops); chomp_newline (line); /* Silently ignore blank lines */ if (line[0] == '\0') { return 1; } rerr = xregexec (®ex, line, 3, match, 0); if (rerr == REG_NOMATCH) { fprintf (stderr, "Warning: Ignoring invalid sup format line: %s\n", line); return 1; } *query_str = talloc_strndup_debug (ctx, line + match[1].rm_so, match[1].rm_eo - match[1].rm_so); file_tags = talloc_strndup_debug (ctx, line + match[2].rm_so, match[2].rm_eo - match[2].rm_so); char *tok = file_tags; size_t tok_len = 0; tag_op_list_reset (tag_ops); while ((tok = strtok_len (tok + tok_len, " ", &tok_len)) != NULL) { if (*(tok + tok_len) != '\0') { *(tok + tok_len) = '\0'; tok_len++; } if (tag_op_list_append (tag_ops, tok, FALSE)) return -1; } return 0; }
tag_parse_status_t parse_tag_command_line (void *ctx, int argc, char **argv, char **query_str, tag_op_list_t *tag_ops) { int i; tag_op_list_reset (tag_ops); for (i = 0; i < argc; i++) { if (strcmp (argv[i], "--") == 0) { i++; break; } if (argv[i][0] != '+' && argv[i][0] != '-') break; notmuch_bool_t is_remove = argv[i][0] == '-'; const char *msg; msg = illegal_tag (argv[i] + 1, is_remove); if (msg) { fprintf (stderr, "Error: %s", msg); return TAG_PARSE_INVALID; } tag_op_list_append (tag_ops, argv[i] + 1, is_remove); } *query_str = query_string_from_args (ctx, argc - i, &argv[i]); if (*query_str == NULL || **query_str == '\0') { fprintf (stderr, "Error: notmuch tag requires at least one search term.\n"); return TAG_PARSE_INVALID; } return TAG_PARSE_SUCCESS; }
int notmuch_insert_command (notmuch_config_t *config, int argc, char *argv[]) { notmuch_status_t status, close_status; notmuch_database_t *notmuch; struct sigaction action; const char *db_path; const char **new_tags; size_t new_tags_length; tag_op_list_t *tag_ops; char *query_string = NULL; const char *folder = NULL; notmuch_bool_t create_folder = FALSE; notmuch_bool_t keep = FALSE; notmuch_bool_t no_hooks = FALSE; notmuch_bool_t synchronize_flags; const char *maildir; char *newpath; int opt_index; unsigned int i; notmuch_opt_desc_t options[] = { { NOTMUCH_OPT_STRING, &folder, "folder", 0, 0 }, { NOTMUCH_OPT_BOOLEAN, &create_folder, "create-folder", 0, 0 }, { NOTMUCH_OPT_BOOLEAN, &keep, "keep", 0, 0 }, { NOTMUCH_OPT_BOOLEAN, &no_hooks, "no-hooks", 'n', 0 }, { NOTMUCH_OPT_INHERIT, (void *) ¬much_shared_options, NULL, 0, 0 }, { NOTMUCH_OPT_END, 0, 0, 0, 0 } }; opt_index = parse_arguments (argc, argv, options, 1); if (opt_index < 0) return EXIT_FAILURE; notmuch_process_shared_options (argv[0]); db_path = notmuch_config_get_database_path (config); new_tags = notmuch_config_get_new_tags (config, &new_tags_length); synchronize_flags = notmuch_config_get_maildir_synchronize_flags (config); tag_ops = tag_op_list_create (config); if (tag_ops == NULL) { fprintf (stderr, "Out of memory.\n"); return EXIT_FAILURE; } for (i = 0; i < new_tags_length; i++) { const char *error_msg; error_msg = illegal_tag (new_tags[i], FALSE); if (error_msg) { fprintf (stderr, "Error: tag '%s' in new.tags: %s\n", new_tags[i], error_msg); return EXIT_FAILURE; } if (tag_op_list_append (tag_ops, new_tags[i], FALSE)) return EXIT_FAILURE; } if (parse_tag_command_line (config, argc - opt_index, argv + opt_index, &query_string, tag_ops)) return EXIT_FAILURE; if (*query_string != '\0') { fprintf (stderr, "Error: unexpected query string: %s\n", query_string); return EXIT_FAILURE; } if (folder == NULL) { maildir = db_path; } else { if (! is_valid_folder_name (folder)) { fprintf (stderr, "Error: invalid folder name: '%s'\n", folder); return EXIT_FAILURE; } maildir = talloc_asprintf (config, "%s/%s", db_path, folder); if (! maildir) { fprintf (stderr, "Out of memory\n"); return EXIT_FAILURE; } if (create_folder && ! maildir_create_folder (config, maildir)) return EXIT_FAILURE; } /* Set up our handler for SIGINT. We do not set SA_RESTART so that copying * from standard input may be interrupted. */ memset (&action, 0, sizeof (struct sigaction)); action.sa_handler = handle_sigint; sigemptyset (&action.sa_mask); action.sa_flags = 0; sigaction (SIGINT, &action, NULL); /* Write the message to the Maildir new directory. */ newpath = maildir_write_new (config, STDIN_FILENO, maildir); if (! newpath) { return EXIT_FAILURE; } status = notmuch_database_open (notmuch_config_get_database_path (config), NOTMUCH_DATABASE_MODE_READ_WRITE, ¬much); if (status) return keep ? NOTMUCH_STATUS_SUCCESS : status_to_exit (status); notmuch_exit_if_unmatched_db_uuid (notmuch); /* Index the message. */ status = add_file (notmuch, newpath, tag_ops, synchronize_flags, keep); /* Commit changes. */ close_status = notmuch_database_destroy (notmuch); if (close_status) { /* Hold on to the first error, if any. */ if (! status) status = close_status; fprintf (stderr, "%s: failed to commit database changes: %s\n", keep ? "Warning" : "Error", notmuch_status_to_string (close_status)); } if (status) { if (keep) { status = NOTMUCH_STATUS_SUCCESS; } else { /* If maildir flag sync failed, this might fail. */ if (unlink (newpath)) { fprintf (stderr, "Warning: failed to remove '%s' from maildir " "after errors: %s. Please run 'notmuch new' to fix.\n", newpath, strerror (errno)); } } } if (! no_hooks && status == NOTMUCH_STATUS_SUCCESS) { /* Ignore hook failures. */ notmuch_run_hook (db_path, "post-insert"); } return status_to_exit (status); }
int notmuch_insert_command (notmuch_config_t *config, int argc, char *argv[]) { notmuch_database_t *notmuch; struct sigaction action; const char *db_path; const char **new_tags; size_t new_tags_length; tag_op_list_t *tag_ops; char *query_string = NULL; const char *folder = NULL; notmuch_bool_t create_folder = FALSE; const char *maildir; int opt_index; unsigned int i; notmuch_bool_t ret; notmuch_opt_desc_t options[] = { { NOTMUCH_OPT_STRING, &folder, "folder", 0, 0 }, { NOTMUCH_OPT_BOOLEAN, &create_folder, "create-folder", 0, 0 }, { NOTMUCH_OPT_END, 0, 0, 0, 0 } }; opt_index = parse_arguments (argc, argv, options, 1); if (opt_index < 0) { /* diagnostics already printed */ return 1; } db_path = notmuch_config_get_database_path (config); new_tags = notmuch_config_get_new_tags (config, &new_tags_length); tag_ops = tag_op_list_create (config); if (tag_ops == NULL) { fprintf (stderr, "Out of memory.\n"); return 1; } for (i = 0; i < new_tags_length; i++) { if (tag_op_list_append (tag_ops, new_tags[i], FALSE)) return 1; } if (parse_tag_command_line (config, argc - opt_index, argv + opt_index, &query_string, tag_ops)) return 1; if (*query_string != '\0') { fprintf (stderr, "Error: unexpected query string: %s\n", query_string); return 1; } if (folder == NULL) { maildir = db_path; } else { if (! check_folder_name (folder)) { fprintf (stderr, "Error: bad folder name: %s\n", folder); return 1; } maildir = talloc_asprintf (config, "%s/%s", db_path, folder); if (! maildir) { fprintf (stderr, "Out of memory\n"); return 1; } if (create_folder && ! maildir_create_folder (config, maildir)) { fprintf (stderr, "Error: creating maildir %s: %s\n", maildir, strerror (errno)); return 1; } } /* Setup our handler for SIGINT. We do not set SA_RESTART so that copying * from standard input may be interrupted. */ memset (&action, 0, sizeof (struct sigaction)); action.sa_handler = handle_sigint; sigemptyset (&action.sa_mask); action.sa_flags = 0; sigaction (SIGINT, &action, NULL); if (notmuch_database_open (notmuch_config_get_database_path (config), NOTMUCH_DATABASE_MODE_READ_WRITE, ¬much)) return 1; ret = insert_message (config, notmuch, STDIN_FILENO, maildir, tag_ops); notmuch_database_destroy (notmuch); return (ret) ? 0 : 1; }
tag_parse_status_t parse_tag_line (void *ctx, char *line, tag_op_flag_t flags, char **query_string, tag_op_list_t *tag_ops) { char *tok = line; size_t tok_len = 0; char *line_for_error; tag_parse_status_t ret = TAG_PARSE_SUCCESS; chomp_newline (line); line_for_error = talloc_strdup (ctx, line); if (line_for_error == NULL) { fprintf (stderr, "Error: out of memory\n"); return TAG_PARSE_OUT_OF_MEMORY; } /* remove leading space */ while (*tok == ' ' || *tok == '\t') tok++; /* Skip empty and comment lines. */ if (*tok == '\0' || *tok == '#') { ret = TAG_PARSE_SKIPPED; goto DONE; } tag_op_list_reset (tag_ops); /* Parse tags. */ while ((tok = strtok_len (tok + tok_len, " ", &tok_len)) != NULL) { notmuch_bool_t remove; char *tag; /* Optional explicit end of tags marker. */ if (tok_len == 2 && strncmp (tok, "--", tok_len) == 0) { tok = strtok_len (tok + tok_len, " ", &tok_len); if (tok == NULL) { ret = line_error (TAG_PARSE_INVALID, line_for_error, "no query string after --"); goto DONE; } break; } /* Implicit end of tags. */ if (*tok != '-' && *tok != '+') break; /* If tag is terminated by NUL, there's no query string. */ if (*(tok + tok_len) == '\0') { ret = line_error (TAG_PARSE_INVALID, line_for_error, "no query string"); goto DONE; } /* Terminate, and start next token after terminator. */ *(tok + tok_len++) = '\0'; remove = (*tok == '-'); tag = tok + 1; /* Maybe refuse illegal tags. */ if (! (flags & TAG_FLAG_BE_GENEROUS)) { const char *msg = illegal_tag (tag, remove); if (msg) { ret = line_error (TAG_PARSE_INVALID, line_for_error, msg); goto DONE; } } /* Decode tag. */ if (hex_decode_inplace (tag) != HEX_SUCCESS) { ret = line_error (TAG_PARSE_INVALID, line_for_error, "hex decoding of tag %s failed", tag); goto DONE; } if (tag_op_list_append (tag_ops, tag, remove)) { ret = line_error (TAG_PARSE_OUT_OF_MEMORY, line_for_error, "aborting"); goto DONE; } } if (tok == NULL) { /* use a different error message for testing */ ret = line_error (TAG_PARSE_INVALID, line_for_error, "missing query string"); goto DONE; } /* tok now points to the query string */ *query_string = tok; DONE: talloc_free (line_for_error); return ret; }