PageRenderTime 28ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/usr.bin/mail/list.c

https://bitbucket.org/evzijst/freebsd
C | 816 lines | 696 code | 38 blank | 82 comment | 142 complexity | 85951dd3f2eb5a09728f8f4ecd789075 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.0, AGPL-1.0, LGPL-2.1, GPL-2.0, BSD-2-Clause, 0BSD, MPL-2.0-no-copyleft-exception
  1. /*
  2. * Copyright (c) 1980, 1993
  3. * The Regents of the University of California. All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions
  7. * are met:
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. * 4. Neither the name of the University nor the names of its contributors
  14. * may be used to endorse or promote products derived from this software
  15. * without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  18. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20. * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  21. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  22. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  23. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  24. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  25. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  26. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  27. * SUCH DAMAGE.
  28. */
  29. #ifndef lint
  30. #if 0
  31. static char sccsid[] = "@(#)list.c 8.4 (Berkeley) 5/1/95";
  32. #endif
  33. #endif /* not lint */
  34. #include <sys/cdefs.h>
  35. __FBSDID("$FreeBSD$");
  36. #include "rcv.h"
  37. #include <ctype.h>
  38. #include "extern.h"
  39. /*
  40. * Mail -- a mail program
  41. *
  42. * Message list handling.
  43. */
  44. /*
  45. * Convert the user string of message numbers and
  46. * store the numbers into vector.
  47. *
  48. * Returns the count of messages picked up or -1 on error.
  49. */
  50. int
  51. getmsglist(char *buf, int *vector, int flags)
  52. {
  53. int *ip;
  54. struct message *mp;
  55. if (msgCount == 0) {
  56. *vector = 0;
  57. return (0);
  58. }
  59. if (markall(buf, flags) < 0)
  60. return (-1);
  61. ip = vector;
  62. for (mp = &message[0]; mp < &message[msgCount]; mp++)
  63. if (mp->m_flag & MMARK)
  64. *ip++ = mp - &message[0] + 1;
  65. *ip = 0;
  66. return (ip - vector);
  67. }
  68. /*
  69. * Mark all messages that the user wanted from the command
  70. * line in the message structure. Return 0 on success, -1
  71. * on error.
  72. */
  73. /*
  74. * Bit values for colon modifiers.
  75. */
  76. #define CMNEW 01 /* New messages */
  77. #define CMOLD 02 /* Old messages */
  78. #define CMUNREAD 04 /* Unread messages */
  79. #define CMDELETED 010 /* Deleted messages */
  80. #define CMREAD 020 /* Read messages */
  81. /*
  82. * The following table describes the letters which can follow
  83. * the colon and gives the corresponding modifier bit.
  84. */
  85. static struct coltab {
  86. char co_char; /* What to find past : */
  87. int co_bit; /* Associated modifier bit */
  88. int co_mask; /* m_status bits to mask */
  89. int co_equal; /* ... must equal this */
  90. } coltab[] = {
  91. { 'n', CMNEW, MNEW, MNEW },
  92. { 'o', CMOLD, MNEW, 0 },
  93. { 'u', CMUNREAD, MREAD, 0 },
  94. { 'd', CMDELETED, MDELETED, MDELETED},
  95. { 'r', CMREAD, MREAD, MREAD },
  96. { 0, 0, 0, 0 }
  97. };
  98. static int lastcolmod;
  99. int
  100. markall(char buf[], int f)
  101. {
  102. char **np;
  103. int i;
  104. struct message *mp;
  105. char *namelist[NMLSIZE], *bufp;
  106. int tok, beg, mc, star, other, valdot, colmod, colresult;
  107. valdot = dot - &message[0] + 1;
  108. colmod = 0;
  109. for (i = 1; i <= msgCount; i++)
  110. unmark(i);
  111. bufp = buf;
  112. mc = 0;
  113. np = &namelist[0];
  114. scaninit();
  115. tok = scan(&bufp);
  116. star = 0;
  117. other = 0;
  118. beg = 0;
  119. while (tok != TEOL) {
  120. switch (tok) {
  121. case TNUMBER:
  122. number:
  123. if (star) {
  124. printf("No numbers mixed with *\n");
  125. return (-1);
  126. }
  127. mc++;
  128. other++;
  129. if (beg != 0) {
  130. if (check(lexnumber, f))
  131. return (-1);
  132. for (i = beg; i <= lexnumber; i++)
  133. if (f == MDELETED || (message[i - 1].m_flag & MDELETED) == 0)
  134. mark(i);
  135. beg = 0;
  136. break;
  137. }
  138. beg = lexnumber;
  139. if (check(beg, f))
  140. return (-1);
  141. tok = scan(&bufp);
  142. regret(tok);
  143. if (tok != TDASH) {
  144. mark(beg);
  145. beg = 0;
  146. }
  147. break;
  148. case TPLUS:
  149. if (beg != 0) {
  150. printf("Non-numeric second argument\n");
  151. return (-1);
  152. }
  153. i = valdot;
  154. do {
  155. i++;
  156. if (i > msgCount) {
  157. printf("Referencing beyond EOF\n");
  158. return (-1);
  159. }
  160. } while ((message[i - 1].m_flag & MDELETED) != f);
  161. mark(i);
  162. break;
  163. case TDASH:
  164. if (beg == 0) {
  165. i = valdot;
  166. do {
  167. i--;
  168. if (i <= 0) {
  169. printf("Referencing before 1\n");
  170. return (-1);
  171. }
  172. } while ((message[i - 1].m_flag & MDELETED) != f);
  173. mark(i);
  174. }
  175. break;
  176. case TSTRING:
  177. if (beg != 0) {
  178. printf("Non-numeric second argument\n");
  179. return (-1);
  180. }
  181. other++;
  182. if (lexstring[0] == ':') {
  183. colresult = evalcol(lexstring[1]);
  184. if (colresult == 0) {
  185. printf("Unknown colon modifier \"%s\"\n",
  186. lexstring);
  187. return (-1);
  188. }
  189. colmod |= colresult;
  190. }
  191. else
  192. *np++ = savestr(lexstring);
  193. break;
  194. case TDOLLAR:
  195. case TUP:
  196. case TDOT:
  197. lexnumber = metamess(lexstring[0], f);
  198. if (lexnumber == -1)
  199. return (-1);
  200. goto number;
  201. case TSTAR:
  202. if (other) {
  203. printf("Can't mix \"*\" with anything\n");
  204. return (-1);
  205. }
  206. star++;
  207. break;
  208. case TERROR:
  209. return (-1);
  210. }
  211. tok = scan(&bufp);
  212. }
  213. lastcolmod = colmod;
  214. *np = NULL;
  215. mc = 0;
  216. if (star) {
  217. for (i = 0; i < msgCount; i++)
  218. if ((message[i].m_flag & MDELETED) == f) {
  219. mark(i+1);
  220. mc++;
  221. }
  222. if (mc == 0) {
  223. printf("No applicable messages.\n");
  224. return (-1);
  225. }
  226. return (0);
  227. }
  228. /*
  229. * If no numbers were given, mark all of the messages,
  230. * so that we can unmark any whose sender was not selected
  231. * if any user names were given.
  232. */
  233. if ((np > namelist || colmod != 0) && mc == 0)
  234. for (i = 1; i <= msgCount; i++)
  235. if ((message[i-1].m_flag & MDELETED) == f)
  236. mark(i);
  237. /*
  238. * If any names were given, go through and eliminate any
  239. * messages whose senders were not requested.
  240. */
  241. if (np > namelist) {
  242. for (i = 1; i <= msgCount; i++) {
  243. for (mc = 0, np = &namelist[0]; *np != NULL; np++)
  244. if (**np == '/') {
  245. if (matchfield(*np, i)) {
  246. mc++;
  247. break;
  248. }
  249. }
  250. else {
  251. if (matchsender(*np, i)) {
  252. mc++;
  253. break;
  254. }
  255. }
  256. if (mc == 0)
  257. unmark(i);
  258. }
  259. /*
  260. * Make sure we got some decent messages.
  261. */
  262. mc = 0;
  263. for (i = 1; i <= msgCount; i++)
  264. if (message[i-1].m_flag & MMARK) {
  265. mc++;
  266. break;
  267. }
  268. if (mc == 0) {
  269. printf("No applicable messages from {%s",
  270. namelist[0]);
  271. for (np = &namelist[1]; *np != NULL; np++)
  272. printf(", %s", *np);
  273. printf("}\n");
  274. return (-1);
  275. }
  276. }
  277. /*
  278. * If any colon modifiers were given, go through and
  279. * unmark any messages which do not satisfy the modifiers.
  280. */
  281. if (colmod != 0) {
  282. for (i = 1; i <= msgCount; i++) {
  283. struct coltab *colp;
  284. mp = &message[i - 1];
  285. for (colp = &coltab[0]; colp->co_char != '\0'; colp++)
  286. if (colp->co_bit & colmod)
  287. if ((mp->m_flag & colp->co_mask)
  288. != colp->co_equal)
  289. unmark(i);
  290. }
  291. for (mp = &message[0]; mp < &message[msgCount]; mp++)
  292. if (mp->m_flag & MMARK)
  293. break;
  294. if (mp >= &message[msgCount]) {
  295. struct coltab *colp;
  296. printf("No messages satisfy");
  297. for (colp = &coltab[0]; colp->co_char != '\0'; colp++)
  298. if (colp->co_bit & colmod)
  299. printf(" :%c", colp->co_char);
  300. printf("\n");
  301. return (-1);
  302. }
  303. }
  304. return (0);
  305. }
  306. /*
  307. * Turn the character after a colon modifier into a bit
  308. * value.
  309. */
  310. int
  311. evalcol(int col)
  312. {
  313. struct coltab *colp;
  314. if (col == 0)
  315. return (lastcolmod);
  316. for (colp = &coltab[0]; colp->co_char != '\0'; colp++)
  317. if (colp->co_char == col)
  318. return (colp->co_bit);
  319. return (0);
  320. }
  321. /*
  322. * Check the passed message number for legality and proper flags.
  323. * If f is MDELETED, then either kind will do. Otherwise, the message
  324. * has to be undeleted.
  325. */
  326. int
  327. check(int mesg, int f)
  328. {
  329. struct message *mp;
  330. if (mesg < 1 || mesg > msgCount) {
  331. printf("%d: Invalid message number\n", mesg);
  332. return (-1);
  333. }
  334. mp = &message[mesg-1];
  335. if (f != MDELETED && (mp->m_flag & MDELETED) != 0) {
  336. printf("%d: Inappropriate message\n", mesg);
  337. return (-1);
  338. }
  339. return (0);
  340. }
  341. /*
  342. * Scan out the list of string arguments, shell style
  343. * for a RAWLIST.
  344. */
  345. int
  346. getrawlist(char line[], char **argv, int argc)
  347. {
  348. char c, *cp, *cp2, quotec;
  349. int argn;
  350. char *linebuf;
  351. size_t linebufsize = BUFSIZ;
  352. if ((linebuf = malloc(linebufsize)) == NULL)
  353. err(1, "Out of memory");
  354. argn = 0;
  355. cp = line;
  356. for (;;) {
  357. for (; *cp == ' ' || *cp == '\t'; cp++)
  358. ;
  359. if (*cp == '\0')
  360. break;
  361. if (argn >= argc - 1) {
  362. printf(
  363. "Too many elements in the list; excess discarded.\n");
  364. break;
  365. }
  366. cp2 = linebuf;
  367. quotec = '\0';
  368. while ((c = *cp) != '\0') {
  369. /* Allocate more space if necessary */
  370. if (cp2 - linebuf == linebufsize - 1) {
  371. linebufsize += BUFSIZ;
  372. if ((linebuf = realloc(linebuf, linebufsize)) == NULL)
  373. err(1, "Out of memory");
  374. cp2 = linebuf + linebufsize - BUFSIZ - 1;
  375. }
  376. cp++;
  377. if (quotec != '\0') {
  378. if (c == quotec)
  379. quotec = '\0';
  380. else if (c == '\\')
  381. switch (c = *cp++) {
  382. case '\0':
  383. *cp2++ = '\\';
  384. cp--;
  385. break;
  386. case '0': case '1': case '2': case '3':
  387. case '4': case '5': case '6': case '7':
  388. c -= '0';
  389. if (*cp >= '0' && *cp <= '7')
  390. c = c * 8 + *cp++ - '0';
  391. if (*cp >= '0' && *cp <= '7')
  392. c = c * 8 + *cp++ - '0';
  393. *cp2++ = c;
  394. break;
  395. case 'b':
  396. *cp2++ = '\b';
  397. break;
  398. case 'f':
  399. *cp2++ = '\f';
  400. break;
  401. case 'n':
  402. *cp2++ = '\n';
  403. break;
  404. case 'r':
  405. *cp2++ = '\r';
  406. break;
  407. case 't':
  408. *cp2++ = '\t';
  409. break;
  410. case 'v':
  411. *cp2++ = '\v';
  412. break;
  413. default:
  414. *cp2++ = c;
  415. }
  416. else if (c == '^') {
  417. c = *cp++;
  418. if (c == '?')
  419. *cp2++ = '\177';
  420. /* null doesn't show up anyway */
  421. else if ((c >= 'A' && c <= '_') ||
  422. (c >= 'a' && c <= 'z'))
  423. *cp2++ = c & 037;
  424. else {
  425. *cp2++ = '^';
  426. cp--;
  427. }
  428. } else
  429. *cp2++ = c;
  430. } else if (c == '"' || c == '\'')
  431. quotec = c;
  432. else if (c == ' ' || c == '\t')
  433. break;
  434. else
  435. *cp2++ = c;
  436. }
  437. *cp2 = '\0';
  438. argv[argn++] = savestr(linebuf);
  439. }
  440. argv[argn] = NULL;
  441. (void)free(linebuf);
  442. return (argn);
  443. }
  444. /*
  445. * scan out a single lexical item and return its token number,
  446. * updating the string pointer passed **p. Also, store the value
  447. * of the number or string scanned in lexnumber or lexstring as
  448. * appropriate. In any event, store the scanned `thing' in lexstring.
  449. */
  450. static struct lex {
  451. char l_char;
  452. char l_token;
  453. } singles[] = {
  454. { '$', TDOLLAR },
  455. { '.', TDOT },
  456. { '^', TUP },
  457. { '*', TSTAR },
  458. { '-', TDASH },
  459. { '+', TPLUS },
  460. { '(', TOPEN },
  461. { ')', TCLOSE },
  462. { 0, 0 }
  463. };
  464. int
  465. scan(char **sp)
  466. {
  467. char *cp, *cp2;
  468. int c;
  469. struct lex *lp;
  470. int quotec;
  471. if (regretp >= 0) {
  472. strcpy(lexstring, string_stack[regretp]);
  473. lexnumber = numberstack[regretp];
  474. return (regretstack[regretp--]);
  475. }
  476. cp = *sp;
  477. cp2 = lexstring;
  478. c = *cp++;
  479. /*
  480. * strip away leading white space.
  481. */
  482. while (c == ' ' || c == '\t')
  483. c = *cp++;
  484. /*
  485. * If no characters remain, we are at end of line,
  486. * so report that.
  487. */
  488. if (c == '\0') {
  489. *sp = --cp;
  490. return (TEOL);
  491. }
  492. /*
  493. * If the leading character is a digit, scan
  494. * the number and convert it on the fly.
  495. * Return TNUMBER when done.
  496. */
  497. if (isdigit((unsigned char)c)) {
  498. lexnumber = 0;
  499. while (isdigit((unsigned char)c)) {
  500. lexnumber = lexnumber*10 + c - '0';
  501. *cp2++ = c;
  502. c = *cp++;
  503. }
  504. *cp2 = '\0';
  505. *sp = --cp;
  506. return (TNUMBER);
  507. }
  508. /*
  509. * Check for single character tokens; return such
  510. * if found.
  511. */
  512. for (lp = &singles[0]; lp->l_char != '\0'; lp++)
  513. if (c == lp->l_char) {
  514. lexstring[0] = c;
  515. lexstring[1] = '\0';
  516. *sp = cp;
  517. return (lp->l_token);
  518. }
  519. /*
  520. * We've got a string! Copy all the characters
  521. * of the string into lexstring, until we see
  522. * a null, space, or tab.
  523. * If the lead character is a " or ', save it
  524. * and scan until you get another.
  525. */
  526. quotec = 0;
  527. if (c == '\'' || c == '"') {
  528. quotec = c;
  529. c = *cp++;
  530. }
  531. while (c != '\0') {
  532. if (c == quotec) {
  533. cp++;
  534. break;
  535. }
  536. if (quotec == 0 && (c == ' ' || c == '\t'))
  537. break;
  538. if (cp2 - lexstring < STRINGLEN-1)
  539. *cp2++ = c;
  540. c = *cp++;
  541. }
  542. if (quotec && c == '\0') {
  543. fprintf(stderr, "Missing %c\n", quotec);
  544. return (TERROR);
  545. }
  546. *sp = --cp;
  547. *cp2 = '\0';
  548. return (TSTRING);
  549. }
  550. /*
  551. * Unscan the named token by pushing it onto the regret stack.
  552. */
  553. void
  554. regret(int token)
  555. {
  556. if (++regretp >= REGDEP)
  557. errx(1, "Too many regrets");
  558. regretstack[regretp] = token;
  559. lexstring[STRINGLEN-1] = '\0';
  560. string_stack[regretp] = savestr(lexstring);
  561. numberstack[regretp] = lexnumber;
  562. }
  563. /*
  564. * Reset all the scanner global variables.
  565. */
  566. void
  567. scaninit(void)
  568. {
  569. regretp = -1;
  570. }
  571. /*
  572. * Find the first message whose flags & m == f and return
  573. * its message number.
  574. */
  575. int
  576. first(int f, int m)
  577. {
  578. struct message *mp;
  579. if (msgCount == 0)
  580. return (0);
  581. f &= MDELETED;
  582. m &= MDELETED;
  583. for (mp = dot; mp < &message[msgCount]; mp++)
  584. if ((mp->m_flag & m) == f)
  585. return (mp - message + 1);
  586. for (mp = dot-1; mp >= &message[0]; mp--)
  587. if ((mp->m_flag & m) == f)
  588. return (mp - message + 1);
  589. return (0);
  590. }
  591. /*
  592. * See if the passed name sent the passed message number. Return true
  593. * if so.
  594. */
  595. int
  596. matchsender(char *str, int mesg)
  597. {
  598. char *cp;
  599. /* null string matches nothing instead of everything */
  600. if (*str == '\0')
  601. return (0);
  602. cp = nameof(&message[mesg - 1], 0);
  603. return (strcasestr(cp, str) != NULL);
  604. }
  605. /*
  606. * See if the passed name received the passed message number. Return true
  607. * if so.
  608. */
  609. static char *to_fields[] = { "to", "cc", "bcc", NULL };
  610. static int
  611. matchto(char *str, int mesg)
  612. {
  613. struct message *mp;
  614. char *cp, **to;
  615. str++;
  616. /* null string matches nothing instead of everything */
  617. if (*str == '\0')
  618. return (0);
  619. mp = &message[mesg - 1];
  620. for (to = to_fields; *to != NULL; to++) {
  621. cp = hfield(*to, mp);
  622. if (cp != NULL && strcasestr(cp, str) != NULL)
  623. return (1);
  624. }
  625. return (0);
  626. }
  627. /*
  628. * See if the given substring is contained within the specified field. If
  629. * 'searchheaders' is set, then the form '/x:y' will be accepted and matches
  630. * any message with the substring 'y' in field 'x'. If 'x' is omitted or
  631. * 'searchheaders' is not set, then the search matches any messages
  632. * with the substring 'y' in the 'Subject'. The search is case insensitive.
  633. *
  634. * The form '/to:y' is a special case, and will match all messages
  635. * containing the substring 'y' in the 'To', 'Cc', or 'Bcc' header
  636. * fields. The search for 'to' is case sensitive, so that '/To:y' can
  637. * be used to limit the search to just the 'To' field.
  638. */
  639. static char lastscan[STRINGLEN];
  640. int
  641. matchfield(char *str, int mesg)
  642. {
  643. struct message *mp;
  644. char *cp, *cp2;
  645. str++;
  646. if (*str == '\0')
  647. str = lastscan;
  648. else
  649. strlcpy(lastscan, str, sizeof(lastscan));
  650. mp = &message[mesg-1];
  651. /*
  652. * Now look, ignoring case, for the word in the string.
  653. */
  654. if (value("searchheaders") && (cp = strchr(str, ':')) != NULL) {
  655. /* Check for special case "/to:" */
  656. if (strncmp(str, "to:", 3) == 0)
  657. return (matchto(cp, mesg));
  658. *cp++ = '\0';
  659. cp2 = hfield(*str != '\0' ? str : "subject", mp);
  660. cp[-1] = ':';
  661. str = cp;
  662. cp = cp2;
  663. } else
  664. cp = hfield("subject", mp);
  665. if (cp == NULL)
  666. return (0);
  667. return (strcasestr(cp, str) != NULL);
  668. }
  669. /*
  670. * Mark the named message by setting its mark bit.
  671. */
  672. void
  673. mark(int mesg)
  674. {
  675. int i;
  676. i = mesg;
  677. if (i < 1 || i > msgCount)
  678. errx(1, "Bad message number to mark");
  679. message[i-1].m_flag |= MMARK;
  680. }
  681. /*
  682. * Unmark the named message.
  683. */
  684. void
  685. unmark(int mesg)
  686. {
  687. int i;
  688. i = mesg;
  689. if (i < 1 || i > msgCount)
  690. errx(1, "Bad message number to unmark");
  691. message[i-1].m_flag &= ~MMARK;
  692. }
  693. /*
  694. * Return the message number corresponding to the passed meta character.
  695. */
  696. int
  697. metamess(int meta, int f)
  698. {
  699. int c, m;
  700. struct message *mp;
  701. c = meta;
  702. switch (c) {
  703. case '^':
  704. /*
  705. * First 'good' message left.
  706. */
  707. for (mp = &message[0]; mp < &message[msgCount]; mp++)
  708. if ((mp->m_flag & MDELETED) == f)
  709. return (mp - &message[0] + 1);
  710. printf("No applicable messages\n");
  711. return (-1);
  712. case '$':
  713. /*
  714. * Last 'good message left.
  715. */
  716. for (mp = &message[msgCount-1]; mp >= &message[0]; mp--)
  717. if ((mp->m_flag & MDELETED) == f)
  718. return (mp - &message[0] + 1);
  719. printf("No applicable messages\n");
  720. return (-1);
  721. case '.':
  722. /*
  723. * Current message.
  724. */
  725. m = dot - &message[0] + 1;
  726. if ((dot->m_flag & MDELETED) != f) {
  727. printf("%d: Inappropriate message\n", m);
  728. return (-1);
  729. }
  730. return (m);
  731. default:
  732. printf("Unknown metachar (%c)\n", c);
  733. return (-1);
  734. }
  735. }