base64.c: fix compiler warnings
[s-mailx.git] / list.c
blobd662b146822c9ad99cef6e611de61c97e03f35f6
1 /*
2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 */
6 /*
7 * Copyright (c) 1980, 1993
8 * The Regents of the University of California. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
39 #ifndef lint
40 #ifdef DOSCCS
41 static char sccsid[] = "@(#)list.c 2.62 (gritter) 12/11/08";
42 #endif
43 #endif /* not lint */
45 #include "rcv.h"
46 #include <ctype.h>
47 #include "extern.h"
48 #ifdef HAVE_WCTYPE_H
49 #include <wctype.h>
50 #endif /* HAVE_WCTYPE_H */
53 * Mail -- a mail program
55 * Message list handling.
58 enum idfield {
59 ID_REFERENCES,
60 ID_IN_REPLY_TO
63 static char **add_to_namelist(char ***namelist, size_t *nmlsize,
64 char **np, char *string);
65 static int markall(char *buf, int f);
66 static int evalcol(int col);
67 static int check(int mesg, int f);
68 static int scan(char **sp);
69 static void regret(int token);
70 static void scaninit(void);
71 static int matchsender(char *str, int mesg, int allnet);
72 static int matchmid(char *id, enum idfield idfield, int mesg);
73 static int matchsubj(char *str, int mesg);
74 static void unmark(int mesg);
75 static int metamess(int meta, int f);
77 static size_t STRINGLEN;
79 static int lexnumber; /* Number of TNUMBER from scan() */
80 static char *lexstring; /* String from TSTRING, scan() */
81 static int regretp; /* Pointer to TOS of regret tokens */
82 static int regretstack[REGDEP]; /* Stack of regretted tokens */
83 static char *string_stack[REGDEP]; /* Stack of regretted strings */
84 static int numberstack[REGDEP]; /* Stack of regretted numbers */
85 static int threadflag; /* mark entire threads */
88 * Convert the user string of message numbers and
89 * store the numbers into vector.
91 * Returns the count of messages picked up or -1 on error.
93 int
94 getmsglist(char *buf, int *vector, int flags)
96 int *ip;
97 struct message *mp;
98 int mc;
100 if (msgCount == 0) {
101 *vector = 0;
102 return 0;
104 if (markall(buf, flags) < 0)
105 return(-1);
106 ip = vector;
107 if (inhook & 2) {
108 mc = 0;
109 for (mp = &message[0]; mp < &message[msgCount]; mp++)
110 if (mp->m_flag & MMARK) {
111 if ((mp->m_flag & MNEWEST) == 0)
112 unmark(mp - &message[0] + 1);
113 else
114 mc++;
116 if (mc == 0)
117 return -1;
119 if (mb.mb_threaded == 0) {
120 for (mp = &message[0]; mp < &message[msgCount]; mp++)
121 if (mp->m_flag & MMARK)
122 *ip++ = mp - &message[0] + 1;
123 } else {
124 for (mp = threadroot; mp; mp = next_in_thread(mp))
125 if (mp->m_flag & MMARK)
126 *ip++ = mp - &message[0] + 1;
128 *ip = 0;
129 return(ip - vector);
133 * Mark all messages that the user wanted from the command
134 * line in the message structure. Return 0 on success, -1
135 * on error.
139 * Bit values for colon modifiers.
142 #define CMNEW 01 /* New messages */
143 #define CMOLD 02 /* Old messages */
144 #define CMUNREAD 04 /* Unread messages */
145 #define CMDELETED 010 /* Deleted messages */
146 #define CMREAD 020 /* Read messages */
147 #define CMFLAG 040 /* Flagged messages */
148 #define CMANSWER 0100 /* Answered messages */
149 #define CMDRAFT 0200 /* Draft messages */
150 #define CMKILL 0400 /* Killed messages */
151 #define CMJUNK 01000 /* Junk messages */
154 * The following table describes the letters which can follow
155 * the colon and gives the corresponding modifier bit.
158 static struct coltab {
159 char co_char; /* What to find past : */
160 int co_bit; /* Associated modifier bit */
161 int co_mask; /* m_status bits to mask */
162 int co_equal; /* ... must equal this */
163 } coltab[] = {
164 { 'n', CMNEW, MNEW, MNEW },
165 { 'o', CMOLD, MNEW, 0 },
166 { 'u', CMUNREAD, MREAD, 0 },
167 { 'd', CMDELETED, MDELETED, MDELETED },
168 { 'r', CMREAD, MREAD, MREAD },
169 { 'f', CMFLAG, MFLAGGED, MFLAGGED },
170 { 'a', CMANSWER, MANSWERED, MANSWERED },
171 { 't', CMDRAFT, MDRAFTED, MDRAFTED },
172 { 'k', CMKILL, MKILL, MKILL },
173 { 'j', CMJUNK, MJUNK, MJUNK },
174 { 0, 0, 0, 0 }
177 static int lastcolmod;
179 static char **
180 add_to_namelist(char ***namelist, size_t *nmlsize, char **np, char *string)
182 size_t idx;
184 if ((idx = np - *namelist) >= *nmlsize) {
185 *namelist = srealloc(*namelist, (*nmlsize += 8) * sizeof *np);
186 np = &(*namelist)[idx];
188 *np++ = string;
189 return np;
192 #define markall_ret(i) { \
193 retval = i; \
194 ac_free(lexstring); \
195 goto out; \
198 static int
199 markall(char *buf, int f)
201 char **np, **nq;
202 int i, retval, gotheaders;
203 struct message *mp, *mx;
204 char **namelist, *bufp, *id = NULL, *cp;
205 int tok, beg, mc, star, other, valdot, colmod, colresult, topen, tback;
206 size_t nmlsize;
207 enum idfield idfield = ID_REFERENCES;
209 lexstring = ac_alloc(STRINGLEN = 2 * strlen(buf) + 1);
210 valdot = dot - &message[0] + 1;
211 colmod = 0;
212 for (i = 1; i <= msgCount; i++) {
213 message[i-1].m_flag &= ~MOLDMARK;
214 if (message[i-1].m_flag & MMARK)
215 message[i-1].m_flag |= MOLDMARK;
216 unmark(i);
218 bufp = buf;
219 mc = 0;
220 namelist = smalloc((nmlsize = 8) * sizeof *namelist);
221 np = &namelist[0];
222 scaninit();
223 tok = scan(&bufp);
224 star = 0;
225 other = 0;
226 beg = 0;
227 topen = 0;
228 tback = 0;
229 gotheaders = 0;
230 while (tok != TEOL) {
231 switch (tok) {
232 case TNUMBER:
233 number:
234 if (star) {
235 printf(catgets(catd, CATSET, 112,
236 "No numbers mixed with *\n"));
237 markall_ret(-1)
239 mc++;
240 other++;
241 if (beg != 0) {
242 if (check(lexnumber, f))
243 markall_ret(-1)
244 i = beg;
245 while (mb.mb_threaded ? 1 : i <= lexnumber) {
246 if (!(message[i-1].m_flag&MHIDDEN) &&
247 (f == MDELETED ||
248 (message[i-1].m_flag &
249 MDELETED) == 0))
250 mark(i, f);
251 if (mb.mb_threaded) {
252 if (i == lexnumber)
253 break;
254 mx = next_in_thread(&message[i-1]);
255 if (mx == NULL)
256 markall_ret(-1)
257 i = mx-message+1;
258 } else
259 i++;
261 beg = 0;
262 break;
264 beg = lexnumber;
265 if (check(beg, f))
266 markall_ret(-1)
267 tok = scan(&bufp);
268 regret(tok);
269 if (tok != TDASH) {
270 mark(beg, f);
271 beg = 0;
273 break;
275 case TPLUS:
276 if (beg != 0) {
277 printf(catgets(catd, CATSET, 113,
278 "Non-numeric second argument\n"));
279 markall_ret(-1)
281 i = valdot;
282 do {
283 if (mb.mb_threaded) {
284 mx = next_in_thread(&message[i-1]);
285 i = mx ? mx-message+1 : msgCount+1;
286 } else
287 i++;
288 if (i > msgCount) {
289 printf(catgets(catd, CATSET, 114,
290 "Referencing beyond EOF\n"));
291 markall_ret(-1)
293 } while (message[i-1].m_flag == MHIDDEN ||
294 (message[i-1].m_flag & MDELETED) != f ||
295 message[i-1].m_flag & MKILL);
296 mark(i, f);
297 break;
299 case TDASH:
300 if (beg == 0) {
301 i = valdot;
302 do {
303 if (mb.mb_threaded) {
304 mx = prev_in_thread(
305 &message[i-1]);
306 i = mx ? mx-message+1 : 0;
307 } else
308 i--;
309 if (i <= 0) {
310 printf(catgets(catd, CATSET,
311 115,
312 "Referencing before 1\n"));
313 markall_ret(-1)
315 } while (message[i-1].m_flag & MHIDDEN ||
316 (message[i-1].m_flag & MDELETED)
317 != f ||
318 message[i-1].m_flag & MKILL);
319 mark(i, f);
321 break;
323 case TSTRING:
324 if (beg != 0) {
325 printf(catgets(catd, CATSET, 116,
326 "Non-numeric second argument\n"));
327 markall_ret(-1)
329 other++;
330 if (lexstring[0] == ':') {
331 colresult = evalcol(lexstring[1]);
332 if (colresult == 0) {
333 printf(catgets(catd, CATSET, 117,
334 "Unknown colon modifier \"%s\"\n"),
335 lexstring);
336 markall_ret(-1)
338 colmod |= colresult;
340 else
341 np = add_to_namelist(&namelist, &nmlsize,
342 np, savestr(lexstring));
343 break;
345 case TOPEN:
346 if (imap_search(lexstring, f) == STOP)
347 markall_ret(-1)
348 topen++;
349 break;
351 case TDOLLAR:
352 case TUP:
353 case TDOT:
354 case TSEMI:
355 lexnumber = metamess(lexstring[0], f);
356 if (lexnumber == -1)
357 markall_ret(-1)
358 goto number;
360 case TBACK:
361 tback = 1;
362 for (i = 1; i <= msgCount; i++) {
363 if (message[i-1].m_flag&MHIDDEN ||
364 (message[i-1].m_flag&MDELETED)
365 != f)
366 continue;
367 if (message[i-1].m_flag&MOLDMARK)
368 mark(i, f);
370 break;
372 case TSTAR:
373 if (other) {
374 printf(catgets(catd, CATSET, 118,
375 "Can't mix \"*\" with anything\n"));
376 markall_ret(-1)
378 star++;
379 break;
381 case TCOMMA:
382 if (mb.mb_type == MB_IMAP && gotheaders++ == 0)
383 imap_getheaders(1, msgCount);
384 if (id == NULL && (cp = hfield("in-reply-to", dot))
385 != NULL) {
386 id = savestr(cp);
387 idfield = ID_IN_REPLY_TO;
389 if (id == NULL && (cp = hfield("references", dot))
390 != NULL) {
391 struct name *np;
392 if ((np = extract(cp, GREF)) != NULL) {
393 while (np->n_flink != NULL)
394 np = np->n_flink;
395 id = savestr(np->n_name);
396 idfield = ID_REFERENCES;
399 if (id == NULL) {
400 printf(catgets(catd, CATSET, 227,
401 "Cannot determine parent Message-ID of the current message\n"));
402 markall_ret(-1)
404 break;
406 case TERROR:
407 markall_ret(-1)
409 threadflag = 0;
410 tok = scan(&bufp);
412 lastcolmod = colmod;
413 np = add_to_namelist(&namelist, &nmlsize, np, NULL);
414 np--;
415 mc = 0;
416 if (star) {
417 for (i = 0; i < msgCount; i++) {
418 if (!(message[i].m_flag & MHIDDEN) &&
419 (message[i].m_flag & MDELETED) == f) {
420 mark(i+1, f);
421 mc++;
424 if (mc == 0) {
425 if (!inhook)
426 printf(catgets(catd, CATSET, 119,
427 "No applicable messages.\n"));
428 markall_ret(-1)
430 markall_ret(0)
433 if ((topen || tback) && mc == 0) {
434 for (i = 0; i < msgCount; i++)
435 if (message[i].m_flag & MMARK)
436 mc++;
437 if (mc == 0) {
438 if (!inhook)
439 printf(tback ?
440 "No previously marked messages.\n" :
441 "No messages satisfy (criteria).\n");
442 markall_ret(-1)
447 * If no numbers were given, mark all of the messages,
448 * so that we can unmark any whose sender was not selected
449 * if any user names were given.
452 if ((np > namelist || colmod != 0 || id) && mc == 0)
453 for (i = 1; i <= msgCount; i++) {
454 if (!(message[i-1].m_flag & MHIDDEN) &&
455 (message[i-1].m_flag & MDELETED) == f)
456 mark(i, f);
460 * If any names were given, go through and eliminate any
461 * messages whose senders were not requested.
464 if (np > namelist || id) {
465 int allnet = value("allnet") != NULL;
467 if (mb.mb_type == MB_IMAP && gotheaders++ == 0)
468 imap_getheaders(1, msgCount);
469 for (i = 1; i <= msgCount; i++) {
470 mc = 0;
471 if (np > namelist) {
472 for (nq = &namelist[0]; *nq != NULL; nq++) {
473 if (**nq == '/') {
474 if (matchsubj(*nq, i)) {
475 mc++;
476 break;
479 else {
480 if (matchsender(*nq, i,
481 allnet)) {
482 mc++;
483 break;
488 if (mc == 0 && id && matchmid(id, idfield, i))
489 mc++;
490 if (mc == 0)
491 unmark(i);
495 * Make sure we got some decent messages.
498 mc = 0;
499 for (i = 1; i <= msgCount; i++)
500 if (message[i-1].m_flag & MMARK) {
501 mc++;
502 break;
504 if (mc == 0) {
505 if (!inhook && np > namelist) {
506 printf(catgets(catd, CATSET, 120,
507 "No applicable messages from {%s"),
508 namelist[0]);
509 for (nq = &namelist[1]; *nq != NULL; nq++)
510 printf(catgets(catd, CATSET, 121,
511 ", %s"), *nq);
512 printf(catgets(catd, CATSET, 122, "}\n"));
513 } else if (id) {
514 printf(catgets(catd, CATSET, 227,
515 "Parent message not found\n"));
517 markall_ret(-1)
522 * If any colon modifiers were given, go through and
523 * unmark any messages which do not satisfy the modifiers.
526 if (colmod != 0) {
527 for (i = 1; i <= msgCount; i++) {
528 struct coltab *colp;
530 mp = &message[i - 1];
531 for (colp = &coltab[0]; colp->co_char; colp++)
532 if (colp->co_bit & colmod)
533 if ((mp->m_flag & colp->co_mask)
534 != colp->co_equal)
535 unmark(i);
538 for (mp = &message[0]; mp < &message[msgCount]; mp++)
539 if (mp->m_flag & MMARK)
540 break;
541 if (mp >= &message[msgCount]) {
542 struct coltab *colp;
544 if (!inhook) {
545 printf(catgets(catd, CATSET, 123,
546 "No messages satisfy"));
547 for (colp = &coltab[0]; colp->co_char; colp++)
548 if (colp->co_bit & colmod)
549 printf(" :%c", colp->co_char);
550 printf("\n");
552 markall_ret(-1)
555 markall_ret(0)
556 out:
557 free(namelist);
558 return retval;
562 * Turn the character after a colon modifier into a bit
563 * value.
565 static int
566 evalcol(int col)
568 struct coltab *colp;
570 if (col == 0)
571 return(lastcolmod);
572 for (colp = &coltab[0]; colp->co_char; colp++)
573 if (colp->co_char == col)
574 return(colp->co_bit);
575 return(0);
579 * Check the passed message number for legality and proper flags.
580 * If f is MDELETED, then either kind will do. Otherwise, the message
581 * has to be undeleted.
583 static int
584 check(int mesg, int f)
586 struct message *mp;
588 if (mesg < 1 || mesg > msgCount) {
589 printf(catgets(catd, CATSET, 124,
590 "%d: Invalid message number\n"), mesg);
591 return(-1);
593 mp = &message[mesg-1];
594 if (mp->m_flag & MHIDDEN || (f != MDELETED &&
595 (mp->m_flag & MDELETED) != 0)) {
596 printf(catgets(catd, CATSET, 125,
597 "%d: Inappropriate message\n"), mesg);
598 return(-1);
600 return(0);
604 * Scan out the list of string arguments, shell style
605 * for a RAWLIST.
608 getrawlist(const char *line, size_t linesize, char **argv, int argc,
609 int echolist)
611 char c, *cp2, quotec;
612 const char *cp;
613 int argn;
614 char *linebuf;
616 argn = 0;
617 cp = line;
618 linebuf = ac_alloc(linesize + 1);
619 for (;;) {
620 for (; blankchar(*cp & 0377); cp++);
621 if (*cp == '\0')
622 break;
623 if (argn >= argc - 1) {
624 printf(catgets(catd, CATSET, 126,
625 "Too many elements in the list; excess discarded.\n"));
626 break;
628 cp2 = linebuf;
629 quotec = '\0';
630 while ((c = *cp) != '\0') {
631 cp++;
632 if (quotec != '\0') {
633 if (c == quotec) {
634 quotec = '\0';
635 if (echolist)
636 *cp2++ = c;
637 } else if (c == '\\')
638 switch (c = *cp++) {
639 case '\0':
640 *cp2++ = '\\';
641 cp--;
642 break;
644 case '0': case '1': case '2': case '3':
645 case '4': case '5': case '6': case '7':
646 c -= '0';
647 if (*cp >= '0' && *cp <= '7')
648 c = c * 8 + *cp++ - '0';
649 if (*cp >= '0' && *cp <= '7')
650 c = c * 8 + *cp++ - '0';
651 *cp2++ = c;
652 break;
653 case 'b':
654 *cp2++ = '\b';
655 break;
656 case 'f':
657 *cp2++ = '\f';
658 break;
659 case 'n':
660 *cp2++ = '\n';
661 break;
662 case 'r':
663 *cp2++ = '\r';
664 break;
665 case 't':
666 *cp2++ = '\t';
667 break;
668 case 'v':
669 *cp2++ = '\v';
670 break;
672 default:
673 if (cp[-1]!=quotec || echolist)
674 *cp2++ = '\\';
675 *cp2++ = c;
677 /*else if (c == '^') {
678 c = *cp++;
679 if (c == '?')
680 *cp2++ = '\177';
681 /\* null doesn't show up anyway *\/
682 else if ((c >= 'A' && c <= '_') ||
683 (c >= 'a' && c <= 'z'))
684 *cp2++ = c & 037;
685 else {
686 *cp2++ = '^';
687 cp--;
689 }*/ else
690 *cp2++ = c;
691 } else if (c == '"' || c == '\'') {
692 if (echolist)
693 *cp2++ = c;
694 quotec = c;
695 } else if (c == '\\' && !echolist) {
696 if (*cp)
697 *cp2++ = *cp++;
698 else
699 *cp2++ = c;
700 } else if (blankchar(c & 0377))
701 break;
702 else
703 *cp2++ = c;
705 *cp2 = '\0';
706 argv[argn++] = savestr(linebuf);
708 argv[argn] = NULL;
709 ac_free(linebuf);
710 return argn;
714 * scan out a single lexical item and return its token number,
715 * updating the string pointer passed **p. Also, store the value
716 * of the number or string scanned in lexnumber or lexstring as
717 * appropriate. In any event, store the scanned `thing' in lexstring.
720 static struct lex {
721 char l_char;
722 enum ltoken l_token;
723 } singles[] = {
724 { '$', TDOLLAR },
725 { '.', TDOT },
726 { '^', TUP },
727 { '*', TSTAR },
728 { '-', TDASH },
729 { '+', TPLUS },
730 { '(', TOPEN },
731 { ')', TCLOSE },
732 { ',', TCOMMA },
733 { ';', TSEMI },
734 { '`', TBACK },
735 { 0, 0 }
738 static int
739 scan(char **sp)
741 char *cp, *cp2;
742 int c, level, inquote;
743 struct lex *lp;
744 int quotec;
746 if (regretp >= 0) {
747 strncpy(lexstring, string_stack[regretp], STRINGLEN);
748 lexstring[STRINGLEN-1]='\0';
749 lexnumber = numberstack[regretp];
750 return(regretstack[regretp--]);
752 cp = *sp;
753 cp2 = lexstring;
754 c = *cp++;
757 * strip away leading white space.
760 while (blankchar(c))
761 c = *cp++;
764 * If no characters remain, we are at end of line,
765 * so report that.
768 if (c == '\0') {
769 *sp = --cp;
770 return(TEOL);
774 * Select members of a message thread.
776 if (c == '&') {
777 threadflag = 1;
778 if (*cp == '\0' || spacechar(*cp&0377)) {
779 lexstring[0] = '.';
780 lexstring[1] = '\0';
781 *sp = cp;
782 return TDOT;
784 c = *cp++;
788 * If the leading character is a digit, scan
789 * the number and convert it on the fly.
790 * Return TNUMBER when done.
793 if (digitchar(c)) {
794 lexnumber = 0;
795 while (digitchar(c)) {
796 lexnumber = lexnumber*10 + c - '0';
797 *cp2++ = c;
798 c = *cp++;
800 *cp2 = '\0';
801 *sp = --cp;
802 return(TNUMBER);
806 * An IMAP SEARCH list. Note that TOPEN has always been included
807 * in singles[] in Mail and mailx. Thus although there is no formal
808 * definition for (LIST) lists, they do not collide with historical
809 * practice because a subject string (LIST) could never been matched
810 * this way.
813 if (c == '(') {
814 level = 1;
815 inquote = 0;
816 *cp2++ = c;
817 do {
818 if ((c = *cp++&0377) == '\0') {
819 mtop: fprintf(stderr, "Missing \")\".\n");
820 return TERROR;
822 if (inquote && c == '\\') {
823 *cp2++ = c;
824 c = *cp++&0377;
825 if (c == '\0')
826 goto mtop;
827 } else if (c == '"')
828 inquote = !inquote;
829 else if (inquote)
830 /*EMPTY*/;
831 else if (c == '(')
832 level++;
833 else if (c == ')')
834 level--;
835 else if (spacechar(c)) {
837 * Replace unquoted whitespace by single
838 * space characters, to make the string
839 * IMAP SEARCH conformant.
841 c = ' ';
842 if (cp2[-1] == ' ')
843 cp2--;
845 *cp2++ = c;
846 } while (c != ')' || level > 0);
847 *cp2 = '\0';
848 *sp = cp;
849 return TOPEN;
853 * Check for single character tokens; return such
854 * if found.
857 for (lp = &singles[0]; lp->l_char != 0; lp++)
858 if (c == lp->l_char) {
859 lexstring[0] = c;
860 lexstring[1] = '\0';
861 *sp = cp;
862 return(lp->l_token);
866 * We've got a string! Copy all the characters
867 * of the string into lexstring, until we see
868 * a null, space, or tab.
869 * If the lead character is a " or ', save it
870 * and scan until you get another.
873 quotec = 0;
874 if (c == '\'' || c == '"') {
875 quotec = c;
876 c = *cp++;
878 while (c != '\0') {
879 if (quotec == 0 && c == '\\' && *cp)
880 c = *cp++;
881 if (c == quotec) {
882 cp++;
883 break;
885 if (quotec == 0 && blankchar(c))
886 break;
887 if (cp2 - lexstring < STRINGLEN-1)
888 *cp2++ = c;
889 c = *cp++;
891 if (quotec && c == 0) {
892 fprintf(stderr, catgets(catd, CATSET, 127,
893 "Missing %c\n"), quotec);
894 return TERROR;
896 *sp = --cp;
897 *cp2 = '\0';
898 return(TSTRING);
902 * Unscan the named token by pushing it onto the regret stack.
904 static void
905 regret(int token)
907 if (++regretp >= REGDEP)
908 panic(catgets(catd, CATSET, 128, "Too many regrets"));
909 regretstack[regretp] = token;
910 lexstring[STRINGLEN-1] = '\0';
911 string_stack[regretp] = savestr(lexstring);
912 numberstack[regretp] = lexnumber;
916 * Reset all the scanner global variables.
918 static void
919 scaninit(void)
921 regretp = -1;
922 threadflag = 0;
926 * Find the first message whose flags & m == f and return
927 * its message number.
929 int
930 first(int f, int m)
932 struct message *mp;
934 if (msgCount == 0)
935 return 0;
936 f &= MDELETED;
937 m &= MDELETED;
938 for (mp = dot; mb.mb_threaded ? mp != NULL : mp < &message[msgCount];
939 mb.mb_threaded ? mp = next_in_thread(mp) : mp++) {
940 if (!(mp->m_flag & MHIDDEN) && (mp->m_flag & m) == f)
941 return mp - message + 1;
943 if (dot > &message[0]) {
944 for (mp = dot-1; mb.mb_threaded ?
945 mp != NULL : mp >= &message[0];
946 mb.mb_threaded ?
947 mp = prev_in_thread(mp) : mp--) {
948 if (!(mp->m_flag & MHIDDEN) && (mp->m_flag & m) == f)
949 return mp - message + 1;
952 return 0;
956 * See if the passed name sent the passed message number. Return true
957 * if so.
959 static int
960 matchsender(char *str, int mesg, int allnet)
962 if (allnet) {
963 char *cp = nameof(&message[mesg - 1], 0);
965 do {
966 if ((*cp == '@' || *cp == '\0') &&
967 (*str == '@' || *str == '\0'))
968 return 1;
969 if (*cp != *str)
970 break;
971 } while (cp++, *str++ != '\0');
972 return 0;
974 return !strcmp(str, (value("showname") ? realname : skin)
975 (name1(&message[mesg - 1], 0)));
978 static int
979 matchmid(char *id, enum idfield idfield, int mesg)
981 struct name *np;
982 char *cp;
984 if ((cp = hfield("message-id", &message[mesg - 1])) != NULL) {
985 switch (idfield) {
986 case ID_REFERENCES:
987 return msgidcmp(id, cp) == 0;
988 case ID_IN_REPLY_TO:
989 if ((np = extract(id, GREF)) != NULL)
990 do {
991 if (msgidcmp(np->n_name, cp) == 0)
992 return 1;
993 } while ((np = np->n_flink) != NULL);
994 break;
997 return 0;
1001 * See if the given string matches inside the subject field of the
1002 * given message. For the purpose of the scan, we ignore case differences.
1003 * If it does, return true. The string search argument is assumed to
1004 * have the form "/search-string." If it is of the form "/," we use the
1005 * previous search string.
1008 static char lastscan[128];
1010 static int
1011 matchsubj(char *str, int mesg)
1013 struct message *mp;
1014 char *cp, *cp2;
1015 struct str in, out;
1016 int i;
1018 str++;
1019 if (strlen(str) == 0) {
1020 str = lastscan;
1021 } else {
1022 strncpy(lastscan, str, sizeof lastscan);
1023 lastscan[sizeof lastscan - 1]='\0';
1025 mp = &message[mesg-1];
1028 * Now look, ignoring case, for the word in the string.
1031 if (value("searchheaders") && (cp = strchr(str, ':'))) {
1032 *cp++ = '\0';
1033 cp2 = hfield(str, mp);
1034 cp[-1] = ':';
1035 str = cp;
1036 } else {
1037 cp = str;
1038 cp2 = hfield("subject", mp);
1040 if (cp2 == NULL)
1041 return(0);
1042 in.s = cp2;
1043 in.l = strlen(cp2);
1044 mime_fromhdr(&in, &out, TD_ICONV);
1045 i = substr(out.s, cp);
1046 free(out.s);
1047 return i;
1051 * Mark the named message by setting its mark bit.
1053 void
1054 mark(int mesg, int f)
1056 struct message *mp;
1057 int i;
1059 i = mesg;
1060 if (i < 1 || i > msgCount)
1061 panic(catgets(catd, CATSET, 129, "Bad message number to mark"));
1062 if (mb.mb_threaded == 1 && threadflag) {
1063 if ((message[i-1].m_flag & MHIDDEN) == 0) {
1064 if (f == MDELETED ||
1065 (message[i-1].m_flag&MDELETED) == 0)
1066 message[i-1].m_flag |= MMARK;
1068 if (message[i-1].m_child) {
1069 mp = message[i-1].m_child;
1070 mark(mp-message+1, f);
1071 for (mp = mp->m_younger; mp; mp = mp->m_younger)
1072 mark(mp-message+1, f);
1074 } else
1075 message[i-1].m_flag |= MMARK;
1079 * Unmark the named message.
1081 static void
1082 unmark(int mesg)
1084 int i;
1086 i = mesg;
1087 if (i < 1 || i > msgCount)
1088 panic(catgets(catd, CATSET, 130,
1089 "Bad message number to unmark"));
1090 message[i-1].m_flag &= ~MMARK;
1094 * Return the message number corresponding to the passed meta character.
1096 static int
1097 metamess(int meta, int f)
1099 int c, m;
1100 struct message *mp;
1102 c = meta;
1103 switch (c) {
1104 case '^':
1106 * First 'good' message left.
1108 mp = mb.mb_threaded ? threadroot : &message[0];
1109 while (mp < &message[msgCount]) {
1110 if (!(mp->m_flag & (MHIDDEN|MKILL)) &&
1111 (mp->m_flag & MDELETED) == f)
1112 return(mp - &message[0] + 1);
1113 if (mb.mb_threaded) {
1114 mp = next_in_thread(mp);
1115 if (mp == NULL)
1116 break;
1117 } else
1118 mp++;
1120 if (!inhook)
1121 printf(catgets(catd, CATSET, 131,
1122 "No applicable messages\n"));
1123 return(-1);
1125 case '$':
1127 * Last 'good message left.
1129 mp = mb.mb_threaded ? this_in_thread(threadroot, -1) :
1130 &message[msgCount-1];
1131 while (mp >= &message[0]) {
1132 if (!(mp->m_flag & (MHIDDEN|MKILL)) &&
1133 (mp->m_flag & MDELETED) == f)
1134 return(mp - &message[0] + 1);
1135 if (mb.mb_threaded) {
1136 mp = prev_in_thread(mp);
1137 if (mp == NULL)
1138 break;
1139 } else
1140 mp--;
1142 if (!inhook)
1143 printf(catgets(catd, CATSET, 132,
1144 "No applicable messages\n"));
1145 return(-1);
1147 case '.':
1149 * Current message.
1151 m = dot - &message[0] + 1;
1152 if (dot->m_flag & MHIDDEN || (dot->m_flag & MDELETED) != f) {
1153 printf(catgets(catd, CATSET, 133,
1154 "%d: Inappropriate message\n"), m);
1155 return(-1);
1157 return(m);
1159 case ';':
1161 * Previously current message.
1163 if (prevdot == NULL) {
1164 printf(catgets(catd, CATSET, 228,
1165 "No previously current message\n"));
1166 return(-1);
1168 m = prevdot - &message[0] + 1;
1169 if (prevdot->m_flag&MHIDDEN || (prevdot->m_flag&MDELETED)!=f) {
1170 printf(catgets(catd, CATSET, 133,
1171 "%d: Inappropriate message\n"), m);
1172 return(-1);
1174 return(m);
1176 default:
1177 printf(catgets(catd, CATSET, 134,
1178 "Unknown metachar (%c)\n"), c);
1179 return(-1);