nail.h: struct message.m_spamscore: ui_it -> ui32_t
[s-mailx.git] / list.c
blob4a85a9abc86eea4eaa2cc96b2ebb90d08637d7c5
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Message (search a.k.a. argument) list handling.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2014 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 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 enum idfield {
45 ID_REFERENCES,
46 ID_IN_REPLY_TO
49 enum {
50 CMNEW = 1<<0, /* New messages */
51 CMOLD = 1<<1, /* Old messages */
52 CMUNREAD = 1<<2, /* Unread messages */
53 CMDELETED =1<<3, /* Deleted messages */
54 CMREAD = 1<<4, /* Read messages */
55 CMFLAG = 1<<5, /* Flagged messages */
56 CMANSWER = 1<<6, /* Answered messages */
57 CMDRAFT = 1<<7, /* Draft messages */
58 CMSPAM = 1<<8 /* Spam messages */
61 struct coltab {
62 char co_char; /* What to find past : */
63 int co_bit; /* Associated modifier bit */
64 int co_mask; /* m_status bits to mask */
65 int co_equal; /* ... must equal this */
68 struct lex {
69 char l_char;
70 enum ltoken l_token;
73 static struct coltab const _coltab[] = {
74 { 'n', CMNEW, MNEW, MNEW },
75 { 'o', CMOLD, MNEW, 0 },
76 { 'u', CMUNREAD, MREAD, 0 },
77 { 'd', CMDELETED, MDELETED, MDELETED },
78 { 'r', CMREAD, MREAD, MREAD },
79 { 'f', CMFLAG, MFLAGGED, MFLAGGED },
80 { 'a', CMANSWER, MANSWERED, MANSWERED },
81 { 't', CMDRAFT, MDRAFTED, MDRAFTED },
82 { 's', CMSPAM, MSPAM, MSPAM },
83 { '\0', 0, 0, 0 }
86 static struct lex const _singles[] = {
87 { '$', TDOLLAR },
88 { '.', TDOT },
89 { '^', TUP },
90 { '*', TSTAR },
91 { '-', TDASH },
92 { '+', TPLUS },
93 { '(', TOPEN },
94 { ')', TCLOSE },
95 { ',', TCOMMA },
96 { ';', TSEMI },
97 { '`', TBACK },
98 { '\0', 0 }
101 static int lastcolmod;
102 static size_t STRINGLEN;
103 static int lexnumber; /* Number of TNUMBER from scan() */
104 static char *lexstring; /* String from TSTRING, scan() */
105 static int regretp; /* Pointer to TOS of regret tokens */
106 static int regretstack[REGDEP]; /* Stack of regretted tokens */
107 static char *string_stack[REGDEP]; /* Stack of regretted strings */
108 static int numberstack[REGDEP]; /* Stack of regretted numbers */
109 static int threadflag; /* mark entire threads */
111 /* Append, taking care of resizes */
112 static char ** add_to_namelist(char ***namelist, size_t *nmlsize,
113 char **np, char *string);
115 /* Mark all messages that the user wanted from the command line in the message
116 * structure. Return 0 on success, -1 on error */
117 static int markall(char *buf, int f);
119 /* Turn the character after a colon modifier into a bit value */
120 static int evalcol(int col);
122 /* Check the passed message number for legality and proper flags. If f is
123 * MDELETED, then either kind will do. Otherwise, the message has to be
124 * undeleted */
125 static int check(int mesg, int f);
127 /* Scan out a single lexical item and return its token number, updating the
128 * string pointer passed **sp. Also, store the value of the number or string
129 * scanned in lexnumber or lexstring as appropriate. In any event, store the
130 * scanned `thing' in lexstring */
131 static int scan(char **sp);
133 /* Unscan the named token by pushing it onto the regret stack */
134 static void regret(int token);
136 /* Reset all the scanner global variables */
137 static void scaninit(void);
139 /* See if the passed name sent the passed message number. Return true if so */
140 static int matchsender(char *str, int mesg, int allnet);
142 static int matchmid(char *id, enum idfield idfield, int mesg);
144 /* See if the given string matches inside the subject field of the given
145 * message. For the purpose of the scan, we ignore case differences. If it
146 * does, return true. The string search argument is assumed to have the form
147 * "/string." If it is of the form "/" we use the previous search string */
148 static int matchsubj(char *str, int mesg);
150 /* Unmark the named message */
151 static void unmark(int mesg);
153 /* Return the message number corresponding to the passed meta character */
154 static int metamess(int meta, int f);
156 static char **
157 add_to_namelist(char ***namelist, size_t *nmlsize, char **np, char *string)
159 size_t idx;
160 NYD_ENTER;
162 if ((idx = PTR2SIZE(np - *namelist)) >= *nmlsize) {
163 *namelist = srealloc(*namelist, (*nmlsize += 8) * sizeof *np);
164 np = &(*namelist)[idx];
166 *np++ = string;
167 NYD_LEAVE;
168 return np;
171 static int
172 markall(char *buf, int f)
174 #define markall_ret(i) do { rv = i; goto jleave; } while (0);
176 char **np, **nq;
177 int i, rv;
178 struct message *mp, *mx;
179 char **namelist, *bufp, *id = NULL, *cp;
180 int tok, beg, mc, star, other, valdot, colmod, colresult, topen, tback;
181 size_t nmlsize;
182 enum idfield idfield = ID_REFERENCES;
183 #ifdef HAVE_IMAP
184 int gotheaders;
185 #endif
186 NYD_ENTER;
188 lexstring = ac_alloc(STRINGLEN = 2 * strlen(buf) + 1);
189 valdot = (int)PTR2SIZE(dot - message + 1);
190 colmod = 0;
192 for (i = 1; i <= msgCount; ++i) {
193 enum mflag mf;
195 mf = message[i - 1].m_flag;
196 if (mf & MMARK)
197 mf |= MOLDMARK;
198 else
199 mf &= ~MOLDMARK;
200 mf &= ~MMARK;
201 message[i - 1].m_flag = mf;
204 namelist = smalloc((nmlsize = 8) * sizeof *namelist);
205 np = &namelist[0];
206 scaninit();
207 bufp = buf;
208 beg = mc = star = other = topen = tback = 0;
209 #ifdef HAVE_IMAP
210 gotheaders = 0;
211 #endif
213 for (tok = scan(&bufp); tok != TEOL;) {
214 switch (tok) {
215 case TNUMBER:
216 number:
217 if (star) {
218 fprintf(stderr, tr(112, "No numbers mixed with *\n"));
219 markall_ret(-1)
221 list_saw_numbers = TRU1;
222 mc++;
223 other++;
224 if (beg != 0) {
225 if (check(lexnumber, f))
226 markall_ret(-1)
227 i = beg;
228 while (mb.mb_threaded ? 1 : i <= lexnumber) {
229 if (!(message[i - 1].m_flag & MHIDDEN) &&
230 (f == MDELETED || !(message[i - 1].m_flag & MDELETED)))
231 mark(i, f);
232 if (mb.mb_threaded) {
233 if (i == lexnumber)
234 break;
235 mx = next_in_thread(&message[i - 1]);
236 if (mx == NULL)
237 markall_ret(-1)
238 i = (int)PTR2SIZE(mx - message + 1);
239 } else
240 ++i;
242 beg = 0;
243 break;
245 beg = lexnumber;
246 if (check(beg, f))
247 markall_ret(-1)
248 tok = scan(&bufp);
249 regret(tok);
250 if (tok != TDASH) {
251 mark(beg, f);
252 beg = 0;
254 break;
256 case TPLUS:
257 msglist_is_single = FAL0;
258 if (beg != 0) {
259 printf(tr(113, "Non-numeric second argument\n"));
260 markall_ret(-1)
262 i = valdot;
263 do {
264 if (mb.mb_threaded) {
265 mx = next_in_thread(&message[i - 1]);
266 i = mx ? (int)PTR2SIZE(mx - message + 1) : msgCount + 1;
267 } else
268 i++;
269 if (i > msgCount) {
270 fprintf(stderr, tr(114, "Referencing beyond EOF\n"));
271 markall_ret(-1)
273 } while (message[i-1].m_flag == MHIDDEN ||
274 (message[i - 1].m_flag & MDELETED) != (unsigned)f);
275 mark(i, f);
276 break;
278 case TDASH:
279 msglist_is_single = FAL0;
280 if (beg == 0) {
281 i = valdot;
282 do {
283 if (mb.mb_threaded) {
284 mx = prev_in_thread(&message[i - 1]);
285 i = mx ? (int)PTR2SIZE(mx - message + 1) : 0;
286 } else
287 i--;
288 if (i <= 0) {
289 fprintf(stderr, tr(115, "Referencing before 1\n"));
290 markall_ret(-1)
292 } while ((message[i - 1].m_flag & MHIDDEN) ||
293 (message[i - 1].m_flag & MDELETED) != (unsigned)f);
294 mark(i, f);
296 break;
298 case TSTRING:
299 msglist_is_single = FAL0;
300 if (beg != 0) {
301 fprintf(stderr, tr(116, "Non-numeric second argument\n"));
302 markall_ret(-1)
304 other++;
305 if (lexstring[0] == ':') {
306 colresult = evalcol(lexstring[1]);
307 if (colresult == 0) {
308 fprintf(stderr, tr(117, "Unknown colon modifier \"%s\"\n"),
309 lexstring);
310 markall_ret(-1)
312 colmod |= colresult;
314 else
315 np = add_to_namelist(&namelist, &nmlsize, np, savestr(lexstring));
316 break;
318 case TOPEN:
319 msglist_is_single = FAL0;
320 if (imap_search(lexstring, f) == STOP) /* TODO IMAP_SEARCH->option */
321 markall_ret(-1)
322 ++topen;
323 break;
325 case TDOLLAR:
326 case TUP:
327 case TDOT:
328 case TSEMI:
329 msglist_is_single = FAL0;
330 lexnumber = metamess(lexstring[0], f);
331 if (lexnumber == -1)
332 markall_ret(-1)
333 goto number;
335 case TBACK:
336 msglist_is_single = FAL0;
337 tback = 1;
338 for (i = 1; i <= msgCount; i++) {
339 if ((message[i - 1].m_flag & MHIDDEN) ||
340 (message[i - 1].m_flag & MDELETED) != (unsigned)f)
341 continue;
342 if (message[i - 1].m_flag & MOLDMARK)
343 mark(i, f);
345 break;
347 case TSTAR:
348 msglist_is_single = FAL0;
349 if (other) {
350 fprintf(stderr, tr(118, "Can't mix \"*\" with anything\n"));
351 markall_ret(-1)
353 ++star;
354 break;
356 case TCOMMA:
357 msglist_is_single = FAL0;
358 #ifdef HAVE_IMAP
359 if (mb.mb_type == MB_IMAP && gotheaders++ == 0)
360 imap_getheaders(1, msgCount);
361 #endif
362 if (id == NULL && (cp = hfield1("in-reply-to", dot)) != NULL) {
363 id = savestr(cp);
364 idfield = ID_IN_REPLY_TO;
366 if (id == NULL && (cp = hfield1("references", dot)) != NULL) {
367 struct name *enp;
369 if ((enp = extract(cp, GREF)) != NULL) {
370 while (enp->n_flink != NULL)
371 enp = enp->n_flink;
372 id = savestr(enp->n_name);
373 idfield = ID_REFERENCES;
376 if (id == NULL) {
377 printf(tr(227,
378 "Cannot determine parent Message-ID of the current message\n"));
379 markall_ret(-1)
381 break;
383 case TERROR:
384 list_saw_numbers = TRU1;
385 msglist_is_single = FAL0;
386 markall_ret(-1)
388 threadflag = 0;
389 tok = scan(&bufp);
392 lastcolmod = colmod;
393 np = add_to_namelist(&namelist, &nmlsize, np, NULL);
394 --np;
395 mc = 0;
396 if (star) {
397 for (i = 0; i < msgCount; ++i) {
398 if (!(message[i].m_flag & MHIDDEN) &&
399 (message[i].m_flag & MDELETED) == (unsigned)f) {
400 mark(i + 1, f);
401 ++mc;
404 if (mc == 0) {
405 if (!inhook)
406 printf(tr(119, "No applicable messages.\n"));
407 markall_ret(-1)
409 markall_ret(0)
412 if ((topen || tback) && mc == 0) {
413 for (i = 0; i < msgCount; ++i)
414 if (message[i].m_flag & MMARK)
415 ++mc;
416 if (mc == 0) {
417 if (!inhook) {
418 if (tback)
419 fprintf(stderr, tr(131, "No previously marked messages.\n"));
420 else
421 printf("No messages satisfy (criteria).\n");/*TODO tr*/
423 markall_ret(-1)
427 /* If no numbers were given, mark all messages, so that we can unmark
428 * any whose sender was not selected if any user names were given */
429 if ((np > namelist || colmod != 0 || id) && mc == 0)
430 for (i = 1; i <= msgCount; ++i) {
431 if (!(message[i - 1].m_flag & MHIDDEN) &&
432 (message[i - 1].m_flag & MDELETED) == (unsigned)f)
433 mark(i, f);
436 /* If any names were given, go through and eliminate any messages whose
437 * senders were not requested */
438 if (np > namelist || id) {
439 bool_t allnet = ok_blook(allnet);
441 #ifdef HAVE_IMAP
442 if (mb.mb_type == MB_IMAP && gotheaders++ == 0)
443 imap_getheaders(1, msgCount);
444 #endif
445 srelax_hold();
446 for (i = 1; i <= msgCount; ++i) {
447 mc = 0;
448 if (np > namelist) {
449 for (nq = namelist; *nq != NULL; ++nq)
450 if (**nq == '/') {
451 if (matchsubj(*nq, i)) {
452 ++mc;
453 break;
455 } else if (matchsender(*nq, i, allnet)) {
456 ++mc;
457 break;
460 if (mc == 0 && id && matchmid(id, idfield, i))
461 ++mc;
462 if (mc == 0)
463 unmark(i);
464 srelax();
466 srelax_rele();
468 /* Make sure we got some decent messages */
469 mc = 0;
470 for (i = 1; i <= msgCount; ++i)
471 if (message[i - 1].m_flag & MMARK) {
472 ++mc;
473 break;
475 if (mc == 0) {
476 if (!inhook && np > namelist) {
477 printf(tr(120, "No applicable messages from {%s"), namelist[0]);
478 for (nq = namelist + 1; *nq != NULL; ++nq)
479 printf(tr(121, ", %s"), *nq);
480 printf(tr(122, "}\n"));
481 } else if (id)
482 printf(tr(227, "Parent message not found\n"));
483 markall_ret(-1)
487 /* If any colon modifiers were given, go through and unmark any
488 * messages which do not satisfy the modifiers */
489 if (colmod != 0) {
490 for (i = 1; i <= msgCount; ++i) {
491 struct coltab const *colp;
492 bool_t bad = TRU1;
494 mp = &message[i - 1];
495 for (colp = _coltab; colp->co_char != '\0'; ++colp)
496 if ((colp->co_bit & colmod) &&
497 ((mp->m_flag & colp->co_mask) == (unsigned)colp->co_equal))
498 bad = FAL0;
499 if (bad)
500 unmark(i);
502 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
503 if (mp->m_flag & MMARK)
504 break;
505 if (PTRCMP(mp, >=, message + msgCount)) {
506 struct coltab const *colp;
508 if (!inhook) {
509 printf(tr(123, "No messages satisfy"));
510 for (colp = _coltab; colp->co_char != '\0'; ++colp)
511 if (colp->co_bit & colmod)
512 printf(" :%c", colp->co_char);
513 printf("\n");
515 markall_ret(-1)
519 markall_ret(0)
520 jleave:
521 free(namelist);
522 ac_free(lexstring);
523 NYD_LEAVE;
524 return rv;
526 #undef markall_ret
529 static int
530 evalcol(int col)
532 struct coltab const *colp;
533 int rv;
534 NYD_ENTER;
536 if (col == 0)
537 rv = lastcolmod;
538 else {
539 rv = 0;
540 for (colp = _coltab; colp->co_char != '\0'; ++colp)
541 if (colp->co_char == col) {
542 rv = colp->co_bit;
543 break;
546 NYD_LEAVE;
547 return rv;
550 static int
551 check(int mesg, int f)
553 struct message *mp;
554 NYD_ENTER;
556 if (mesg < 1 || mesg > msgCount) {
557 printf(tr(124, "%d: Invalid message number\n"), mesg);
558 goto jem1;
560 mp = &message[mesg - 1];
561 if (mp->m_flag & MHIDDEN ||
562 (f != MDELETED && (mp->m_flag & MDELETED) != 0)) {
563 fprintf(stderr, tr(125, "%d: Inappropriate message\n"), mesg);
564 goto jem1;
566 f = 0;
567 jleave:
568 NYD_LEAVE;
569 return f;
570 jem1:
571 f = -1;
572 goto jleave;
575 static int
576 scan(char **sp)
578 char *cp, *cp2;
579 int rv, c, inquote, quotec;
580 struct lex const *lp;
581 NYD_ENTER;
583 if (regretp >= 0) {
584 strncpy(lexstring, string_stack[regretp], STRINGLEN);
585 lexstring[STRINGLEN-1]='\0';
586 lexnumber = numberstack[regretp];
587 rv = regretstack[regretp--];
588 goto jleave;
591 cp = *sp;
592 cp2 = lexstring;
593 c = *cp++;
595 /* strip away leading white space */
596 while (blankchar(c))
597 c = *cp++;
599 /* If no characters remain, we are at end of line, so report that */
600 if (c == '\0') {
601 *sp = --cp;
602 rv = TEOL;
603 goto jleave;
606 /* Select members of a message thread */
607 if (c == '&') {
608 threadflag = 1;
609 if (*cp == '\0' || spacechar(*cp)) {
610 lexstring[0] = '.';
611 lexstring[1] = '\0';
612 *sp = cp;
613 rv = TDOT;
614 goto jleave;
616 c = *cp++;
619 /* If the leading character is a digit, scan the number and convert it
620 * on the fly. Return TNUMBER when done */
621 if (digitchar(c)) {
622 lexnumber = 0;
623 while (digitchar(c)) {
624 lexnumber = lexnumber*10 + c - '0';
625 *cp2++ = c;
626 c = *cp++;
628 *cp2 = '\0';
629 *sp = --cp;
630 rv = TNUMBER;
631 goto jleave;
634 /* An IMAP SEARCH list. Note that TOPEN has always been included in
635 * singles[] in Mail and mailx. Thus although there is no formal
636 * definition for (LIST) lists, they do not collide with historical
637 * practice because a subject string (LIST) could never been matched
638 * this way */
639 if (c == '(') {
640 ui32_t level = 1;
641 inquote = 0;
642 *cp2++ = c;
643 do {
644 if ((c = *cp++&0377) == '\0') {
645 jmtop:
646 fprintf(stderr, "Missing \")\".\n");
647 rv = TERROR;
648 goto jleave;
650 if (inquote && c == '\\') {
651 *cp2++ = c;
652 c = *cp++&0377;
653 if (c == '\0')
654 goto jmtop;
655 } else if (c == '"')
656 inquote = !inquote;
657 else if (inquote)
658 /*EMPTY*/;
659 else if (c == '(')
660 level++;
661 else if (c == ')')
662 level--;
663 else if (spacechar(c)) {
664 /* Replace unquoted whitespace by single space characters, to make
665 * the string IMAP SEARCH conformant */
666 c = ' ';
667 if (cp2[-1] == ' ')
668 cp2--;
670 *cp2++ = c;
671 } while (c != ')' || level > 0);
672 *cp2 = '\0';
673 *sp = cp;
674 rv = TOPEN;
675 goto jleave;
678 /* Check for single character tokens; return such if found */
679 for (lp = _singles; lp->l_char != '\0'; ++lp)
680 if (c == lp->l_char) {
681 lexstring[0] = c;
682 lexstring[1] = '\0';
683 *sp = cp;
684 rv = lp->l_token;
685 goto jleave;
688 /* We've got a string! Copy all the characters of the string into
689 * lexstring, until we see a null, space, or tab. If the lead character is
690 * a " or ', save it and scan until you get another */
691 quotec = 0;
692 if (c == '\'' || c == '"') {
693 quotec = c;
694 c = *cp++;
696 while (c != '\0') {
697 if (quotec == 0 && c == '\\' && *cp)
698 c = *cp++;
699 if (c == quotec) {
700 cp++;
701 break;
703 if (quotec == 0 && blankchar(c))
704 break;
705 if (PTRCMP(cp2 - lexstring, <, STRINGLEN - 1))
706 *cp2++ = c;
707 c = *cp++;
709 if (quotec && c == 0) {
710 fprintf(stderr, tr(127, "Missing %c\n"), quotec);
711 rv = TERROR;
712 goto jleave;
714 *sp = --cp;
715 *cp2 = '\0';
716 rv = TSTRING;
717 jleave:
718 NYD_LEAVE;
719 return rv;
722 static void
723 regret(int token)
725 NYD_ENTER;
726 if (++regretp >= REGDEP)
727 panic(tr(128, "Too many regrets"));
728 regretstack[regretp] = token;
729 lexstring[STRINGLEN - 1] = '\0';
730 string_stack[regretp] = savestr(lexstring);
731 numberstack[regretp] = lexnumber;
732 NYD_LEAVE;
735 static void
736 scaninit(void)
738 NYD_ENTER;
739 regretp = -1;
740 threadflag = 0;
741 NYD_LEAVE;
744 static int
745 matchsender(char *str, int mesg, int allnet)
747 int rv;
748 NYD_ENTER;
750 if (allnet) {
751 char *cp = nameof(&message[mesg - 1], 0);
753 do {
754 if ((*cp == '@' || *cp == '\0') && (*str == '@' || *str == '\0')) {
755 rv = 1;
756 goto jleave;
758 if (*cp != *str)
759 break;
760 } while (cp++, *str++ != '\0');
761 rv = 0;
762 goto jleave;
764 rv = !strcmp(str,
765 (ok_blook(showname) ? realname : skin)(name1(&message[mesg - 1], 0)));
766 jleave:
767 NYD_LEAVE;
768 return rv;
771 static int
772 matchmid(char *id, enum idfield idfield, int mesg)
774 struct name *np;
775 char *cp;
776 int rv;
777 NYD_ENTER;
779 if ((cp = hfield1("message-id", &message[mesg - 1])) != NULL) {
780 switch (idfield) {
781 case ID_REFERENCES:
782 rv = (msgidcmp(id, cp) == 0);
783 goto jleave;
784 case ID_IN_REPLY_TO:
785 if ((np = extract(id, GREF)) != NULL)
786 do {
787 if (msgidcmp(np->n_name, cp) == 0) {
788 rv = 1;
789 goto jleave;
791 } while ((np = np->n_flink) != NULL);
792 break;
795 rv = 0;
796 jleave:
797 NYD_LEAVE;
798 return rv;
801 static int
802 matchsubj(char *str, int mesg) /* FIXME regex-enable; funbody=only matching!! */
804 static char lastscan[128];
806 struct str in, out;
807 struct message *mp;
808 char *cp, *cp2;
809 int i;
810 NYD_ENTER;
812 ++str;
813 if (strlen(str) == 0) {
814 str = lastscan;
815 } else {
816 strncpy(lastscan, str, sizeof lastscan); /* XXX use new n_str object! */
817 lastscan[sizeof lastscan - 1] = '\0';
820 mp = &message[mesg - 1];
822 /* Now look, ignoring case, for the word in the string */
823 if (ok_blook(searchheaders) && (cp = strchr(str, ':'))) {
824 *cp++ = '\0';
825 cp2 = hfieldX(str, mp);
826 cp[-1] = ':';
827 } else {
828 cp = str;
829 cp2 = hfield1("subject", mp);
831 if (cp2 == NULL) {
832 i = 0;
833 goto jleave;
836 in.s = cp2;
837 in.l = strlen(cp2);
838 mime_fromhdr(&in, &out, TD_ICONV);
839 i = substr(out.s, cp);
840 free(out.s);
841 jleave:
842 NYD_LEAVE;
843 return i;
846 static void
847 unmark(int mesg)
849 size_t i;
850 NYD_ENTER;
852 i = (size_t)mesg;
853 if (i < 1 || UICMP(z, i, >, msgCount))
854 panic(tr(130, "Bad message number to unmark"));
855 message[i - 1].m_flag &= ~MMARK;
856 NYD_LEAVE;
859 static int
860 metamess(int meta, int f)
862 int c, m;
863 struct message *mp;
864 NYD_ENTER;
866 c = meta;
867 switch (c) {
868 case '^': /* First 'good' message left */
869 mp = mb.mb_threaded ? threadroot : &message[0];
870 while (PTRCMP(mp, <, message + msgCount)) {
871 if (!(mp->m_flag & MHIDDEN) && (mp->m_flag & MDELETED) ==(unsigned)f){
872 c = (int)PTR2SIZE(mp - message + 1);
873 goto jleave;
875 if (mb.mb_threaded) {
876 mp = next_in_thread(mp);
877 if (mp == NULL)
878 break;
879 } else
880 ++mp;
882 if (!inhook)
883 printf(tr(132, "No applicable messages\n"));
884 goto jem1;
886 case '$': /* Last 'good message left */
887 mp = mb.mb_threaded ? this_in_thread(threadroot, -1)
888 : &message[msgCount-1];
889 while (mp >= &message[0]) {
890 if (!(mp->m_flag & MHIDDEN) && (mp->m_flag & MDELETED) == (ui32_t)f) {
891 c = (int)PTR2SIZE(mp - message + 1);
892 goto jleave;
894 if (mb.mb_threaded) {
895 mp = prev_in_thread(mp);
896 if (mp == NULL)
897 break;
898 } else
899 --mp;
901 if (!inhook)
902 printf(tr(132, "No applicable messages\n"));
903 goto jem1;
905 case '.':
906 /* Current message */
907 m = dot - message + 1;
908 if ((dot->m_flag & MHIDDEN) || (dot->m_flag & MDELETED) != (unsigned)f) {
909 printf(tr(133, "%d: Inappropriate message\n"), m);
910 goto jem1;
912 c = m;
913 break;
915 case ';':
916 /* Previously current message */
917 if (prevdot == NULL) {
918 fprintf(stderr, tr(228, "No previously current message\n"));
919 goto jem1;
921 m = prevdot - message + 1;
922 if ((prevdot->m_flag & MHIDDEN) ||
923 (prevdot->m_flag & MDELETED) != (unsigned)f) {
924 fprintf(stderr, tr(133, "%d: Inappropriate message\n"), m);
925 goto jem1;
927 c = m;
928 break;
930 default:
931 fprintf(stderr, tr(134, "Unknown metachar (%c)\n"), c);
932 goto jem1;
934 jleave:
935 NYD_LEAVE;
936 return c;
937 jem1:
938 c = -1;
939 goto jleave;
942 FL int
943 getmsglist(char *buf, int *vector, int flags)
945 int *ip, mc;
946 struct message *mp;
947 NYD_ENTER;
949 list_saw_numbers =
950 msglist_is_single = FAL0;
952 if (msgCount == 0) {
953 *vector = 0;
954 mc = 0;
955 goto jleave;
958 msglist_is_single = TRU1;
959 if (markall(buf, flags) < 0) {
960 mc = -1;
961 goto jleave;
964 ip = vector;
965 if (inhook & 2) {
966 mc = 0;
967 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
968 if (mp->m_flag & MMARK) {
969 if ((mp->m_flag & MNEWEST) == 0)
970 unmark((int)PTR2SIZE(mp - message + 1));
971 else
972 ++mc;
974 if (mc == 0) {
975 mc = -1;
976 goto jleave;
980 if (mb.mb_threaded == 0) {
981 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
982 if (mp->m_flag & MMARK)
983 *ip++ = (int)PTR2SIZE(mp - message + 1);
984 } else {
985 for (mp = threadroot; mp != NULL; mp = next_in_thread(mp))
986 if (mp->m_flag & MMARK)
987 *ip++ = (int)PTR2SIZE(mp - message + 1);
989 *ip = 0;
990 mc = (int)PTR2SIZE(ip - vector);
991 msglist_is_single = (mc == 1);
992 jleave:
993 NYD_LEAVE;
994 return mc;
997 FL int
998 getrawlist(char const *line, size_t linesize, char **argv, int argc,
999 int echolist)
1001 char c, *cp2, quotec, *linebuf;
1002 char const *cp;
1003 int argn;
1004 NYD_ENTER;
1006 list_saw_numbers = FAL0;
1008 argn = 0;
1009 cp = line;
1010 linebuf = ac_alloc(linesize + 1);
1011 for (;;) {
1012 for (; blankchar(*cp); ++cp)
1014 if (*cp == '\0')
1015 break;
1016 if (argn >= argc - 1) {
1017 printf(tr(126, "Too many elements in the list; excess discarded.\n"));
1018 break;
1020 cp2 = linebuf;
1021 quotec = '\0';
1022 while ((c = *cp) != '\0') {
1023 cp++;
1024 if (quotec != '\0') {
1025 if (c == quotec) {
1026 quotec = '\0';
1027 if (echolist)
1028 *cp2++ = c;
1029 } else if (c == '\\')
1030 switch (c = *cp++) {
1031 case '\0':
1032 *cp2++ = '\\';
1033 cp--;
1034 break;
1036 case '0': case '1': case '2': case '3':
1037 case '4': case '5': case '6': case '7':
1038 c -= '0';
1039 if (*cp >= '0' && *cp <= '7')
1040 c = c * 8 + *cp++ - '0';
1041 if (*cp >= '0' && *cp <= '7')
1042 c = c * 8 + *cp++ - '0';
1043 *cp2++ = c;
1044 break;
1045 case 'b':
1046 *cp2++ = '\b';
1047 break;
1048 case 'f':
1049 *cp2++ = '\f';
1050 break;
1051 case 'n':
1052 *cp2++ = '\n';
1053 break;
1054 case 'r':
1055 *cp2++ = '\r';
1056 break;
1057 case 't':
1058 *cp2++ = '\t';
1059 break;
1060 case 'v':
1061 *cp2++ = '\v';
1062 break;
1064 default:
1065 if (cp[-1] != quotec || echolist)
1066 *cp2++ = '\\';
1067 *cp2++ = c;
1069 /*else if (c == '^') {
1070 c = *cp++;
1071 if (c == '?')
1072 *cp2++ = '\177';
1073 /\* null doesn't show up anyway *\/
1074 else if ((c >= 'A' && c <= '_') ||
1075 (c >= 'a' && c <= 'z'))
1076 *cp2++ = c & 037;
1077 else {
1078 *cp2++ = '^';
1079 cp--;
1081 }*/ else
1082 *cp2++ = c;
1083 } else if (c == '"' || c == '\'') {
1084 if (echolist)
1085 *cp2++ = c;
1086 quotec = c;
1087 } else if (c == '\\' && !echolist) {
1088 if (*cp)
1089 *cp2++ = *cp++;
1090 else
1091 *cp2++ = c;
1092 } else if (blankchar(c))
1093 break;
1094 else
1095 *cp2++ = c;
1097 *cp2 = '\0';
1098 argv[argn++] = savestr(linebuf);
1100 argv[argn] = NULL;
1101 ac_free(linebuf);
1102 NYD_LEAVE;
1103 return argn;
1106 FL int
1107 first(int f, int m)
1109 struct message *mp;
1110 int rv;
1111 NYD_ENTER;
1113 if (msgCount == 0) {
1114 rv = 0;
1115 goto jleave;
1118 f &= MDELETED;
1119 m &= MDELETED;
1120 for (mp = dot;
1121 mb.mb_threaded ? mp != NULL : PTRCMP(mp, <, message + msgCount);
1122 mb.mb_threaded ? mp = next_in_thread(mp) : ++mp) {
1123 if (!(mp->m_flag & MHIDDEN) && (mp->m_flag & m) == (unsigned)f) {
1124 rv = (int)PTR2SIZE(mp - message + 1);
1125 goto jleave;
1129 if (dot > message) {
1130 for (mp = dot-1; (mb.mb_threaded ? mp != NULL : mp >= message);
1131 mb.mb_threaded ? mp = prev_in_thread(mp) : --mp) {
1132 if (!(mp->m_flag & MHIDDEN) && (mp->m_flag & m) == (unsigned)f) {
1133 rv = (int)PTR2SIZE(mp - message + 1);
1134 goto jleave;
1138 rv = 0;
1139 jleave:
1140 NYD_LEAVE;
1141 return rv;
1144 FL void
1145 mark(int mesg, int f)
1147 struct message *mp;
1148 int i;
1149 NYD_ENTER;
1151 i = mesg;
1152 if (i < 1 || i > msgCount)
1153 panic(tr(129, "Bad message number to mark"));
1154 if (mb.mb_threaded == 1 && threadflag) {
1155 if ((message[i - 1].m_flag & MHIDDEN) == 0) {
1156 if (f == MDELETED || (message[i - 1].m_flag&MDELETED) == 0)
1157 message[i - 1].m_flag |= MMARK;
1160 if (message[i - 1].m_child) {
1161 mp = message[i - 1].m_child;
1162 mark((int)PTR2SIZE(mp - message + 1), f);
1163 for (mp = mp->m_younger; mp; mp = mp->m_younger)
1164 mark((int)PTR2SIZE(mp - message + 1), f);
1166 } else
1167 message[i - 1].m_flag |= MMARK;
1168 NYD_LEAVE;
1171 /* vim:set fenc=utf-8:s-it-mode */