send.c: fix compiler warnings..
[s-mailx.git] / list.c
blobfab17281be5da885c7851945567fb8a949a56922
1 /*
2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 Steffen "Daode" Nurpmeso.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #ifndef lint
41 #ifdef DOSCCS
42 static char sccsid[] = "@(#)list.c 2.62 (gritter) 12/11/08";
43 #endif
44 #endif /* not lint */
46 #include "rcv.h"
47 #include <ctype.h>
48 #include "extern.h"
49 #ifdef HAVE_WCTYPE_H
50 #include <wctype.h>
51 #endif /* HAVE_WCTYPE_H */
54 * Mail -- a mail program
56 * Message list handling.
59 enum idfield {
60 ID_REFERENCES,
61 ID_IN_REPLY_TO
64 static char **add_to_namelist(char ***namelist, size_t *nmlsize,
65 char **np, char *string);
66 static int markall(char *buf, int f);
67 static int evalcol(int col);
68 static int check(int mesg, int f);
69 static int scan(char **sp);
70 static void regret(int token);
71 static void scaninit(void);
72 static int matchsender(char *str, int mesg, int allnet);
73 static int matchmid(char *id, enum idfield idfield, int mesg);
74 static int matchsubj(char *str, int mesg);
75 static void unmark(int mesg);
76 static int metamess(int meta, int f);
78 static size_t STRINGLEN;
80 static int lexnumber; /* Number of TNUMBER from scan() */
81 static char *lexstring; /* String from TSTRING, scan() */
82 static int regretp; /* Pointer to TOS of regret tokens */
83 static int regretstack[REGDEP]; /* Stack of regretted tokens */
84 static char *string_stack[REGDEP]; /* Stack of regretted strings */
85 static int numberstack[REGDEP]; /* Stack of regretted numbers */
86 static int threadflag; /* mark entire threads */
89 * Convert the user string of message numbers and
90 * store the numbers into vector.
92 * Returns the count of messages picked up or -1 on error.
94 int
95 getmsglist(char *buf, int *vector, int flags)
97 int *ip;
98 struct message *mp;
99 int mc;
101 if (msgCount == 0) {
102 *vector = 0;
103 return 0;
105 if (markall(buf, flags) < 0)
106 return(-1);
107 ip = vector;
108 if (inhook & 2) {
109 mc = 0;
110 for (mp = &message[0]; mp < &message[msgCount]; mp++)
111 if (mp->m_flag & MMARK) {
112 if ((mp->m_flag & MNEWEST) == 0)
113 unmark(mp - &message[0] + 1);
114 else
115 mc++;
117 if (mc == 0)
118 return -1;
120 if (mb.mb_threaded == 0) {
121 for (mp = &message[0]; mp < &message[msgCount]; mp++)
122 if (mp->m_flag & MMARK)
123 *ip++ = mp - &message[0] + 1;
124 } else {
125 for (mp = threadroot; mp; mp = next_in_thread(mp))
126 if (mp->m_flag & MMARK)
127 *ip++ = mp - &message[0] + 1;
129 *ip = 0;
130 return(ip - vector);
134 * Mark all messages that the user wanted from the command
135 * line in the message structure. Return 0 on success, -1
136 * on error.
140 * Bit values for colon modifiers.
143 #define CMNEW 01 /* New messages */
144 #define CMOLD 02 /* Old messages */
145 #define CMUNREAD 04 /* Unread messages */
146 #define CMDELETED 010 /* Deleted messages */
147 #define CMREAD 020 /* Read messages */
148 #define CMFLAG 040 /* Flagged messages */
149 #define CMANSWER 0100 /* Answered messages */
150 #define CMDRAFT 0200 /* Draft messages */
151 #define CMKILL 0400 /* Killed messages */
152 #define CMJUNK 01000 /* Junk messages */
155 * The following table describes the letters which can follow
156 * the colon and gives the corresponding modifier bit.
159 static struct coltab {
160 char co_char; /* What to find past : */
161 int co_bit; /* Associated modifier bit */
162 int co_mask; /* m_status bits to mask */
163 int co_equal; /* ... must equal this */
164 } coltab[] = {
165 { 'n', CMNEW, MNEW, MNEW },
166 { 'o', CMOLD, MNEW, 0 },
167 { 'u', CMUNREAD, MREAD, 0 },
168 { 'd', CMDELETED, MDELETED, MDELETED },
169 { 'r', CMREAD, MREAD, MREAD },
170 { 'f', CMFLAG, MFLAGGED, MFLAGGED },
171 { 'a', CMANSWER, MANSWERED, MANSWERED },
172 { 't', CMDRAFT, MDRAFTED, MDRAFTED },
173 { 'k', CMKILL, MKILL, MKILL },
174 { 'j', CMJUNK, MJUNK, MJUNK },
175 { 0, 0, 0, 0 }
178 static int lastcolmod;
180 static char **
181 add_to_namelist(char ***namelist, size_t *nmlsize, char **np, char *string)
183 size_t idx;
185 if ((idx = np - *namelist) >= *nmlsize) {
186 *namelist = srealloc(*namelist, (*nmlsize += 8) * sizeof *np);
187 np = &(*namelist)[idx];
189 *np++ = string;
190 return np;
193 #define markall_ret(i) { \
194 retval = i; \
195 ac_free(lexstring); \
196 goto out; \
199 static int
200 markall(char *buf, int f)
202 char **np, **nq;
203 int i, retval, gotheaders;
204 struct message *mp, *mx;
205 char **namelist, *bufp, *id = NULL, *cp;
206 int tok, beg, mc, star, other, valdot, colmod, colresult, topen, tback;
207 size_t nmlsize;
208 enum idfield idfield = ID_REFERENCES;
210 lexstring = ac_alloc(STRINGLEN = 2 * strlen(buf) + 1);
211 valdot = dot - &message[0] + 1;
212 colmod = 0;
213 for (i = 1; i <= msgCount; i++) {
214 message[i-1].m_flag &= ~MOLDMARK;
215 if (message[i-1].m_flag & MMARK)
216 message[i-1].m_flag |= MOLDMARK;
217 unmark(i);
219 bufp = buf;
220 mc = 0;
221 namelist = smalloc((nmlsize = 8) * sizeof *namelist);
222 np = &namelist[0];
223 scaninit();
224 tok = scan(&bufp);
225 star = 0;
226 other = 0;
227 beg = 0;
228 topen = 0;
229 tback = 0;
230 gotheaders = 0;
231 while (tok != TEOL) {
232 switch (tok) {
233 case TNUMBER:
234 number:
235 if (star) {
236 printf(catgets(catd, CATSET, 112,
237 "No numbers mixed with *\n"));
238 markall_ret(-1)
240 mc++;
241 other++;
242 if (beg != 0) {
243 if (check(lexnumber, f))
244 markall_ret(-1)
245 i = beg;
246 while (mb.mb_threaded ? 1 : i <= lexnumber) {
247 if (!(message[i-1].m_flag&MHIDDEN) &&
248 (f == MDELETED ||
249 (message[i-1].m_flag &
250 MDELETED) == 0))
251 mark(i, f);
252 if (mb.mb_threaded) {
253 if (i == lexnumber)
254 break;
255 mx = next_in_thread(&message[i-1]);
256 if (mx == NULL)
257 markall_ret(-1)
258 i = mx-message+1;
259 } else
260 i++;
262 beg = 0;
263 break;
265 beg = lexnumber;
266 if (check(beg, f))
267 markall_ret(-1)
268 tok = scan(&bufp);
269 regret(tok);
270 if (tok != TDASH) {
271 mark(beg, f);
272 beg = 0;
274 break;
276 case TPLUS:
277 if (beg != 0) {
278 printf(catgets(catd, CATSET, 113,
279 "Non-numeric second argument\n"));
280 markall_ret(-1)
282 i = valdot;
283 do {
284 if (mb.mb_threaded) {
285 mx = next_in_thread(&message[i-1]);
286 i = mx ? mx-message+1 : msgCount+1;
287 } else
288 i++;
289 if (i > msgCount) {
290 printf(catgets(catd, CATSET, 114,
291 "Referencing beyond EOF\n"));
292 markall_ret(-1)
294 } while (message[i-1].m_flag == MHIDDEN ||
295 (message[i-1].m_flag & MDELETED) !=
296 (unsigned)f ||
297 (message[i-1].m_flag & MKILL));
298 mark(i, f);
299 break;
301 case TDASH:
302 if (beg == 0) {
303 i = valdot;
304 do {
305 if (mb.mb_threaded) {
306 mx = prev_in_thread(
307 &message[i-1]);
308 i = mx ? mx-message+1 : 0;
309 } else
310 i--;
311 if (i <= 0) {
312 printf(catgets(catd, CATSET,
313 115,
314 "Referencing before 1\n"));
315 markall_ret(-1)
317 } while ((message[i-1].m_flag & MHIDDEN) ||
318 (message[i-1].m_flag & MDELETED)
319 != (unsigned)f ||
320 (message[i-1].m_flag & MKILL));
321 mark(i, f);
323 break;
325 case TSTRING:
326 if (beg != 0) {
327 printf(catgets(catd, CATSET, 116,
328 "Non-numeric second argument\n"));
329 markall_ret(-1)
331 other++;
332 if (lexstring[0] == ':') {
333 colresult = evalcol(lexstring[1]);
334 if (colresult == 0) {
335 printf(catgets(catd, CATSET, 117,
336 "Unknown colon modifier \"%s\"\n"),
337 lexstring);
338 markall_ret(-1)
340 colmod |= colresult;
342 else
343 np = add_to_namelist(&namelist, &nmlsize,
344 np, savestr(lexstring));
345 break;
347 case TOPEN:
348 if (imap_search(lexstring, f) == STOP)
349 markall_ret(-1)
350 topen++;
351 break;
353 case TDOLLAR:
354 case TUP:
355 case TDOT:
356 case TSEMI:
357 lexnumber = metamess(lexstring[0], f);
358 if (lexnumber == -1)
359 markall_ret(-1)
360 goto number;
362 case TBACK:
363 tback = 1;
364 for (i = 1; i <= msgCount; i++) {
365 if ((message[i-1].m_flag & MHIDDEN) ||
366 (message[i-1].m_flag&MDELETED)
367 != (unsigned)f)
368 continue;
369 if (message[i-1].m_flag&MOLDMARK)
370 mark(i, f);
372 break;
374 case TSTAR:
375 if (other) {
376 printf(catgets(catd, CATSET, 118,
377 "Can't mix \"*\" with anything\n"));
378 markall_ret(-1)
380 star++;
381 break;
383 case TCOMMA:
384 if (mb.mb_type == MB_IMAP && gotheaders++ == 0)
385 imap_getheaders(1, msgCount);
386 if (id == NULL && (cp = hfield("in-reply-to", dot))
387 != NULL) {
388 id = savestr(cp);
389 idfield = ID_IN_REPLY_TO;
391 if (id == NULL && (cp = hfield("references", dot))
392 != NULL) {
393 struct name *np;
394 if ((np = extract(cp, GREF)) != NULL) {
395 while (np->n_flink != NULL)
396 np = np->n_flink;
397 id = savestr(np->n_name);
398 idfield = ID_REFERENCES;
401 if (id == NULL) {
402 printf(catgets(catd, CATSET, 227,
403 "Cannot determine parent Message-ID of the current message\n"));
404 markall_ret(-1)
406 break;
408 case TERROR:
409 markall_ret(-1)
411 threadflag = 0;
412 tok = scan(&bufp);
414 lastcolmod = colmod;
415 np = add_to_namelist(&namelist, &nmlsize, np, NULL);
416 np--;
417 mc = 0;
418 if (star) {
419 for (i = 0; i < msgCount; i++) {
420 if (!(message[i].m_flag & MHIDDEN) &&
421 (message[i].m_flag & MDELETED) ==
422 (unsigned)f) {
423 mark(i+1, f);
424 mc++;
427 if (mc == 0) {
428 if (!inhook)
429 printf(catgets(catd, CATSET, 119,
430 "No applicable messages.\n"));
431 markall_ret(-1)
433 markall_ret(0)
436 if ((topen || tback) && mc == 0) {
437 for (i = 0; i < msgCount; i++)
438 if (message[i].m_flag & MMARK)
439 mc++;
440 if (mc == 0) {
441 if (!inhook)
442 printf(tback ?
443 "No previously marked messages.\n" :
444 "No messages satisfy (criteria).\n");
445 markall_ret(-1)
450 * If no numbers were given, mark all of the messages,
451 * so that we can unmark any whose sender was not selected
452 * if any user names were given.
455 if ((np > namelist || colmod != 0 || id) && mc == 0)
456 for (i = 1; i <= msgCount; i++) {
457 if (!(message[i-1].m_flag & MHIDDEN) &&
458 (message[i-1].m_flag & MDELETED) ==
459 (unsigned)f)
460 mark(i, f);
464 * If any names were given, go through and eliminate any
465 * messages whose senders were not requested.
468 if (np > namelist || id) {
469 int allnet = value("allnet") != NULL;
471 if (mb.mb_type == MB_IMAP && gotheaders++ == 0)
472 imap_getheaders(1, msgCount);
473 for (i = 1; i <= msgCount; i++) {
474 mc = 0;
475 if (np > namelist) {
476 for (nq = &namelist[0]; *nq != NULL; nq++) {
477 if (**nq == '/') {
478 if (matchsubj(*nq, i)) {
479 mc++;
480 break;
483 else {
484 if (matchsender(*nq, i,
485 allnet)) {
486 mc++;
487 break;
492 if (mc == 0 && id && matchmid(id, idfield, i))
493 mc++;
494 if (mc == 0)
495 unmark(i);
499 * Make sure we got some decent messages.
502 mc = 0;
503 for (i = 1; i <= msgCount; i++)
504 if (message[i-1].m_flag & MMARK) {
505 mc++;
506 break;
508 if (mc == 0) {
509 if (!inhook && np > namelist) {
510 printf(catgets(catd, CATSET, 120,
511 "No applicable messages from {%s"),
512 namelist[0]);
513 for (nq = &namelist[1]; *nq != NULL; nq++)
514 printf(catgets(catd, CATSET, 121,
515 ", %s"), *nq);
516 printf(catgets(catd, CATSET, 122, "}\n"));
517 } else if (id) {
518 printf(catgets(catd, CATSET, 227,
519 "Parent message not found\n"));
521 markall_ret(-1)
526 * If any colon modifiers were given, go through and
527 * unmark any messages which do not satisfy the modifiers.
530 if (colmod != 0) {
531 for (i = 1; i <= msgCount; i++) {
532 struct coltab *colp;
534 mp = &message[i - 1];
535 for (colp = &coltab[0]; colp->co_char; colp++)
536 if (colp->co_bit & colmod)
537 if ((mp->m_flag & colp->co_mask)
538 != (unsigned)colp->co_equal)
539 unmark(i);
542 for (mp = &message[0]; mp < &message[msgCount]; mp++)
543 if (mp->m_flag & MMARK)
544 break;
545 if (mp >= &message[msgCount]) {
546 struct coltab *colp;
548 if (!inhook) {
549 printf(catgets(catd, CATSET, 123,
550 "No messages satisfy"));
551 for (colp = &coltab[0]; colp->co_char; colp++)
552 if (colp->co_bit & colmod)
553 printf(" :%c", colp->co_char);
554 printf("\n");
556 markall_ret(-1)
559 markall_ret(0)
560 out:
561 free(namelist);
562 return retval;
566 * Turn the character after a colon modifier into a bit
567 * value.
569 static int
570 evalcol(int col)
572 struct coltab *colp;
574 if (col == 0)
575 return(lastcolmod);
576 for (colp = &coltab[0]; colp->co_char; colp++)
577 if (colp->co_char == col)
578 return(colp->co_bit);
579 return(0);
583 * Check the passed message number for legality and proper flags.
584 * If f is MDELETED, then either kind will do. Otherwise, the message
585 * has to be undeleted.
587 static int
588 check(int mesg, int f)
590 struct message *mp;
592 if (mesg < 1 || mesg > msgCount) {
593 printf(catgets(catd, CATSET, 124,
594 "%d: Invalid message number\n"), mesg);
595 return(-1);
597 mp = &message[mesg-1];
598 if (mp->m_flag & MHIDDEN || (f != MDELETED &&
599 (mp->m_flag & MDELETED) != 0)) {
600 printf(catgets(catd, CATSET, 125,
601 "%d: Inappropriate message\n"), mesg);
602 return(-1);
604 return(0);
608 * Scan out the list of string arguments, shell style
609 * for a RAWLIST.
612 getrawlist(const char *line, size_t linesize, char **argv, int argc,
613 int echolist)
615 char c, *cp2, quotec;
616 const char *cp;
617 int argn;
618 char *linebuf;
620 argn = 0;
621 cp = line;
622 linebuf = ac_alloc(linesize + 1);
623 for (;;) {
624 for (; blankchar(*cp & 0377); cp++);
625 if (*cp == '\0')
626 break;
627 if (argn >= argc - 1) {
628 printf(catgets(catd, CATSET, 126,
629 "Too many elements in the list; excess discarded.\n"));
630 break;
632 cp2 = linebuf;
633 quotec = '\0';
634 while ((c = *cp) != '\0') {
635 cp++;
636 if (quotec != '\0') {
637 if (c == quotec) {
638 quotec = '\0';
639 if (echolist)
640 *cp2++ = c;
641 } else if (c == '\\')
642 switch (c = *cp++) {
643 case '\0':
644 *cp2++ = '\\';
645 cp--;
646 break;
648 case '0': case '1': case '2': case '3':
649 case '4': case '5': case '6': case '7':
650 c -= '0';
651 if (*cp >= '0' && *cp <= '7')
652 c = c * 8 + *cp++ - '0';
653 if (*cp >= '0' && *cp <= '7')
654 c = c * 8 + *cp++ - '0';
655 *cp2++ = c;
656 break;
657 case 'b':
658 *cp2++ = '\b';
659 break;
660 case 'f':
661 *cp2++ = '\f';
662 break;
663 case 'n':
664 *cp2++ = '\n';
665 break;
666 case 'r':
667 *cp2++ = '\r';
668 break;
669 case 't':
670 *cp2++ = '\t';
671 break;
672 case 'v':
673 *cp2++ = '\v';
674 break;
676 default:
677 if (cp[-1]!=quotec || echolist)
678 *cp2++ = '\\';
679 *cp2++ = c;
681 /*else if (c == '^') {
682 c = *cp++;
683 if (c == '?')
684 *cp2++ = '\177';
685 /\* null doesn't show up anyway *\/
686 else if ((c >= 'A' && c <= '_') ||
687 (c >= 'a' && c <= 'z'))
688 *cp2++ = c & 037;
689 else {
690 *cp2++ = '^';
691 cp--;
693 }*/ else
694 *cp2++ = c;
695 } else if (c == '"' || c == '\'') {
696 if (echolist)
697 *cp2++ = c;
698 quotec = c;
699 } else if (c == '\\' && !echolist) {
700 if (*cp)
701 *cp2++ = *cp++;
702 else
703 *cp2++ = c;
704 } else if (blankchar(c & 0377))
705 break;
706 else
707 *cp2++ = c;
709 *cp2 = '\0';
710 argv[argn++] = savestr(linebuf);
712 argv[argn] = NULL;
713 ac_free(linebuf);
714 return argn;
718 * scan out a single lexical item and return its token number,
719 * updating the string pointer passed **p. Also, store the value
720 * of the number or string scanned in lexnumber or lexstring as
721 * appropriate. In any event, store the scanned `thing' in lexstring.
724 static struct lex {
725 char l_char;
726 enum ltoken l_token;
727 } singles[] = {
728 { '$', TDOLLAR },
729 { '.', TDOT },
730 { '^', TUP },
731 { '*', TSTAR },
732 { '-', TDASH },
733 { '+', TPLUS },
734 { '(', TOPEN },
735 { ')', TCLOSE },
736 { ',', TCOMMA },
737 { ';', TSEMI },
738 { '`', TBACK },
739 { 0, 0 }
742 static int
743 scan(char **sp)
745 char *cp, *cp2;
746 int c, level, inquote;
747 struct lex *lp;
748 int quotec;
750 if (regretp >= 0) {
751 strncpy(lexstring, string_stack[regretp], STRINGLEN);
752 lexstring[STRINGLEN-1]='\0';
753 lexnumber = numberstack[regretp];
754 return(regretstack[regretp--]);
756 cp = *sp;
757 cp2 = lexstring;
758 c = *cp++;
761 * strip away leading white space.
764 while (blankchar(c))
765 c = *cp++;
768 * If no characters remain, we are at end of line,
769 * so report that.
772 if (c == '\0') {
773 *sp = --cp;
774 return(TEOL);
778 * Select members of a message thread.
780 if (c == '&') {
781 threadflag = 1;
782 if (*cp == '\0' || spacechar(*cp&0377)) {
783 lexstring[0] = '.';
784 lexstring[1] = '\0';
785 *sp = cp;
786 return TDOT;
788 c = *cp++;
792 * If the leading character is a digit, scan
793 * the number and convert it on the fly.
794 * Return TNUMBER when done.
797 if (digitchar(c)) {
798 lexnumber = 0;
799 while (digitchar(c)) {
800 lexnumber = lexnumber*10 + c - '0';
801 *cp2++ = c;
802 c = *cp++;
804 *cp2 = '\0';
805 *sp = --cp;
806 return(TNUMBER);
810 * An IMAP SEARCH list. Note that TOPEN has always been included
811 * in singles[] in Mail and mailx. Thus although there is no formal
812 * definition for (LIST) lists, they do not collide with historical
813 * practice because a subject string (LIST) could never been matched
814 * this way.
817 if (c == '(') {
818 level = 1;
819 inquote = 0;
820 *cp2++ = c;
821 do {
822 if ((c = *cp++&0377) == '\0') {
823 mtop: fprintf(stderr, "Missing \")\".\n");
824 return TERROR;
826 if (inquote && c == '\\') {
827 *cp2++ = c;
828 c = *cp++&0377;
829 if (c == '\0')
830 goto mtop;
831 } else if (c == '"')
832 inquote = !inquote;
833 else if (inquote)
834 /*EMPTY*/;
835 else if (c == '(')
836 level++;
837 else if (c == ')')
838 level--;
839 else if (spacechar(c)) {
841 * Replace unquoted whitespace by single
842 * space characters, to make the string
843 * IMAP SEARCH conformant.
845 c = ' ';
846 if (cp2[-1] == ' ')
847 cp2--;
849 *cp2++ = c;
850 } while (c != ')' || level > 0);
851 *cp2 = '\0';
852 *sp = cp;
853 return TOPEN;
857 * Check for single character tokens; return such
858 * if found.
861 for (lp = &singles[0]; lp->l_char != 0; lp++)
862 if (c == lp->l_char) {
863 lexstring[0] = c;
864 lexstring[1] = '\0';
865 *sp = cp;
866 return(lp->l_token);
870 * We've got a string! Copy all the characters
871 * of the string into lexstring, until we see
872 * a null, space, or tab.
873 * If the lead character is a " or ', save it
874 * and scan until you get another.
877 quotec = 0;
878 if (c == '\'' || c == '"') {
879 quotec = c;
880 c = *cp++;
882 while (c != '\0') {
883 if (quotec == 0 && c == '\\' && *cp)
884 c = *cp++;
885 if (c == quotec) {
886 cp++;
887 break;
889 if (quotec == 0 && blankchar(c))
890 break;
891 if ((size_t)(cp2 - lexstring) < (size_t)STRINGLEN - 1)
892 *cp2++ = c;
893 c = *cp++;
895 if (quotec && c == 0) {
896 fprintf(stderr, catgets(catd, CATSET, 127,
897 "Missing %c\n"), quotec);
898 return TERROR;
900 *sp = --cp;
901 *cp2 = '\0';
902 return(TSTRING);
906 * Unscan the named token by pushing it onto the regret stack.
908 static void
909 regret(int token)
911 if (++regretp >= REGDEP)
912 panic(catgets(catd, CATSET, 128, "Too many regrets"));
913 regretstack[regretp] = token;
914 lexstring[STRINGLEN-1] = '\0';
915 string_stack[regretp] = savestr(lexstring);
916 numberstack[regretp] = lexnumber;
920 * Reset all the scanner global variables.
922 static void
923 scaninit(void)
925 regretp = -1;
926 threadflag = 0;
930 * Find the first message whose flags & m == f and return
931 * its message number.
933 int
934 first(int f, int m)
936 struct message *mp;
938 if (msgCount == 0)
939 return 0;
940 f &= MDELETED;
941 m &= MDELETED;
942 for (mp = dot; mb.mb_threaded ? mp != NULL : mp < &message[msgCount];
943 mb.mb_threaded ? mp = next_in_thread(mp) : mp++) {
944 if (!(mp->m_flag & MHIDDEN) && (mp->m_flag & m) == (unsigned)f)
945 return mp - message + 1;
947 if (dot > &message[0]) {
948 for (mp = dot-1; mb.mb_threaded ?
949 mp != NULL : mp >= &message[0];
950 mb.mb_threaded ?
951 mp = prev_in_thread(mp) : mp--) {
952 if (! (mp->m_flag & MHIDDEN) &&
953 (mp->m_flag & m) == (unsigned)f)
954 return mp - message + 1;
957 return 0;
961 * See if the passed name sent the passed message number. Return true
962 * if so.
964 static int
965 matchsender(char *str, int mesg, int allnet)
967 if (allnet) {
968 char *cp = nameof(&message[mesg - 1], 0);
970 do {
971 if ((*cp == '@' || *cp == '\0') &&
972 (*str == '@' || *str == '\0'))
973 return 1;
974 if (*cp != *str)
975 break;
976 } while (cp++, *str++ != '\0');
977 return 0;
979 return !strcmp(str, (value("showname") ? realname : skin)
980 (name1(&message[mesg - 1], 0)));
983 static int
984 matchmid(char *id, enum idfield idfield, int mesg)
986 struct name *np;
987 char *cp;
989 if ((cp = hfield("message-id", &message[mesg - 1])) != NULL) {
990 switch (idfield) {
991 case ID_REFERENCES:
992 return msgidcmp(id, cp) == 0;
993 case ID_IN_REPLY_TO:
994 if ((np = extract(id, GREF)) != NULL)
995 do {
996 if (msgidcmp(np->n_name, cp) == 0)
997 return 1;
998 } while ((np = np->n_flink) != NULL);
999 break;
1002 return 0;
1006 * See if the given string matches inside the subject field of the
1007 * given message. For the purpose of the scan, we ignore case differences.
1008 * If it does, return true. The string search argument is assumed to
1009 * have the form "/search-string." If it is of the form "/," we use the
1010 * previous search string.
1013 static char lastscan[128];
1015 static int
1016 matchsubj(char *str, int mesg)
1018 struct message *mp;
1019 char *cp, *cp2;
1020 struct str in, out;
1021 int i;
1023 str++;
1024 if (strlen(str) == 0) {
1025 str = lastscan;
1026 } else {
1027 strncpy(lastscan, str, sizeof lastscan);
1028 lastscan[sizeof lastscan - 1]='\0';
1030 mp = &message[mesg-1];
1033 * Now look, ignoring case, for the word in the string.
1036 if (value("searchheaders") && (cp = strchr(str, ':'))) {
1037 *cp++ = '\0';
1038 cp2 = hfield(str, mp);
1039 cp[-1] = ':';
1040 str = cp;
1041 } else {
1042 cp = str;
1043 cp2 = hfield("subject", mp);
1045 if (cp2 == NULL)
1046 return(0);
1047 in.s = cp2;
1048 in.l = strlen(cp2);
1049 mime_fromhdr(&in, &out, TD_ICONV);
1050 i = substr(out.s, cp);
1051 free(out.s);
1052 return i;
1056 * Mark the named message by setting its mark bit.
1058 void
1059 mark(int mesg, int f)
1061 struct message *mp;
1062 int i;
1064 i = mesg;
1065 if (i < 1 || i > msgCount)
1066 panic(catgets(catd, CATSET, 129, "Bad message number to mark"));
1067 if (mb.mb_threaded == 1 && threadflag) {
1068 if ((message[i-1].m_flag & MHIDDEN) == 0) {
1069 if (f == MDELETED ||
1070 (message[i-1].m_flag&MDELETED) == 0)
1071 message[i-1].m_flag |= MMARK;
1073 if (message[i-1].m_child) {
1074 mp = message[i-1].m_child;
1075 mark(mp-message+1, f);
1076 for (mp = mp->m_younger; mp; mp = mp->m_younger)
1077 mark(mp-message+1, f);
1079 } else
1080 message[i-1].m_flag |= MMARK;
1084 * Unmark the named message.
1086 static void
1087 unmark(int mesg)
1089 int i;
1091 i = mesg;
1092 if (i < 1 || i > msgCount)
1093 panic(catgets(catd, CATSET, 130,
1094 "Bad message number to unmark"));
1095 message[i-1].m_flag &= ~MMARK;
1099 * Return the message number corresponding to the passed meta character.
1101 static int
1102 metamess(int meta, int f)
1104 int c, m;
1105 struct message *mp;
1107 c = meta;
1108 switch (c) {
1109 case '^':
1111 * First 'good' message left.
1113 mp = mb.mb_threaded ? threadroot : &message[0];
1114 while (mp < &message[msgCount]) {
1115 if (!(mp->m_flag & (MHIDDEN|MKILL)) &&
1116 (mp->m_flag & MDELETED) == (unsigned)f)
1117 return(mp - &message[0] + 1);
1118 if (mb.mb_threaded) {
1119 mp = next_in_thread(mp);
1120 if (mp == NULL)
1121 break;
1122 } else
1123 mp++;
1125 if (!inhook)
1126 printf(catgets(catd, CATSET, 131,
1127 "No applicable messages\n"));
1128 return(-1);
1130 case '$':
1132 * Last 'good message left.
1134 mp = mb.mb_threaded ? this_in_thread(threadroot, -1) :
1135 &message[msgCount-1];
1136 while (mp >= &message[0]) {
1137 if (!(mp->m_flag & (MHIDDEN|MKILL)) &&
1138 (mp->m_flag & MDELETED) == (unsigned)f)
1139 return(mp - &message[0] + 1);
1140 if (mb.mb_threaded) {
1141 mp = prev_in_thread(mp);
1142 if (mp == NULL)
1143 break;
1144 } else
1145 mp--;
1147 if (!inhook)
1148 printf(catgets(catd, CATSET, 132,
1149 "No applicable messages\n"));
1150 return(-1);
1152 case '.':
1154 * Current message.
1156 m = dot - &message[0] + 1;
1157 if ((dot->m_flag & MHIDDEN) ||
1158 (dot->m_flag & MDELETED) != (unsigned)f) {
1159 printf(catgets(catd, CATSET, 133,
1160 "%d: Inappropriate message\n"), m);
1161 return(-1);
1163 return(m);
1165 case ';':
1167 * Previously current message.
1169 if (prevdot == NULL) {
1170 printf(catgets(catd, CATSET, 228,
1171 "No previously current message\n"));
1172 return(-1);
1174 m = prevdot - &message[0] + 1;
1175 if ((prevdot->m_flag & MHIDDEN) ||
1176 (prevdot->m_flag & MDELETED) != (unsigned)f) {
1177 printf(catgets(catd, CATSET, 133,
1178 "%d: Inappropriate message\n"), m);
1179 return(-1);
1181 return(m);
1183 default:
1184 printf(catgets(catd, CATSET, 134,
1185 "Unknown metachar (%c)\n"), c);
1186 return(-1);