Nits for silencing -Wshadow
[s-mailx.git] / list.c
blob9b8dc27d6324db8610545d4d1a6683f376a1b8f8
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Message list handling.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2013 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
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 #include "rcv.h"
42 #include <ctype.h>
43 #ifdef HAVE_WCTYPE_H
44 # include <wctype.h>
45 #endif
47 #include "extern.h"
49 enum idfield {
50 ID_REFERENCES,
51 ID_IN_REPLY_TO
54 static char **add_to_namelist(char ***namelist, size_t *nmlsize,
55 char **np, char *string);
56 static int markall(char *buf, int f);
57 static int evalcol(int col);
58 static int check(int mesg, int f);
59 static int scan(char **sp);
60 static void regret(int token);
61 static void scaninit(void);
62 static int matchsender(char *str, int mesg, int allnet);
63 static int matchmid(char *id, enum idfield idfield, int mesg);
64 static int matchsubj(char *str, int mesg);
65 static void unmark(int mesg);
66 static int metamess(int meta, int f);
68 static size_t STRINGLEN;
70 static int lexnumber; /* Number of TNUMBER from scan() */
71 static char *lexstring; /* String from TSTRING, scan() */
72 static int regretp; /* Pointer to TOS of regret tokens */
73 static int regretstack[REGDEP]; /* Stack of regretted tokens */
74 static char *string_stack[REGDEP]; /* Stack of regretted strings */
75 static int numberstack[REGDEP]; /* Stack of regretted numbers */
76 static int threadflag; /* mark entire threads */
79 * Convert the user string of message numbers and
80 * store the numbers into vector.
82 * Returns the count of messages picked up or -1 on error.
84 int
85 getmsglist(char *buf, int *vector, int flags)
87 int *ip;
88 struct message *mp;
89 int mc;
91 msglist_is_single = FAL0;
92 if (msgCount == 0) {
93 *vector = 0;
94 return 0;
97 msglist_is_single = TRU1;
98 if (markall(buf, flags) < 0)
99 return(-1);
100 ip = vector;
101 if (inhook & 2) {
102 mc = 0;
103 for (mp = &message[0]; mp < &message[msgCount]; mp++)
104 if (mp->m_flag & MMARK) {
105 if ((mp->m_flag & MNEWEST) == 0)
106 unmark(mp - &message[0] + 1);
107 else
108 mc++;
110 if (mc == 0)
111 return -1;
113 if (mb.mb_threaded == 0) {
114 for (mp = &message[0]; mp < &message[msgCount]; mp++)
115 if (mp->m_flag & MMARK)
116 *ip++ = mp - &message[0] + 1;
117 } else {
118 for (mp = threadroot; mp; mp = next_in_thread(mp))
119 if (mp->m_flag & MMARK)
120 *ip++ = mp - &message[0] + 1;
122 *ip = 0;
123 if ((size_t)(ip - vector) != 1)
124 msglist_is_single = FAL0;
125 return(ip - vector);
129 * Mark all messages that the user wanted from the command
130 * line in the message structure. Return 0 on success, -1
131 * on error.
135 * Bit values for colon modifiers.
138 #define CMNEW 01 /* New messages */
139 #define CMOLD 02 /* Old messages */
140 #define CMUNREAD 04 /* Unread messages */
141 #define CMDELETED 010 /* Deleted messages */
142 #define CMREAD 020 /* Read messages */
143 #define CMFLAG 040 /* Flagged messages */
144 #define CMANSWER 0100 /* Answered messages */
145 #define CMDRAFT 0200 /* Draft messages */
146 #define CMSPAM 0400 /* Spam messages */
149 * The following table describes the letters which can follow
150 * the colon and gives the corresponding modifier bit.
153 static struct coltab {
154 char co_char; /* What to find past : */
155 int co_bit; /* Associated modifier bit */
156 int co_mask; /* m_status bits to mask */
157 int co_equal; /* ... must equal this */
158 } coltab[] = {
159 { 'n', CMNEW, MNEW, MNEW },
160 { 'o', CMOLD, MNEW, 0 },
161 { 'u', CMUNREAD, MREAD, 0 },
162 { 'd', CMDELETED, MDELETED, MDELETED },
163 { 'r', CMREAD, MREAD, MREAD },
164 { 'f', CMFLAG, MFLAGGED, MFLAGGED },
165 { 'a', CMANSWER, MANSWERED, MANSWERED },
166 { 't', CMDRAFT, MDRAFTED, MDRAFTED },
167 { 's', CMSPAM, MSPAM, MSPAM },
168 { 0, 0, 0, 0 }
171 static int lastcolmod;
173 static char **
174 add_to_namelist(char ***namelist, size_t *nmlsize, char **np, char *string)
176 size_t idx;
178 if ((idx = np - *namelist) >= *nmlsize) {
179 *namelist = srealloc(*namelist, (*nmlsize += 8) * sizeof *np);
180 np = &(*namelist)[idx];
182 *np++ = string;
183 return np;
186 static int
187 markall(char *buf, int f)
189 #define markall_ret(i) do { retval = i; goto out; } while (0);
190 char **np, **nq;
191 int i, retval;
192 struct message *mp, *mx;
193 char **namelist, *bufp, *id = NULL, *cp;
194 int tok, beg, mc, star, other, valdot, colmod, colresult, topen, tback;
195 size_t nmlsize;
196 enum idfield idfield = ID_REFERENCES;
197 #ifdef HAVE_IMAP
198 int gotheaders;
199 #endif
201 lexstring = ac_alloc(STRINGLEN = 2 * strlen(buf) + 1);
202 valdot = dot - &message[0] + 1;
203 colmod = 0;
204 for (i = 1; i <= msgCount; i++) {
205 message[i-1].m_flag &= ~MOLDMARK;
206 if (message[i-1].m_flag & MMARK)
207 message[i-1].m_flag |= MOLDMARK;
208 unmark(i);
210 bufp = buf;
211 mc = 0;
212 namelist = smalloc((nmlsize = 8) * sizeof *namelist);
213 np = &namelist[0];
214 scaninit();
215 tok = scan(&bufp);
216 star = 0;
217 other = 0;
218 beg = 0;
219 topen = 0;
220 tback = 0;
221 #ifdef HAVE_IMAP
222 gotheaders = 0;
223 #endif
225 while (tok != TEOL) {
226 switch (tok) {
227 case TNUMBER:
228 number:
229 if (star) {
230 printf(catgets(catd, CATSET, 112,
231 "No numbers mixed with *\n"));
232 markall_ret(-1)
234 mc++;
235 other++;
236 if (beg != 0) {
237 if (check(lexnumber, f))
238 markall_ret(-1)
239 i = beg;
240 while (mb.mb_threaded ? 1 : i <= lexnumber) {
241 if (!(message[i-1].m_flag&MHIDDEN) &&
242 (f == MDELETED ||
243 (message[i-1].m_flag &
244 MDELETED) == 0))
245 mark(i, f);
246 if (mb.mb_threaded) {
247 if (i == lexnumber)
248 break;
249 mx = next_in_thread(&message[i-1]);
250 if (mx == NULL)
251 markall_ret(-1)
252 i = mx-message+1;
253 } else
254 i++;
256 beg = 0;
257 break;
259 beg = lexnumber;
260 if (check(beg, f))
261 markall_ret(-1)
262 tok = scan(&bufp);
263 regret(tok);
264 if (tok != TDASH) {
265 mark(beg, f);
266 beg = 0;
268 break;
270 case TPLUS:
271 msglist_is_single = FAL0;
272 if (beg != 0) {
273 printf(catgets(catd, CATSET, 113,
274 "Non-numeric second argument\n"));
275 markall_ret(-1)
277 i = valdot;
278 do {
279 if (mb.mb_threaded) {
280 mx = next_in_thread(&message[i-1]);
281 i = mx ? mx-message+1 : msgCount+1;
282 } else
283 i++;
284 if (i > msgCount) {
285 printf(catgets(catd, CATSET, 114,
286 "Referencing beyond EOF\n"));
287 markall_ret(-1)
289 } while (message[i-1].m_flag == MHIDDEN ||
290 (message[i-1].m_flag & MDELETED) !=
291 (unsigned)f);
292 mark(i, f);
293 break;
295 case TDASH:
296 msglist_is_single = FAL0;
297 if (beg == 0) {
298 i = valdot;
299 do {
300 if (mb.mb_threaded) {
301 mx = prev_in_thread(
302 &message[i-1]);
303 i = mx ? mx-message+1 : 0;
304 } else
305 i--;
306 if (i <= 0) {
307 printf(catgets(catd, CATSET,
308 115,
309 "Referencing before 1\n"));
310 markall_ret(-1)
312 } while ((message[i-1].m_flag & MHIDDEN) ||
313 (message[i-1].m_flag & MDELETED)
314 != (unsigned)f);
315 mark(i, f);
317 break;
319 case TSTRING:
320 msglist_is_single = FAL0;
321 if (beg != 0) {
322 printf(catgets(catd, CATSET, 116,
323 "Non-numeric second argument\n"));
324 markall_ret(-1)
326 other++;
327 if (lexstring[0] == ':') {
328 colresult = evalcol(lexstring[1]);
329 if (colresult == 0) {
330 printf(catgets(catd, CATSET, 117,
331 "Unknown colon modifier \"%s\"\n"),
332 lexstring);
333 markall_ret(-1)
335 colmod |= colresult;
337 else
338 np = add_to_namelist(&namelist, &nmlsize,
339 np, savestr(lexstring));
340 break;
342 case TOPEN:
343 msglist_is_single = FAL0;
344 if (imap_search(lexstring, f) == STOP)
345 markall_ret(-1)
346 topen++;
347 break;
349 case TDOLLAR:
350 case TUP:
351 case TDOT:
352 case TSEMI:
353 msglist_is_single = FAL0;
354 lexnumber = metamess(lexstring[0], f);
355 if (lexnumber == -1)
356 markall_ret(-1)
357 goto number;
359 case TBACK:
360 msglist_is_single = FAL0;
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 != (unsigned)f)
366 continue;
367 if (message[i-1].m_flag&MOLDMARK)
368 mark(i, f);
370 break;
372 case TSTAR:
373 msglist_is_single = FAL0;
374 if (other) {
375 printf(catgets(catd, CATSET, 118,
376 "Can't mix \"*\" with anything\n"));
377 markall_ret(-1)
379 star++;
380 break;
382 case TCOMMA:
383 msglist_is_single = FAL0;
384 #ifdef HAVE_IMAP
385 if (mb.mb_type == MB_IMAP && gotheaders++ == 0)
386 imap_getheaders(1, msgCount);
387 #endif
388 if (id == NULL && (cp = hfield1("in-reply-to", dot))
389 != NULL) {
390 id = savestr(cp);
391 idfield = ID_IN_REPLY_TO;
393 if (id == NULL && (cp = hfield1("references", dot))
394 != NULL) {
395 struct name *enp;
396 if ((enp = extract(cp, GREF)) != NULL) {
397 while (enp->n_flink != NULL)
398 enp = enp->n_flink;
399 id = savestr(enp->n_name);
400 idfield = ID_REFERENCES;
403 if (id == NULL) {
404 printf(catgets(catd, CATSET, 227,
405 "Cannot determine parent Message-ID of the current message\n"));
406 markall_ret(-1)
408 break;
410 case TERROR:
411 msglist_is_single = FAL0;
412 markall_ret(-1)
414 threadflag = 0;
415 tok = scan(&bufp);
417 lastcolmod = colmod;
418 np = add_to_namelist(&namelist, &nmlsize, np, NULL);
419 np--;
420 mc = 0;
421 if (star) {
422 for (i = 0; i < msgCount; i++) {
423 if (!(message[i].m_flag & MHIDDEN) &&
424 (message[i].m_flag & MDELETED) ==
425 (unsigned)f) {
426 mark(i+1, f);
427 mc++;
430 if (mc == 0) {
431 if (!inhook)
432 printf(catgets(catd, CATSET, 119,
433 "No applicable messages.\n"));
434 markall_ret(-1)
436 markall_ret(0)
439 if ((topen || tback) && mc == 0) {
440 for (i = 0; i < msgCount; i++)
441 if (message[i].m_flag & MMARK)
442 mc++;
443 if (mc == 0) {
444 if (!inhook)
445 printf(tback ?
446 "No previously marked messages.\n" :
447 "No messages satisfy (criteria).\n");
448 markall_ret(-1)
453 * If no numbers were given, mark all of the messages,
454 * so that we can unmark any whose sender was not selected
455 * if any user names were given.
458 if ((np > namelist || colmod != 0 || id) && mc == 0)
459 for (i = 1; i <= msgCount; i++) {
460 if (!(message[i-1].m_flag & MHIDDEN) &&
461 (message[i-1].m_flag & MDELETED) ==
462 (unsigned)f)
463 mark(i, f);
467 * If any names were given, go through and eliminate any
468 * messages whose senders were not requested.
471 if (np > namelist || id) {
472 int allnet = value("allnet") != NULL;
474 #ifdef HAVE_IMAP
475 if (mb.mb_type == MB_IMAP && gotheaders++ == 0)
476 imap_getheaders(1, msgCount);
477 #endif
478 for (i = 1; i <= msgCount; i++) {
479 mc = 0;
480 if (np > namelist) {
481 for (nq = &namelist[0]; *nq != NULL; nq++) {
482 if (**nq == '/') {
483 if (matchsubj(*nq, i)) {
484 mc++;
485 break;
488 else {
489 if (matchsender(*nq, i,
490 allnet)) {
491 mc++;
492 break;
497 if (mc == 0 && id && matchmid(id, idfield, i))
498 mc++;
499 if (mc == 0)
500 unmark(i);
504 * Make sure we got some decent messages.
507 mc = 0;
508 for (i = 1; i <= msgCount; i++)
509 if (message[i-1].m_flag & MMARK) {
510 mc++;
511 break;
513 if (mc == 0) {
514 if (!inhook && np > namelist) {
515 printf(catgets(catd, CATSET, 120,
516 "No applicable messages from {%s"),
517 namelist[0]);
518 for (nq = &namelist[1]; *nq != NULL; nq++)
519 printf(catgets(catd, CATSET, 121,
520 ", %s"), *nq);
521 printf(catgets(catd, CATSET, 122, "}\n"));
522 } else if (id) {
523 printf(catgets(catd, CATSET, 227,
524 "Parent message not found\n"));
526 markall_ret(-1)
531 * If any colon modifiers were given, go through and
532 * unmark any messages which do not satisfy the modifiers.
535 if (colmod != 0) {
536 for (i = 1; i <= msgCount; i++) {
537 struct coltab *colp;
538 bool_t bad = TRU1;
540 mp = &message[i - 1];
541 for (colp = &coltab[0]; colp->co_char; colp++)
542 if ((colp->co_bit & colmod) &&
543 ((mp->m_flag & colp->co_mask)
544 == (unsigned)colp->co_equal))
545 bad = FAL0;
546 if (bad)
547 unmark(i);
549 for (mp = &message[0]; mp < &message[msgCount]; mp++)
550 if (mp->m_flag & MMARK)
551 break;
552 if (mp >= &message[msgCount]) {
553 struct coltab *colp;
555 if (!inhook) {
556 printf(tr(123, "No messages satisfy"));
557 for (colp = &coltab[0]; colp->co_char; colp++)
558 if (colp->co_bit & colmod)
559 printf(" :%c", colp->co_char);
560 printf("\n");
562 markall_ret(-1)
565 markall_ret(0)
566 out:
567 free(namelist);
568 ac_free(lexstring);
569 return retval;
570 #undef markall_ret
574 * Turn the character after a colon modifier into a bit
575 * value.
577 static int
578 evalcol(int col)
580 struct coltab *colp;
582 if (col == 0)
583 return(lastcolmod);
584 for (colp = &coltab[0]; colp->co_char; colp++)
585 if (colp->co_char == col)
586 return(colp->co_bit);
587 return(0);
591 * Check the passed message number for legality and proper flags.
592 * If f is MDELETED, then either kind will do. Otherwise, the message
593 * has to be undeleted.
595 static int
596 check(int mesg, int f)
598 struct message *mp;
600 if (mesg < 1 || mesg > msgCount) {
601 printf(catgets(catd, CATSET, 124,
602 "%d: Invalid message number\n"), mesg);
603 return(-1);
605 mp = &message[mesg-1];
606 if (mp->m_flag & MHIDDEN || (f != MDELETED &&
607 (mp->m_flag & MDELETED) != 0)) {
608 printf(catgets(catd, CATSET, 125,
609 "%d: Inappropriate message\n"), mesg);
610 return(-1);
612 return(0);
616 * Scan out the list of string arguments, shell style
617 * for a RAWLIST.
620 getrawlist(const char *line, size_t linesize, char **argv, int argc,
621 int echolist)
623 char c, *cp2, quotec;
624 const char *cp;
625 int argn;
626 char *linebuf;
628 argn = 0;
629 cp = line;
630 linebuf = ac_alloc(linesize + 1);
631 for (;;) {
632 for (; blankchar(*cp & 0377); cp++);
633 if (*cp == '\0')
634 break;
635 if (argn >= argc - 1) {
636 printf(catgets(catd, CATSET, 126,
637 "Too many elements in the list; excess discarded.\n"));
638 break;
640 cp2 = linebuf;
641 quotec = '\0';
642 while ((c = *cp) != '\0') {
643 cp++;
644 if (quotec != '\0') {
645 if (c == quotec) {
646 quotec = '\0';
647 if (echolist)
648 *cp2++ = c;
649 } else if (c == '\\')
650 switch (c = *cp++) {
651 case '\0':
652 *cp2++ = '\\';
653 cp--;
654 break;
656 case '0': case '1': case '2': case '3':
657 case '4': case '5': case '6': case '7':
658 c -= '0';
659 if (*cp >= '0' && *cp <= '7')
660 c = c * 8 + *cp++ - '0';
661 if (*cp >= '0' && *cp <= '7')
662 c = c * 8 + *cp++ - '0';
663 *cp2++ = c;
664 break;
665 case 'b':
666 *cp2++ = '\b';
667 break;
668 case 'f':
669 *cp2++ = '\f';
670 break;
671 case 'n':
672 *cp2++ = '\n';
673 break;
674 case 'r':
675 *cp2++ = '\r';
676 break;
677 case 't':
678 *cp2++ = '\t';
679 break;
680 case 'v':
681 *cp2++ = '\v';
682 break;
684 default:
685 if (cp[-1]!=quotec || echolist)
686 *cp2++ = '\\';
687 *cp2++ = c;
689 /*else if (c == '^') {
690 c = *cp++;
691 if (c == '?')
692 *cp2++ = '\177';
693 /\* null doesn't show up anyway *\/
694 else if ((c >= 'A' && c <= '_') ||
695 (c >= 'a' && c <= 'z'))
696 *cp2++ = c & 037;
697 else {
698 *cp2++ = '^';
699 cp--;
701 }*/ else
702 *cp2++ = c;
703 } else if (c == '"' || c == '\'') {
704 if (echolist)
705 *cp2++ = c;
706 quotec = c;
707 } else if (c == '\\' && !echolist) {
708 if (*cp)
709 *cp2++ = *cp++;
710 else
711 *cp2++ = c;
712 } else if (blankchar(c & 0377))
713 break;
714 else
715 *cp2++ = c;
717 *cp2 = '\0';
718 argv[argn++] = savestr(linebuf);
720 argv[argn] = NULL;
721 ac_free(linebuf);
722 return argn;
726 * scan out a single lexical item and return its token number,
727 * updating the string pointer passed **p. Also, store the value
728 * of the number or string scanned in lexnumber or lexstring as
729 * appropriate. In any event, store the scanned `thing' in lexstring.
732 static struct lex {
733 char l_char;
734 enum ltoken l_token;
735 } singles[] = {
736 { '$', TDOLLAR },
737 { '.', TDOT },
738 { '^', TUP },
739 { '*', TSTAR },
740 { '-', TDASH },
741 { '+', TPLUS },
742 { '(', TOPEN },
743 { ')', TCLOSE },
744 { ',', TCOMMA },
745 { ';', TSEMI },
746 { '`', TBACK },
747 { 0, 0 }
750 static int
751 scan(char **sp)
753 char *cp, *cp2;
754 int c, level, inquote;
755 struct lex *lp;
756 int quotec;
758 if (regretp >= 0) {
759 strncpy(lexstring, string_stack[regretp], STRINGLEN);
760 lexstring[STRINGLEN-1]='\0';
761 lexnumber = numberstack[regretp];
762 return(regretstack[regretp--]);
764 cp = *sp;
765 cp2 = lexstring;
766 c = *cp++;
769 * strip away leading white space.
772 while (blankchar(c))
773 c = *cp++;
776 * If no characters remain, we are at end of line,
777 * so report that.
780 if (c == '\0') {
781 *sp = --cp;
782 return(TEOL);
786 * Select members of a message thread.
788 if (c == '&') {
789 threadflag = 1;
790 if (*cp == '\0' || spacechar(*cp&0377)) {
791 lexstring[0] = '.';
792 lexstring[1] = '\0';
793 *sp = cp;
794 return TDOT;
796 c = *cp++;
800 * If the leading character is a digit, scan
801 * the number and convert it on the fly.
802 * Return TNUMBER when done.
805 if (digitchar(c)) {
806 lexnumber = 0;
807 while (digitchar(c)) {
808 lexnumber = lexnumber*10 + c - '0';
809 *cp2++ = c;
810 c = *cp++;
812 *cp2 = '\0';
813 *sp = --cp;
814 return(TNUMBER);
818 * An IMAP SEARCH list. Note that TOPEN has always been included
819 * in singles[] in Mail and mailx. Thus although there is no formal
820 * definition for (LIST) lists, they do not collide with historical
821 * practice because a subject string (LIST) could never been matched
822 * this way.
825 if (c == '(') {
826 level = 1;
827 inquote = 0;
828 *cp2++ = c;
829 do {
830 if ((c = *cp++&0377) == '\0') {
831 mtop: fprintf(stderr, "Missing \")\".\n");
832 return TERROR;
834 if (inquote && c == '\\') {
835 *cp2++ = c;
836 c = *cp++&0377;
837 if (c == '\0')
838 goto mtop;
839 } else if (c == '"')
840 inquote = !inquote;
841 else if (inquote)
842 /*EMPTY*/;
843 else if (c == '(')
844 level++;
845 else if (c == ')')
846 level--;
847 else if (spacechar(c)) {
849 * Replace unquoted whitespace by single
850 * space characters, to make the string
851 * IMAP SEARCH conformant.
853 c = ' ';
854 if (cp2[-1] == ' ')
855 cp2--;
857 *cp2++ = c;
858 } while (c != ')' || level > 0);
859 *cp2 = '\0';
860 *sp = cp;
861 return TOPEN;
865 * Check for single character tokens; return such
866 * if found.
869 for (lp = &singles[0]; lp->l_char != 0; lp++)
870 if (c == lp->l_char) {
871 lexstring[0] = c;
872 lexstring[1] = '\0';
873 *sp = cp;
874 return(lp->l_token);
878 * We've got a string! Copy all the characters
879 * of the string into lexstring, until we see
880 * a null, space, or tab.
881 * If the lead character is a " or ', save it
882 * and scan until you get another.
885 quotec = 0;
886 if (c == '\'' || c == '"') {
887 quotec = c;
888 c = *cp++;
890 while (c != '\0') {
891 if (quotec == 0 && c == '\\' && *cp)
892 c = *cp++;
893 if (c == quotec) {
894 cp++;
895 break;
897 if (quotec == 0 && blankchar(c))
898 break;
899 if ((size_t)(cp2 - lexstring) < (size_t)STRINGLEN - 1)
900 *cp2++ = c;
901 c = *cp++;
903 if (quotec && c == 0) {
904 fprintf(stderr, catgets(catd, CATSET, 127,
905 "Missing %c\n"), quotec);
906 return TERROR;
908 *sp = --cp;
909 *cp2 = '\0';
910 return(TSTRING);
914 * Unscan the named token by pushing it onto the regret stack.
916 static void
917 regret(int token)
919 if (++regretp >= REGDEP)
920 panic(catgets(catd, CATSET, 128, "Too many regrets"));
921 regretstack[regretp] = token;
922 lexstring[STRINGLEN-1] = '\0';
923 string_stack[regretp] = savestr(lexstring);
924 numberstack[regretp] = lexnumber;
928 * Reset all the scanner global variables.
930 static void
931 scaninit(void)
933 regretp = -1;
934 threadflag = 0;
938 * Find the first message whose flags & m == f and return
939 * its message number.
941 int
942 first(int f, int m)
944 struct message *mp;
946 if (msgCount == 0)
947 return 0;
948 f &= MDELETED;
949 m &= MDELETED;
950 for (mp = dot; mb.mb_threaded ? mp != NULL : mp < &message[msgCount];
951 mb.mb_threaded ? mp = next_in_thread(mp) : mp++) {
952 if (!(mp->m_flag & MHIDDEN) && (mp->m_flag & m) == (unsigned)f)
953 return mp - message + 1;
955 if (dot > &message[0]) {
956 for (mp = dot-1; mb.mb_threaded ?
957 mp != NULL : mp >= &message[0];
958 mb.mb_threaded ?
959 mp = prev_in_thread(mp) : mp--) {
960 if (! (mp->m_flag & MHIDDEN) &&
961 (mp->m_flag & m) == (unsigned)f)
962 return mp - message + 1;
965 return 0;
969 * See if the passed name sent the passed message number. Return true
970 * if so.
972 static int
973 matchsender(char *str, int mesg, int allnet)
975 if (allnet) {
976 char *cp = nameof(&message[mesg - 1], 0);
978 do {
979 if ((*cp == '@' || *cp == '\0') &&
980 (*str == '@' || *str == '\0'))
981 return 1;
982 if (*cp != *str)
983 break;
984 } while (cp++, *str++ != '\0');
985 return 0;
987 return !strcmp(str, (value("showname") ? realname : skin)
988 (name1(&message[mesg - 1], 0)));
991 static int
992 matchmid(char *id, enum idfield idfield, int mesg)
994 struct name *np;
995 char *cp;
997 if ((cp = hfield1("message-id", &message[mesg - 1])) != NULL) {
998 switch (idfield) {
999 case ID_REFERENCES:
1000 return msgidcmp(id, cp) == 0;
1001 case ID_IN_REPLY_TO:
1002 if ((np = extract(id, GREF)) != NULL)
1003 do {
1004 if (msgidcmp(np->n_name, cp) == 0)
1005 return 1;
1006 } while ((np = np->n_flink) != NULL);
1007 break;
1010 return 0;
1014 * See if the given string matches inside the subject field of the
1015 * given message. For the purpose of the scan, we ignore case differences.
1016 * If it does, return true. The string search argument is assumed to
1017 * have the form "/search-string." If it is of the form "/," we use the
1018 * previous search string.
1021 static char lastscan[128];
1023 static int
1024 matchsubj(char *str, int mesg)
1026 struct message *mp;
1027 char *cp, *cp2;
1028 struct str in, out;
1029 int i;
1031 str++;
1032 if (strlen(str) == 0) {
1033 str = lastscan;
1034 } else {
1035 strncpy(lastscan, str, sizeof lastscan);
1036 lastscan[sizeof lastscan - 1]='\0';
1038 mp = &message[mesg-1];
1041 * Now look, ignoring case, for the word in the string.
1044 if (value("searchheaders") && (cp = strchr(str, ':'))) {
1045 *cp++ = '\0';
1046 cp2 = hfieldX(str, mp);
1047 cp[-1] = ':';
1048 } else {
1049 cp = str;
1050 cp2 = hfield1("subject", mp);
1052 if (cp2 == NULL)
1053 return(0);
1054 in.s = cp2;
1055 in.l = strlen(cp2);
1056 mime_fromhdr(&in, &out, TD_ICONV);
1057 i = substr(out.s, cp);
1058 free(out.s);
1059 return i;
1063 * Mark the named message by setting its mark bit.
1065 void
1066 mark(int mesg, int f)
1068 struct message *mp;
1069 int i;
1071 i = mesg;
1072 if (i < 1 || i > msgCount)
1073 panic(catgets(catd, CATSET, 129, "Bad message number to mark"));
1074 if (mb.mb_threaded == 1 && threadflag) {
1075 if ((message[i-1].m_flag & MHIDDEN) == 0) {
1076 if (f == MDELETED ||
1077 (message[i-1].m_flag&MDELETED) == 0)
1078 message[i-1].m_flag |= MMARK;
1080 if (message[i-1].m_child) {
1081 mp = message[i-1].m_child;
1082 mark(mp-message+1, f);
1083 for (mp = mp->m_younger; mp; mp = mp->m_younger)
1084 mark(mp-message+1, f);
1086 } else
1087 message[i-1].m_flag |= MMARK;
1091 * Unmark the named message.
1093 static void
1094 unmark(int mesg)
1096 int i;
1098 i = mesg;
1099 if (i < 1 || i > msgCount)
1100 panic(catgets(catd, CATSET, 130,
1101 "Bad message number to unmark"));
1102 message[i-1].m_flag &= ~MMARK;
1106 * Return the message number corresponding to the passed meta character.
1108 static int
1109 metamess(int meta, int f)
1111 int c, m;
1112 struct message *mp;
1114 c = meta;
1115 switch (c) {
1116 case '^':
1118 * First 'good' message left.
1120 mp = mb.mb_threaded ? threadroot : &message[0];
1121 while (mp < &message[msgCount]) {
1122 if (!(mp->m_flag & MHIDDEN) &&
1123 (mp->m_flag & MDELETED) == (unsigned)f)
1124 return(mp - &message[0] + 1);
1125 if (mb.mb_threaded) {
1126 mp = next_in_thread(mp);
1127 if (mp == NULL)
1128 break;
1129 } else
1130 mp++;
1132 if (!inhook)
1133 printf(catgets(catd, CATSET, 131,
1134 "No applicable messages\n"));
1135 return(-1);
1137 case '$':
1139 * Last 'good message left.
1141 mp = mb.mb_threaded ? this_in_thread(threadroot, -1) :
1142 &message[msgCount-1];
1143 while (mp >= &message[0]) {
1144 if (!(mp->m_flag & MHIDDEN) &&
1145 (mp->m_flag & MDELETED) == (unsigned)f)
1146 return(mp - &message[0] + 1);
1147 if (mb.mb_threaded) {
1148 mp = prev_in_thread(mp);
1149 if (mp == NULL)
1150 break;
1151 } else
1152 mp--;
1154 if (!inhook)
1155 printf(catgets(catd, CATSET, 132,
1156 "No applicable messages\n"));
1157 return(-1);
1159 case '.':
1161 * Current message.
1163 m = dot - &message[0] + 1;
1164 if ((dot->m_flag & MHIDDEN) ||
1165 (dot->m_flag & MDELETED) != (unsigned)f) {
1166 printf(catgets(catd, CATSET, 133,
1167 "%d: Inappropriate message\n"), m);
1168 return(-1);
1170 return(m);
1172 case ';':
1174 * Previously current message.
1176 if (prevdot == NULL) {
1177 printf(catgets(catd, CATSET, 228,
1178 "No previously current message\n"));
1179 return(-1);
1181 m = prevdot - &message[0] + 1;
1182 if ((prevdot->m_flag & MHIDDEN) ||
1183 (prevdot->m_flag & MDELETED) != (unsigned)f) {
1184 printf(catgets(catd, CATSET, 133,
1185 "%d: Inappropriate message\n"), m);
1186 return(-1);
1188 return(m);
1190 default:
1191 printf(catgets(catd, CATSET, 134,
1192 "Unknown metachar (%c)\n"), c);
1193 return(-1);