PageRenderTime 54ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/mh/pick.c

#
C | 400 lines | 300 code | 56 blank | 44 comment | 18 complexity | 9ac6c1b39ea080a9901e09dc249f0084 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-3.0, CC-BY-SA-3.0
  1. /* GNU Mailutils -- a suite of utilities for electronic mail
  2. Copyright (C) 2003, 2005-2012, 2014 Free Software Foundation, Inc.
  3. GNU Mailutils is free software; you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation; either version 3, or (at your option)
  6. any later version.
  7. GNU Mailutils is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
  13. /* MH pick command */
  14. #include <mh.h>
  15. #include <regex.h>
  16. #include <pick.h>
  17. #include <pick-gram.h>
  18. static char doc[] = N_("GNU MH pick")"\v"
  19. N_("Compatibility syntax for picking a matching component is:\n\
  20. \n\
  21. --Component pattern\n\
  22. \n\
  23. where Component is the component name, containing at least one capital\n\
  24. letter or followed by a colon, e.g.:\n\
  25. \n\
  26. --User-Agent Mailutils\n\
  27. --user-agent: Mailutils\n\
  28. \n\
  29. Use -help to obtain a list of traditional MH options.");
  30. static char args_doc[] = N_("[MSGLIST]");
  31. /* GNU options */
  32. static struct argp_option options[] = {
  33. #define GRID 10
  34. { "folder", ARG_FOLDER, N_("FOLDER"), 0,
  35. N_("specify folder to operate upon"), GRID },
  36. #undef GRID
  37. #define GRID 20
  38. { N_("Search patterns"), 0, NULL, OPTION_DOC, NULL, GRID },
  39. { "component", ARG_COMPONENT, N_("FIELD"), 0,
  40. N_("search the named header field"), GRID+1},
  41. { "pattern", ARG_PATTERN, N_("STRING"), 0,
  42. N_("set pattern to look for"), GRID+1 },
  43. { "search", 0, NULL, OPTION_ALIAS, NULL, GRID+1 },
  44. { "cflags", ARG_CFLAGS, N_("STRING"), 0,
  45. N_("flags controlling the type of regular expressions. STRING must consist of one or more of the following letters: B=basic, E=extended, I=ignore case, C=case sensitive. Default is \"EI\". The flags remain in effect until the next occurrence of --cflags option. The option must occur right before --pattern or --component option (or its alias)."), GRID+1 },
  46. { "cc", ARG_CC, N_("STRING"), 0,
  47. N_("same as --component cc --pattern STRING"), GRID+1 },
  48. { "date", ARG_DATE, N_("STRING"), 0,
  49. N_("same as --component date --pattern STRING"), GRID+1 },
  50. { "from", ARG_FROM, N_("STRING"), 0,
  51. N_("same as --component from --pattern STRING"), GRID+1 },
  52. { "subject", ARG_SUBJECT, N_("STRING"), 0,
  53. N_("same as --component subject --pattern STRING"), GRID+1 },
  54. { "to", ARG_TO, N_("STRING"), 0,
  55. N_("same as --component to --pattern STRING"), GRID+1 },
  56. #undef GRID
  57. #define GRID 30
  58. { N_("Date constraint operations"), 0, NULL, OPTION_DOC, NULL, GRID },
  59. { "datefield",ARG_DATEFIELD, N_("STRING"), 0,
  60. N_("search in the named date header field (default is `Date:')"), GRID+1 },
  61. { "after", ARG_AFTER, N_("DATE"), 0,
  62. N_("match messages after the given date"), GRID+1 },
  63. { "before", ARG_BEFORE, N_("DATE"), 0,
  64. N_("match messages before the given date"), GRID+1 },
  65. #undef GRID
  66. #define GRID 40
  67. { N_("Logical operations and grouping"), 0, NULL, OPTION_DOC, NULL, GRID },
  68. { "and", ARG_AND, NULL, 0,
  69. N_("logical AND (default)"), GRID+1 },
  70. { "or", ARG_OR, NULL, 0,
  71. N_("logical OR"), GRID+1 },
  72. { "not", ARG_NOT, NULL, 0,
  73. N_("logical NOT"), GRID+1 },
  74. { "lbrace", ARG_LBRACE, NULL, 0,
  75. N_("open group"), GRID+1 },
  76. { "(", 0, NULL, OPTION_ALIAS, NULL, GRID+1 },
  77. { "rbrace", ARG_RBRACE, NULL, 0,
  78. N_("close group"), GRID+1},
  79. { ")", 0, NULL, OPTION_ALIAS, NULL, GRID+1 },
  80. #undef GRID
  81. #define GRID 50
  82. { N_("Operations over the selected messages"), 0, NULL, OPTION_DOC, NULL,
  83. GRID },
  84. { "list", ARG_LIST, N_("BOOL"), OPTION_ARG_OPTIONAL,
  85. N_("list the numbers of the selected messages (default)"), GRID+1 },
  86. { "nolist", ARG_NOLIST, NULL, OPTION_HIDDEN, "", GRID+1 },
  87. { "sequence", ARG_SEQUENCE, N_("NAME"), 0,
  88. N_("add matching messages to the given sequence"), GRID+1 },
  89. { "public", ARG_PUBLIC, N_("BOOL"), OPTION_ARG_OPTIONAL,
  90. N_("create public sequence"), GRID+1 },
  91. { "nopublic", ARG_NOPUBLIC, NULL, OPTION_HIDDEN, "", GRID+1 },
  92. { "zero", ARG_ZERO, N_("BOOL"), OPTION_ARG_OPTIONAL,
  93. N_("empty the sequence before adding messages"), GRID+1 },
  94. { "nozero", ARG_NOZERO, NULL, OPTION_HIDDEN, "", GRID+1 },
  95. #undef GRID
  96. { NULL }
  97. };
  98. /* Traditional MH options */
  99. struct mh_option mh_option[] = {
  100. { "component", MH_OPT_ARG, "field" },
  101. { "pattern", MH_OPT_ARG, "pattern" },
  102. { "search", MH_OPT_ARG, "pattern" },
  103. { "cc", MH_OPT_ARG, "pattern" },
  104. { "date", MH_OPT_ARG, "pattern" },
  105. { "from", MH_OPT_ARG, "pattern" },
  106. { "subject", MH_OPT_ARG, "pattern" },
  107. { "to", MH_OPT_ARG, "pattern" },
  108. { "datefield", MH_OPT_ARG, "field" },
  109. { "after", MH_OPT_ARG, "date" },
  110. { "before", MH_OPT_ARG, "date" },
  111. { "and" },
  112. { "or" },
  113. { "not" },
  114. { "lbrace" },
  115. { "rbrace" },
  116. { "list", MH_OPT_BOOL },
  117. { "sequence", MH_OPT_ARG, "name" },
  118. { "public", MH_OPT_BOOL },
  119. { "zero", MH_OPT_BOOL },
  120. { NULL }
  121. };
  122. static int list = 1;
  123. static int seq_flags = 0; /* Create public sequences;
  124. Do not zero the sequence before addition */
  125. static mu_list_t seq_list; /* List of sequence names to operate upon */
  126. static mu_list_t lexlist; /* List of input tokens */
  127. static mu_msgset_t picked_message_uids;
  128. static void
  129. add_sequence (char *name)
  130. {
  131. if (!seq_list && mu_list_create (&seq_list))
  132. {
  133. mu_error (_("cannot create sequence list"));
  134. exit (1);
  135. }
  136. mu_list_append (seq_list, name);
  137. }
  138. static error_t
  139. opt_handler (int key, char *arg, struct argp_state *state)
  140. {
  141. switch (key)
  142. {
  143. case ARG_FOLDER:
  144. mh_set_current_folder (arg);
  145. break;
  146. case ARG_SEQUENCE:
  147. add_sequence (arg);
  148. list = 0;
  149. break;
  150. case ARG_LIST:
  151. list = is_true (arg);
  152. break;
  153. case ARG_NOLIST:
  154. list = 0;
  155. break;
  156. case ARG_COMPONENT:
  157. pick_add_token (&lexlist, T_COMP, arg);
  158. break;
  159. case ARG_PATTERN:
  160. pick_add_token (&lexlist, T_STRING, arg);
  161. break;
  162. case ARG_CC:
  163. pick_add_token (&lexlist, T_COMP, "cc");
  164. pick_add_token (&lexlist, T_STRING, arg);
  165. break;
  166. case ARG_DATE:
  167. pick_add_token (&lexlist, T_COMP, "date");
  168. pick_add_token (&lexlist, T_STRING, arg);
  169. break;
  170. case ARG_FROM:
  171. pick_add_token (&lexlist, T_COMP, "from");
  172. pick_add_token (&lexlist, T_STRING, arg);
  173. break;
  174. case ARG_SUBJECT:
  175. pick_add_token (&lexlist, T_COMP, "subject");
  176. pick_add_token (&lexlist, T_STRING, arg);
  177. break;
  178. case ARG_TO:
  179. pick_add_token (&lexlist, T_COMP, "to");
  180. pick_add_token (&lexlist, T_STRING, arg);
  181. break;
  182. case ARG_DATEFIELD:
  183. pick_add_token (&lexlist, T_DATEFIELD, arg);
  184. break;
  185. case ARG_AFTER:
  186. pick_add_token (&lexlist, T_AFTER, NULL);
  187. pick_add_token (&lexlist, T_STRING, arg);
  188. break;
  189. case ARG_BEFORE:
  190. pick_add_token (&lexlist, T_BEFORE, NULL);
  191. pick_add_token (&lexlist, T_STRING, arg);
  192. break;
  193. case ARG_AND:
  194. pick_add_token (&lexlist, T_AND, NULL);
  195. break;
  196. case ARG_OR:
  197. pick_add_token (&lexlist, T_OR, NULL);
  198. break;
  199. case ARG_NOT:
  200. pick_add_token (&lexlist, T_NOT, NULL);
  201. break;
  202. case ARG_LBRACE:
  203. pick_add_token (&lexlist, T_LBRACE, NULL);
  204. break;
  205. case ARG_RBRACE:
  206. pick_add_token (&lexlist, T_RBRACE, NULL);
  207. break;
  208. case ARG_CFLAGS:
  209. pick_add_token (&lexlist, T_CFLAGS, arg);
  210. break;
  211. case ARG_PUBLIC:
  212. if (is_true (arg))
  213. seq_flags &= ~SEQ_PRIVATE;
  214. else
  215. seq_flags |= SEQ_PRIVATE;
  216. break;
  217. case ARG_NOPUBLIC:
  218. seq_flags |= SEQ_PRIVATE;
  219. break;
  220. case ARG_ZERO:
  221. if (is_true (arg))
  222. seq_flags |= SEQ_ZERO;
  223. else
  224. seq_flags &= ~SEQ_ZERO;
  225. break;
  226. case ARG_NOZERO:
  227. seq_flags &= ~SEQ_ZERO;
  228. break;
  229. default:
  230. return ARGP_ERR_UNKNOWN;
  231. }
  232. return 0;
  233. }
  234. static int
  235. pick_message (size_t num, mu_message_t msg, void *data)
  236. {
  237. if (pick_eval (msg))
  238. {
  239. mh_message_number (msg, &num);
  240. if (list)
  241. printf ("%s\n", mu_umaxtostr (0, num));
  242. if (picked_message_uids)
  243. mu_msgset_add_range (picked_message_uids, num, num, MU_MSGSET_UID);
  244. }
  245. return 0;
  246. }
  247. static int
  248. action_add (void *item, void *data)
  249. {
  250. mu_mailbox_t mbox = data;
  251. mh_seq_add (mbox, (char *)item, picked_message_uids, seq_flags);
  252. return 0;
  253. }
  254. void
  255. pick_help_hook (void)
  256. {
  257. printf ("\n");
  258. printf (_("To match another component, use:\n\n"));
  259. printf (_(" --Component pattern\n\n"));
  260. printf (_("Note, that the component name must either be capitalized,\n"
  261. "or followed by a colon.\n"));
  262. printf ("\n");
  263. }
  264. /* NOTICE: For compatibility with RAND MH we have to support
  265. the following command line syntax:
  266. --COMP STRING
  267. where `COMP' may be any string and which is equivalent to
  268. `--component FIELD --pattern STRING'. Obviously this is in conflict
  269. with the usual GNU long options paradigm which requires that any
  270. unrecognized long option produce an error. The following
  271. compromise solution is used:
  272. The arguments `--COMP STRING' is recognized as a component matching
  273. request if any of the following conditions is met:
  274. 1. The word `COMP' contains at least one capital letter. E.g.:
  275. --User-Agent Mailutils
  276. 2. The word `COMP' ends with a colon, e.g.:
  277. --user-agent: Mailutils
  278. 3. Standard input is not connected to a terminal. This is always
  279. true when pick is invoked from mh-pick.el Emacs module.
  280. */
  281. int
  282. main (int argc, char **argv)
  283. {
  284. int status;
  285. int index;
  286. mu_mailbox_t mbox;
  287. mu_msgset_t msgset;
  288. int interactive = mh_interactive_mode_p ();
  289. MU_APP_INIT_NLS ();
  290. for (index = 1; index < argc; index++)
  291. {
  292. int colon = 0, cpos;
  293. if (argv[index][0] == '-' && argv[index][1] == '-' &&
  294. !strchr (argv[index], '=') &&
  295. (!interactive ||
  296. (colon = argv[index][cpos = strlen (argv[index]) - 1] == ':') ||
  297. *mu_str_skip_class_comp (argv[index], MU_CTYPE_UPPER)) &&
  298. index + 1 < argc)
  299. {
  300. if (colon)
  301. {
  302. cpos -= 2;
  303. mu_asprintf (&argv[index], "--component=%*.*s", cpos, cpos,
  304. argv[index] + 2);
  305. }
  306. else
  307. mu_asprintf (&argv[index], "--component=%s", argv[index] + 2);
  308. mu_asprintf (&argv[index + 1], "--pattern=%s", argv[index + 1]);
  309. index++;
  310. }
  311. }
  312. mh_help_hook = pick_help_hook;
  313. mh_argp_init ();
  314. mh_argp_parse (&argc, &argv, 0, options, mh_option,
  315. args_doc, doc, opt_handler, NULL, &index);
  316. if (pick_parse (lexlist))
  317. return 1;
  318. mbox = mh_open_folder (mh_current_folder (),
  319. seq_list ? MU_STREAM_RDWR : MU_STREAM_READ);
  320. argc -= index;
  321. argv += index;
  322. if (seq_list)
  323. mu_msgset_create (&picked_message_uids, NULL, MU_MSGSET_UID);
  324. mh_msgset_parse (&msgset, mbox, argc, argv, "all");
  325. status = mu_msgset_foreach_message (msgset, pick_message, NULL);
  326. if (picked_message_uids)
  327. mu_list_foreach (seq_list, action_add, mbox);
  328. mh_global_save_state ();
  329. mu_mailbox_close (mbox);
  330. mu_mailbox_destroy (&mbox);
  331. return status;
  332. }