PageRenderTime 26ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/mail/msgset.y

#
Happy | 776 lines | 697 code | 79 blank | 0 comment | 0 complexity | 78c6663de8c7996d1dc191e41d4a5c8c 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) 1999-2003, 2005, 2007, 2009-2012, 2014 Free Software
  3. Foundation, Inc.
  4. GNU Mailutils is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 3, or (at your option)
  7. any later version.
  8. GNU Mailutils is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
  14. %{
  15. #include "mail.h"
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. /* Defined in <limits.h> on some systems, but redefined in <regex.h>
  19. if we are using GNU's regex. So, undef it to avoid duplicate definition
  20. warnings. */
  21. #ifdef RE_DUP_MAX
  22. # undef RE_DUP_MAX
  23. #endif
  24. #include <regex.h>
  25. struct header_data
  26. {
  27. char *header;
  28. char *expr;
  29. };
  30. static msgset_t *msgset_select (int (*sel) (mu_message_t, void *),
  31. void *closure, int rev,
  32. unsigned int max_matches);
  33. static int select_header (mu_message_t msg, void *closure);
  34. static int select_body (mu_message_t msg, void *closure);
  35. static int select_type (mu_message_t msg, void *closure);
  36. static int select_sender (mu_message_t msg, void *closure);
  37. static int select_deleted (mu_message_t msg, void *closure);
  38. static int check_set (msgset_t **pset);
  39. int yyerror (const char *);
  40. int yylex (void);
  41. static int msgset_flags = MSG_NODELETED;
  42. static msgset_t *result;
  43. %}
  44. %union {
  45. char *string;
  46. int number;
  47. int type;
  48. msgset_t *mset;
  49. }
  50. %token <type> TYPE
  51. %token <string> IDENT REGEXP HEADER BODY
  52. %token <number> NUMBER
  53. %type <mset> msgset msgspec msgexpr msg rangeset range partno number
  54. %type <string> header
  55. %%
  56. input : /* empty */
  57. {
  58. result = msgset_make_1 (get_cursor ());
  59. }
  60. | '.'
  61. {
  62. result = msgset_make_1 (get_cursor ());
  63. }
  64. | msgset
  65. {
  66. result = $1;
  67. }
  68. | '^'
  69. {
  70. result = msgset_select (select_deleted, NULL, 0, 1);
  71. }
  72. | '$'
  73. {
  74. result = msgset_select (select_deleted, NULL, 1, 1);
  75. }
  76. | '*'
  77. {
  78. result = msgset_select (select_deleted, NULL, 0, total);
  79. }
  80. | '-'
  81. {
  82. result = msgset_select (select_deleted, NULL, 1, 1);
  83. }
  84. | '+'
  85. {
  86. result = msgset_select (select_deleted, NULL, 0, 1);
  87. }
  88. ;
  89. msgset : msgexpr
  90. | msgset ',' msgexpr
  91. {
  92. $$ = msgset_append ($1, $3);
  93. }
  94. | msgset msgexpr
  95. {
  96. $$ = msgset_append ($1, $2);
  97. }
  98. ;
  99. msgexpr : msgspec
  100. {
  101. $$ = $1;
  102. if (check_set (&$$))
  103. YYABORT;
  104. }
  105. | '{' msgset '}'
  106. {
  107. $$ = $2;
  108. }
  109. | '!' msgexpr
  110. {
  111. $$ = msgset_negate ($2);
  112. }
  113. ;
  114. msgspec : msg
  115. | msg '[' rangeset ']'
  116. {
  117. $$ = msgset_expand ($1, $3);
  118. msgset_free ($1);
  119. msgset_free ($3);
  120. }
  121. | range
  122. ;
  123. msg : header REGEXP /* /.../ */
  124. {
  125. struct header_data hd;
  126. hd.header = $1;
  127. hd.expr = $2;
  128. $$ = msgset_select (select_header, &hd, 0, 0);
  129. if ($1)
  130. free ($1);
  131. free ($2);
  132. }
  133. | BODY
  134. {
  135. $$ = msgset_select (select_body, $1, 0, 0);
  136. }
  137. | TYPE /* :n, :d, etc */
  138. {
  139. if (strchr ("dnorTtu", $1) == NULL)
  140. {
  141. yyerror (_("unknown message type"));
  142. YYERROR;
  143. }
  144. $$ = msgset_select (select_type, (void *)&$1, 0, 0);
  145. }
  146. | IDENT /* Sender name */
  147. {
  148. $$ = msgset_select (select_sender, (void *)$1, 0, 0);
  149. free ($1);
  150. }
  151. ;
  152. header : /* empty */
  153. {
  154. $$ = NULL;
  155. }
  156. | HEADER
  157. {
  158. $$ = $1;
  159. }
  160. ;
  161. rangeset : range
  162. | rangeset ',' range
  163. {
  164. $$ = msgset_append ($1, $3);
  165. }
  166. | rangeset range
  167. {
  168. $$ = msgset_append ($1, $2);
  169. }
  170. ;
  171. range : number
  172. | NUMBER '-' number
  173. {
  174. if ($3->npart == 1)
  175. {
  176. $$ = msgset_range ($1, $3->msg_part[0]);
  177. }
  178. else
  179. {
  180. $$ = msgset_range ($1, $3->msg_part[0]-1);
  181. if (!$$)
  182. YYERROR;
  183. msgset_append ($$, $3);
  184. }
  185. }
  186. | NUMBER '-' '*'
  187. {
  188. $$ = msgset_range ($1, total);
  189. }
  190. ;
  191. number : partno
  192. | partno '[' rangeset ']'
  193. {
  194. $$ = msgset_expand ($1, $3);
  195. msgset_free ($1);
  196. msgset_free ($3);
  197. }
  198. ;
  199. partno : NUMBER
  200. {
  201. $$ = msgset_make_1 ($1);
  202. }
  203. | '(' rangeset ')'
  204. {
  205. $$ = $2;
  206. }
  207. ;
  208. %%
  209. static int xargc;
  210. static char **xargv;
  211. static int cur_ind;
  212. static char *cur_p;
  213. int
  214. yyerror (const char *s)
  215. {
  216. mu_stream_printf (mu_strerr, "%s: ", xargv[0]);
  217. mu_stream_printf (mu_strerr, "%s", s);
  218. if (!cur_p)
  219. mu_stream_printf (mu_strerr, _(" near end"));
  220. else if (*cur_p == 0)
  221. {
  222. int i = (*cur_p == 0) ? cur_ind + 1 : cur_ind;
  223. if (i == xargc)
  224. mu_stream_printf (mu_strerr, _(" near end"));
  225. else
  226. mu_stream_printf (mu_strerr, _(" near %s"), xargv[i]);
  227. }
  228. else
  229. mu_stream_printf (mu_strerr, _(" near %s"), cur_p);
  230. mu_stream_printf (mu_strerr, "\n");
  231. return 0;
  232. }
  233. int
  234. yylex()
  235. {
  236. if (cur_ind == xargc)
  237. return 0;
  238. if (!cur_p)
  239. cur_p = xargv[cur_ind];
  240. if (*cur_p == 0)
  241. {
  242. cur_ind++;
  243. cur_p = NULL;
  244. return yylex ();
  245. }
  246. if (mu_isdigit (*cur_p))
  247. {
  248. yylval.number = strtoul (cur_p, &cur_p, 10);
  249. return NUMBER;
  250. }
  251. if (mu_isalpha (*cur_p))
  252. {
  253. char *p = cur_p;
  254. int len;
  255. while (*cur_p && *cur_p != ',' && *cur_p != ':')
  256. cur_p++;
  257. len = cur_p - p + 1;
  258. yylval.string = mu_alloc (len);
  259. memcpy (yylval.string, p, len-1);
  260. yylval.string[len-1] = 0;
  261. if (*cur_p == ':')
  262. {
  263. ++cur_p;
  264. return HEADER;
  265. }
  266. return IDENT;
  267. }
  268. if (*cur_p == '/')
  269. {
  270. char *p = ++cur_p;
  271. int len;
  272. while (*cur_p && *cur_p != '/')
  273. cur_p++;
  274. len = cur_p - p + 1;
  275. cur_p++;
  276. yylval.string = mu_alloc (len);
  277. memcpy (yylval.string, p, len-1);
  278. yylval.string[len-1] = 0;
  279. return REGEXP;
  280. }
  281. if (*cur_p == ':')
  282. {
  283. cur_p++;
  284. if (*cur_p == '/')
  285. {
  286. char *p = ++cur_p;
  287. int len;
  288. while (*cur_p && *cur_p != '/')
  289. cur_p++;
  290. len = cur_p - p + 1;
  291. cur_p++;
  292. yylval.string = mu_alloc (len);
  293. memcpy (yylval.string, p, len-1);
  294. yylval.string[len-1] = 0;
  295. return BODY;
  296. }
  297. yylval.type = *cur_p++;
  298. return TYPE;
  299. }
  300. return *cur_p++;
  301. }
  302. int
  303. msgset_parse (const int argc, char **argv, int flags, msgset_t **mset)
  304. {
  305. int rc;
  306. xargc = argc;
  307. xargv = argv;
  308. msgset_flags = flags;
  309. cur_ind = 1;
  310. cur_p = NULL;
  311. result = NULL;
  312. rc = yyparse ();
  313. if (rc == 0)
  314. {
  315. if (result == NULL)
  316. {
  317. util_noapp ();
  318. rc = 1;
  319. }
  320. else
  321. *mset = result;
  322. }
  323. return rc;
  324. }
  325. void
  326. msgset_free (msgset_t *msg_set)
  327. {
  328. msgset_t *next;
  329. if (!msg_set)
  330. return;
  331. while (msg_set)
  332. {
  333. next = msg_set->next;
  334. if (msg_set->msg_part)
  335. free (msg_set->msg_part);
  336. free (msg_set);
  337. msg_set = next;
  338. }
  339. }
  340. size_t
  341. msgset_count (msgset_t *set)
  342. {
  343. size_t count = 0;
  344. for (; set; set = set->next)
  345. count++;
  346. return count;
  347. }
  348. /* Create a message set consisting of a single msg_num and no subparts */
  349. msgset_t *
  350. msgset_make_1 (size_t number)
  351. {
  352. msgset_t *mp;
  353. if (number == 0)
  354. return NULL;
  355. mp = mu_alloc (sizeof (*mp));
  356. mp->next = NULL;
  357. mp->npart = 1;
  358. mp->msg_part = mu_alloc (sizeof mp->msg_part[0]);
  359. mp->msg_part[0] = number;
  360. return mp;
  361. }
  362. msgset_t *
  363. msgset_dup (const msgset_t *set)
  364. {
  365. msgset_t *mp;
  366. mp = mu_alloc (sizeof (*mp));
  367. mp->next = NULL;
  368. mp->npart = set->npart;
  369. mp->msg_part = mu_calloc (mp->npart, sizeof mp->msg_part[0]);
  370. memcpy (mp->msg_part, set->msg_part, mp->npart * sizeof mp->msg_part[0]);
  371. return mp;
  372. }
  373. msgset_t *
  374. msgset_append (msgset_t *one, msgset_t *two)
  375. {
  376. msgset_t *last;
  377. if (!one)
  378. return two;
  379. for (last = one; last->next; last = last->next)
  380. ;
  381. last->next = two;
  382. return one;
  383. }
  384. int
  385. msgset_member (msgset_t *set, size_t n)
  386. {
  387. for (; set; set = set->next)
  388. if (set->msg_part[0] == n)
  389. return 1;
  390. return 0;
  391. }
  392. msgset_t *
  393. msgset_negate (msgset_t *set)
  394. {
  395. size_t i;
  396. msgset_t *first = NULL, *last = NULL;
  397. for (i = 1; i <= total; i++)
  398. {
  399. if (!msgset_member (set, i))
  400. {
  401. msgset_t *mp = msgset_make_1 (i);
  402. if (!first)
  403. first = mp;
  404. else
  405. last->next = mp;
  406. last = mp;
  407. }
  408. }
  409. return first;
  410. }
  411. msgset_t *
  412. msgset_range (int low, int high)
  413. {
  414. int i;
  415. msgset_t *mp, *first = NULL, *last = NULL;
  416. if (low == high)
  417. return msgset_make_1 (low);
  418. if (low >= high)
  419. {
  420. yyerror (_("range error"));
  421. return NULL;
  422. }
  423. for (i = 0; low <= high; i++, low++)
  424. {
  425. mp = msgset_make_1 (low);
  426. if (!first)
  427. first = mp;
  428. else
  429. last->next = mp;
  430. last = mp;
  431. }
  432. return first;
  433. }
  434. msgset_t *
  435. msgset_expand (msgset_t *set, msgset_t *expand_by)
  436. {
  437. msgset_t *i, *j;
  438. msgset_t *first = NULL, *last = NULL, *mp;
  439. for (i = set; i; i = i->next)
  440. for (j = expand_by; j; j = j->next)
  441. {
  442. mp = mu_alloc (sizeof *mp);
  443. mp->next = NULL;
  444. mp->npart = i->npart + j->npart;
  445. mp->msg_part = mu_calloc (mp->npart, sizeof mp->msg_part[0]);
  446. memcpy (mp->msg_part, i->msg_part, i->npart * sizeof i->msg_part[0]);
  447. memcpy (mp->msg_part + i->npart, j->msg_part,
  448. j->npart * sizeof j->msg_part[0]);
  449. if (!first)
  450. first = mp;
  451. else
  452. last->next = mp;
  453. last = mp;
  454. }
  455. return first;
  456. }
  457. msgset_t *
  458. msgset_select (int (*sel) (mu_message_t, void *), void *closure, int rev,
  459. unsigned int max_matches)
  460. {
  461. size_t i, match_count = 0;
  462. msgset_t *first = NULL, *last = NULL, *mp;
  463. mu_message_t msg = NULL;
  464. if (max_matches == 0)
  465. max_matches = total;
  466. if (rev)
  467. {
  468. for (i = total; i > 0; i--)
  469. {
  470. mu_mailbox_get_message (mbox, i, &msg);
  471. if ((*sel)(msg, closure))
  472. {
  473. mp = msgset_make_1 (i);
  474. if (!first)
  475. first = mp;
  476. else
  477. last->next = mp;
  478. last = mp;
  479. if (++match_count == max_matches)
  480. break;
  481. }
  482. }
  483. }
  484. else
  485. {
  486. for (i = 1; i <= total; i++)
  487. {
  488. mu_mailbox_get_message (mbox, i, &msg);
  489. if ((*sel)(msg, closure))
  490. {
  491. mp = msgset_make_1 (i);
  492. if (!first)
  493. first = mp;
  494. else
  495. last->next = mp;
  496. last = mp;
  497. if (++match_count == max_matches)
  498. break;
  499. }
  500. }
  501. }
  502. return first;
  503. }
  504. int
  505. select_header (mu_message_t msg, void *closure)
  506. {
  507. struct header_data *hd = (struct header_data *)closure;
  508. mu_header_t hdr;
  509. char *contents;
  510. const char *header = hd->header ? hd->header : MU_HEADER_SUBJECT;
  511. mu_message_get_header (msg, &hdr);
  512. if (mu_header_aget_value (hdr, header, &contents) == 0)
  513. {
  514. if (mailvar_get (NULL, "regex", mailvar_type_boolean, 0) == 0)
  515. {
  516. /* Match string against the extended regular expression(ignoring
  517. case) in pattern, treating errors as no match.
  518. Return 1 for match, 0 for no match.
  519. */
  520. regex_t re;
  521. int status;
  522. int flags = REG_EXTENDED;
  523. if (mu_islower (header[0]))
  524. flags |= REG_ICASE;
  525. if (regcomp (&re, hd->expr, flags) != 0)
  526. {
  527. free (contents);
  528. return 0;
  529. }
  530. status = regexec (&re, contents, 0, NULL, 0);
  531. free (contents);
  532. regfree (&re);
  533. return status == 0;
  534. }
  535. else
  536. {
  537. int rc;
  538. mu_strupper (contents);
  539. rc = strstr (contents, hd->expr) != NULL;
  540. free (contents);
  541. return rc;
  542. }
  543. }
  544. return 0;
  545. }
  546. int
  547. select_body (mu_message_t msg, void *closure)
  548. {
  549. char *expr = closure;
  550. int noregex = mailvar_get (NULL, "regex", mailvar_type_boolean, 0);
  551. regex_t re;
  552. int status;
  553. mu_body_t body = NULL;
  554. mu_stream_t stream = NULL;
  555. size_t size = 0, lines = 0;
  556. char buffer[128];
  557. size_t n = 0;
  558. if (noregex)
  559. mu_strupper (expr);
  560. else if (regcomp (&re, expr, REG_EXTENDED | REG_ICASE) != 0)
  561. return 0;
  562. mu_message_get_body (msg, &body);
  563. mu_body_size (body, &size);
  564. mu_body_lines (body, &lines);
  565. status = mu_body_get_streamref (body, &stream);
  566. while (status == 0
  567. && mu_stream_readline (stream, buffer, sizeof(buffer)-1, &n) == 0
  568. && n > 0)
  569. {
  570. if (noregex)
  571. {
  572. mu_strupper (buffer);
  573. status = strstr (buffer, expr) != NULL;
  574. }
  575. else
  576. status = regexec (&re, buffer, 0, NULL, 0);
  577. }
  578. mu_stream_destroy (&stream);
  579. if (!noregex)
  580. regfree (&re);
  581. return status;
  582. }
  583. int
  584. select_sender (mu_message_t msg MU_ARG_UNUSED, void *closure MU_ARG_UNUSED)
  585. {
  586. /* char *sender = (char*) closure; */
  587. /* FIXME: all messages from sender argv[i] */
  588. /* Annoying we can use mu_address_create() for that
  589. but to compare against what? The email ? */
  590. return 0;
  591. }
  592. int
  593. select_type (mu_message_t msg, void *closure)
  594. {
  595. int type = *(int*) closure;
  596. mu_attribute_t attr= NULL;
  597. mu_message_get_attribute (msg, &attr);
  598. switch (type)
  599. {
  600. case 'd':
  601. return mu_attribute_is_deleted (attr);
  602. case 'n':
  603. return mu_attribute_is_recent (attr);
  604. case 'o':
  605. return mu_attribute_is_seen (attr);
  606. case 'r':
  607. return mu_attribute_is_read (attr);
  608. case 'u':
  609. return !mu_attribute_is_read (attr);
  610. case 't':
  611. return mu_attribute_is_userflag (attr, MAIL_ATTRIBUTE_TAGGED);
  612. case 'T':
  613. return !mu_attribute_is_userflag (attr, MAIL_ATTRIBUTE_TAGGED);
  614. }
  615. return 0;
  616. }
  617. int
  618. select_deleted (mu_message_t msg, void *closure MU_ARG_UNUSED)
  619. {
  620. mu_attribute_t attr= NULL;
  621. int rc;
  622. mu_message_get_attribute (msg, &attr);
  623. rc = mu_attribute_is_deleted (attr);
  624. return strcmp (xargv[0], "undelete") == 0 ? rc : !rc;
  625. }
  626. int
  627. check_set (msgset_t **pset)
  628. {
  629. int flags = msgset_flags;
  630. int rc = 0;
  631. if (msgset_count (*pset) == 1)
  632. flags ^= MSG_SILENT;
  633. if (flags & MSG_NODELETED)
  634. {
  635. msgset_t *p = *pset, *prev = NULL;
  636. msgset_t *delset = NULL;
  637. while (p)
  638. {
  639. msgset_t *next = p->next;
  640. if (util_isdeleted (p->msg_part[0]))
  641. {
  642. if ((flags & MSG_SILENT) && (prev || next))
  643. {
  644. /* Mark subset as deleted */
  645. p->next = delset;
  646. delset = p;
  647. /* Remove it from the set */
  648. if (prev)
  649. prev->next = next;
  650. else
  651. *pset = next;
  652. }
  653. else
  654. {
  655. mu_error (_("%lu: Inappropriate message (has been deleted)"),
  656. (unsigned long) p->msg_part[0]);
  657. /* Delete entire set */
  658. delset = *pset;
  659. *pset = NULL;
  660. rc = 1;
  661. break;
  662. }
  663. }
  664. else
  665. prev = p;
  666. p = next;
  667. }
  668. if (delset)
  669. msgset_free (delset);
  670. if (!*pset)
  671. rc = 1;
  672. }
  673. return rc;
  674. }
  675. #if 0
  676. void
  677. msgset_print (msgset_t *mset)
  678. {
  679. int i;
  680. mu_printf ("(");
  681. mu_printf ("%d .", mset->msg_part[0]);
  682. for (i = 1; i < mset->npart; i++)
  683. {
  684. mu_printf (" %d", mset->msg_part[i]);
  685. }
  686. mu_printf (")\n");
  687. }
  688. int
  689. main(int argc, char **argv)
  690. {
  691. msgset_t *mset = NULL;
  692. int rc = msgset_parse (argc, argv, &mset);
  693. for (; mset; mset = mset->next)
  694. msgset_print (mset);
  695. return 0;
  696. }
  697. #endif