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 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
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
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
42 #ifndef HAVE_AMALGAMATION
46 /* Token values returned by the scanner used for argument lists.
47 * Also, sizes of scanner-related things */
49 TEOL
= 0, /* End of the command line */
50 TNUMBER
= 1, /* A message number */
51 TDASH
= 2, /* A simple dash */
52 TSTRING
= 3, /* A string (possibly containing -) */
55 TDOLLAR
= 6, /* A "$" */
56 TSTAR
= 7, /* A "*" */
57 TOPEN
= 8, /* A '(' */
58 TCLOSE
= 9, /* A ')' */
59 TPLUS
= 10, /* A '+' */
60 TERROR
= 11, /* A lexical error */
61 TCOMMA
= 12, /* A ',' */
62 TSEMI
= 13, /* A ';' */
63 TBACK
= 14 /* A '`' */
66 #define REGDEP 2 /* Maximum regret depth. */
74 CMNEW
= 1<<0, /* New messages */
75 CMOLD
= 1<<1, /* Old messages */
76 CMUNREAD
= 1<<2, /* Unread messages */
77 CMDELETED
=1<<3, /* Deleted messages */
78 CMREAD
= 1<<4, /* Read messages */
79 CMFLAG
= 1<<5, /* Flagged messages */
80 CMANSWER
= 1<<6, /* Answered messages */
81 CMDRAFT
= 1<<7, /* Draft messages */
82 CMSPAM
= 1<<8, /* Spam messages */
83 CMSPAMUN
= 1<<9 /* Maybe spam messages (unsure) */
87 char co_char
; /* What to find past : */
88 int co_bit
; /* Associated modifier bit */
89 int co_mask
; /* m_status bits to mask */
90 int co_equal
; /* ... must equal this */
98 static struct coltab
const _coltab
[] = {
99 { 'n', CMNEW
, MNEW
, MNEW
},
100 { 'o', CMOLD
, MNEW
, 0 },
101 { 'u', CMUNREAD
, MREAD
, 0 },
102 { 'd', CMDELETED
, MDELETED
, MDELETED
},
103 { 'r', CMREAD
, MREAD
, MREAD
},
104 { 'f', CMFLAG
, MFLAGGED
, MFLAGGED
},
105 { 'a', CMANSWER
, MANSWERED
, MANSWERED
},
106 { 't', CMDRAFT
, MDRAFTED
, MDRAFTED
},
107 { 's', CMSPAM
, MSPAM
, MSPAM
},
108 { 'S', CMSPAMUN
, MSPAMUNSURE
, MSPAMUNSURE
},
112 static struct lex
const _singles
[] = {
127 static int lastcolmod
;
128 static size_t STRINGLEN
;
129 static int lexnumber
; /* Number of TNUMBER from scan() */
130 static char *lexstring
; /* String from TSTRING, scan() */
131 static int regretp
; /* Pointer to TOS of regret tokens */
132 static int regretstack
[REGDEP
]; /* Stack of regretted tokens */
133 static char *string_stack
[REGDEP
]; /* Stack of regretted strings */
134 static int numberstack
[REGDEP
]; /* Stack of regretted numbers */
135 static int threadflag
; /* mark entire threads */
137 /* Append, taking care of resizes */
138 static char ** add_to_namelist(char ***namelist
, size_t *nmlsize
,
139 char **np
, char *string
);
141 /* Mark all messages that the user wanted from the command line in the message
142 * structure. Return 0 on success, -1 on error */
143 static int markall(char *buf
, int f
);
145 /* Turn the character after a colon modifier into a bit value */
146 static int evalcol(int col
);
148 /* Check the passed message number for legality and proper flags. If f is
149 * MDELETED, then either kind will do. Otherwise, the message has to be
151 static int check(int mesg
, int f
);
153 /* Scan out a single lexical item and return its token number, updating the
154 * string pointer passed **sp. Also, store the value of the number or string
155 * scanned in lexnumber or lexstring as appropriate. In any event, store the
156 * scanned "thing" in lexstring */
157 static int scan(char **sp
);
159 /* Unscan the named token by pushing it onto the regret stack */
160 static void regret(int token
);
162 /* Reset all the scanner global variables */
163 static void scaninit(void);
165 /* See if the passed name sent the passed message */
166 static bool_t
_matchsender(struct message
*mp
, char const *str
, bool_t allnet
);
168 static bool_t
_matchmid(struct message
*mp
, char *id
, enum idfield idfield
);
170 /* See if the given string matches.
171 * For the purpose of the scan, we ignore case differences.
172 * This is the engine behind the "/" search */
173 static bool_t
_match_dash(struct message
*mp
, char const *str
);
175 /* See if the given search expression matches.
176 * For the purpose of the scan, we ignore case differences.
177 * This is the engine behind the "@[..]@" search */
178 static bool_t
_match_at(struct message
*mp
, struct search_expr
*sep
);
180 /* Unmark the named message */
181 static void unmark(int mesg
);
183 /* Return the message number corresponding to the passed meta character */
184 static int metamess(int meta
, int f
);
187 add_to_namelist(char ***namelist
, size_t *nmlsize
, char **np
, char *string
)
192 if ((idx
= PTR2SIZE(np
- *namelist
)) >= *nmlsize
) {
193 *namelist
= srealloc(*namelist
, (*nmlsize
+= 8) * sizeof *np
);
194 np
= *namelist
+ idx
;
202 markall(char *buf
, int f
)
204 #define markall_ret(i) do { rv = i; goto jleave; } while (0);
206 /* TODO use a bit carrier for all the states */
207 char **np
, **nq
, **namelist
, *bufp
, *id
= NULL
, *cp
;
208 int rv
= 0, i
, tok
, beg
, other
, valdot
, colmod
, colresult
;
209 struct message
*mp
, *mx
;
210 bool_t mc
, star
, topen
, tback
;
212 enum idfield idfield
= ID_REFERENCES
;
218 lexstring
= ac_alloc(STRINGLEN
= 2 * strlen(buf
) +1);
219 valdot
= (int)PTR2SIZE(dot
- message
+ 1);
222 for (i
= 1; i
<= msgCount
; ++i
) {
225 mf
= message
[i
- 1].m_flag
;
231 message
[i
- 1].m_flag
= mf
;
234 np
= namelist
= smalloc((nmlsize
= 8) * sizeof *namelist
);
238 beg
= star
= other
= topen
= tback
= FAL0
;
243 for (tok
= scan(&bufp
); tok
!= TEOL
;) {
248 fprintf(stderr
, _("No numbers mixed with *\n"));
251 pstate
|= PS_MSGLIST_SAW_NO
;
254 if (check(lexnumber
, f
))
257 while (mb
.mb_threaded
? 1 : i
<= lexnumber
) {
258 if (!(message
[i
- 1].m_flag
& MHIDDEN
) &&
259 (f
== MDELETED
|| !(message
[i
- 1].m_flag
& MDELETED
)))
261 if (mb
.mb_threaded
) {
264 mx
= next_in_thread(&message
[i
- 1]);
267 i
= (int)PTR2SIZE(mx
- message
+ 1);
286 pstate
&= ~PS_MSGLIST_DIRECT
;
288 printf(_("Non-numeric second argument\n"));
293 if (mb
.mb_threaded
) {
294 mx
= next_in_thread(message
+ i
- 1);
295 i
= mx
? (int)PTR2SIZE(mx
- message
+ 1) : msgCount
+ 1;
299 fprintf(stderr
, _("Referencing beyond EOF\n"));
302 } while (message
[i
- 1].m_flag
== MHIDDEN
||
303 (message
[i
- 1].m_flag
& MDELETED
) != (unsigned)f
);
308 pstate
&= ~PS_MSGLIST_DIRECT
;
312 if (mb
.mb_threaded
) {
313 mx
= prev_in_thread(message
+ i
- 1);
314 i
= mx
? (int)PTR2SIZE(mx
- message
+ 1) : 0;
318 fprintf(stderr
, _("Referencing before 1\n"));
321 } while ((message
[i
- 1].m_flag
& MHIDDEN
) ||
322 (message
[i
- 1].m_flag
& MDELETED
) != (unsigned)f
);
328 pstate
&= ~PS_MSGLIST_DIRECT
;
330 fprintf(stderr
, _("Non-numeric second argument\n"));
334 if ((cp
= lexstring
)[0] == ':') {
335 while (*++cp
!= '\0') {
336 colresult
= evalcol(*cp
);
337 if (colresult
== 0) {
338 fprintf(stderr
, _("Unknown colon modifier \"%s\"\n"),
345 np
= add_to_namelist(&namelist
, &nmlsize
, np
, savestr(lexstring
));
349 #ifdef HAVE_IMAP_SEARCH
350 pstate
&= ~PS_MSGLIST_DIRECT
;
351 if (imap_search(lexstring
, f
) == STOP
)
355 fprintf(stderr
, _("Optional selector is not available: \"%s\"\n"),
365 pstate
&= ~PS_MSGLIST_DIRECT
;
366 lexnumber
= metamess(lexstring
[0], f
);
372 pstate
&= ~PS_MSGLIST_DIRECT
;
374 for (i
= 1; i
<= msgCount
; i
++) {
375 if ((message
[i
- 1].m_flag
& MHIDDEN
) ||
376 (message
[i
- 1].m_flag
& MDELETED
) != (unsigned)f
)
378 if (message
[i
- 1].m_flag
& MOLDMARK
)
384 pstate
&= ~PS_MSGLIST_DIRECT
;
386 fprintf(stderr
, _("Can't mix \"*\" with anything\n"));
393 pstate
&= ~PS_MSGLIST_DIRECT
;
395 if (mb
.mb_type
== MB_IMAP
&& gotheaders
++ == 0)
396 imap_getheaders(1, msgCount
);
398 if (id
== NULL
&& (cp
= hfield1("in-reply-to", dot
)) != NULL
) {
400 idfield
= ID_IN_REPLY_TO
;
402 if (id
== NULL
&& (cp
= hfield1("references", dot
)) != NULL
) {
405 if ((enp
= extract(cp
, GREF
)) != NULL
) {
406 while (enp
->n_flink
!= NULL
)
408 id
= savestr(enp
->n_name
);
409 idfield
= ID_REFERENCES
;
414 "Cannot determine parent Message-ID of the current message\n"));
420 pstate
&= ~PS_MSGLIST_DIRECT
;
421 pstate
|= PS_MSGLIST_SAW_NO
;
429 np
= add_to_namelist(&namelist
, &nmlsize
, np
, NULL
);
433 for (i
= 0; i
< msgCount
; ++i
) {
434 if (!(message
[i
].m_flag
& MHIDDEN
) &&
435 (message
[i
].m_flag
& MDELETED
) == (unsigned)f
) {
441 if (!(pstate
& PS_HOOK_MASK
))
442 printf(_("No applicable messages.\n"));
448 if ((topen
|| tback
) && !mc
) {
449 for (i
= 0; i
< msgCount
; ++i
)
450 if (message
[i
].m_flag
& MMARK
)
453 if (!(pstate
& PS_HOOK_MASK
)) {
455 fprintf(stderr
, _("No previously marked messages.\n"));
457 printf("No messages satisfy (criteria).\n");/*TODO tr*/
463 /* If no numbers were given, mark all messages, so that we can unmark
464 * any whose sender was not selected if any user names were given */
465 if ((np
> namelist
|| colmod
!= 0 || id
) && !mc
) {
466 for (i
= 1; i
<= msgCount
; ++i
) {
467 if (!(message
[i
- 1].m_flag
& MHIDDEN
) &&
468 (message
[i
- 1].m_flag
& MDELETED
) == (unsigned)f
)
473 /* If any names were given, eliminate any messages which don't match */
474 if (np
> namelist
|| id
) {
475 struct search_expr
*sep
= NULL
;
478 /* The @ search works with struct search_expr, so build an array.
479 * To simplify array, i.e., regex_t destruction, and optimize for the
480 * common case we walk the entire array even in case of errors */
482 sep
= scalloc(PTR2SIZE(np
- namelist
), sizeof(*sep
));
483 for (j
= 0, nq
= namelist
; *nq
!= NULL
; ++j
, ++nq
) {
487 if (*x
!= '@' || rv
< 0)
490 for (y
= x
+ 1;; ++y
) {
491 if (*y
== '\0' || !fieldnamechar(*y
)) {
500 sep
[j
].ss_where
= (x
== NULL
|| x
- 1 == *nq
)
501 ? "subject" : savestrbuf(*nq
+ 1, PTR2SIZE(x
- *nq
) - 1);
503 x
= (x
== NULL
? *nq
: x
) + 1;
504 if (*x
== '\0') { /* XXX Simply remove from list instead? */
505 fprintf(stderr
, _("Empty \"[@..]@\" search expression\n"));
510 if (is_maybe_regex(x
)) {
511 sep
[j
].ss_sexpr
= NULL
;
512 if (regcomp(&sep
[j
].ss_regex
, x
,
513 REG_EXTENDED
| REG_ICASE
| REG_NOSUB
) != 0) {
515 "Invalid regular expression: >>> %s <<<\n"), x
);
524 goto jnamesearch_sepfree
;
528 if (mb
.mb_type
== MB_IMAP
&& gotheaders
++ == 0)
529 imap_getheaders(1, msgCount
);
532 allnet
= ok_blook(allnet
);
533 for (i
= 1; i
<= msgCount
; ++i
) {
534 mp
= message
+ i
- 1;
537 for (nq
= namelist
; *nq
!= NULL
; ++nq
) {
539 if (_match_at(mp
, sep
+ PTR2SIZE(nq
- namelist
))) {
543 } else if (**nq
== '/') {
544 if (_match_dash(mp
, *nq
)) {
548 } else if (_matchsender(mp
, *nq
, allnet
)) {
554 if (j
== 0 && id
&& _matchmid(mp
, id
, idfield
))
557 mp
->m_flag
&= ~MMARK
;
562 /* Make sure we got some decent messages */
564 for (i
= 1; i
<= msgCount
; ++i
)
565 if (message
[i
- 1].m_flag
& MMARK
) {
570 if (!(pstate
& PS_HOOK_MASK
) && np
> namelist
) {
571 printf(_("No applicable messages from {%s"), namelist
[0]);
572 for (nq
= namelist
+ 1; *nq
!= NULL
; ++nq
)
573 printf(_(", %s"), *nq
);
576 printf(_("Parent message not found\n"));
578 goto jnamesearch_sepfree
;
584 for (j
= PTR2SIZE(np
- namelist
); j
-- != 0;)
585 if (sep
[j
].ss_sexpr
== NULL
)
586 regfree(&sep
[j
].ss_regex
);
594 /* If any colon modifiers were given, go through and unmark any
595 * messages which do not satisfy the modifiers */
597 for (i
= 1; i
<= msgCount
; ++i
) {
598 struct coltab
const *colp
;
601 mp
= message
+ i
- 1;
602 for (colp
= _coltab
; colp
->co_char
!= '\0'; ++colp
)
603 if ((colp
->co_bit
& colmod
) &&
604 ((mp
->m_flag
& colp
->co_mask
) == (unsigned)colp
->co_equal
))
610 for (mp
= message
; PTRCMP(mp
, <, message
+ msgCount
); ++mp
)
611 if (mp
->m_flag
& MMARK
)
614 if (PTRCMP(mp
, >=, message
+ msgCount
)) {
615 struct coltab
const *colp
;
617 if (!(pstate
& PS_HOOK_MASK
)) {
618 printf(_("No messages satisfy"));
619 for (colp
= _coltab
; colp
->co_char
!= '\0'; ++colp
)
620 if (colp
->co_bit
& colmod
)
621 printf(" :%c", colp
->co_char
);
641 struct coltab
const *colp
;
649 for (colp
= _coltab
; colp
->co_char
!= '\0'; ++colp
)
650 if (colp
->co_char
== col
) {
660 check(int mesg
, int f
)
665 if (mesg
< 1 || mesg
> msgCount
) {
666 printf(_("%d: Invalid message number\n"), mesg
);
669 mp
= message
+ mesg
- 1;
670 if (mp
->m_flag
& MHIDDEN
||
671 (f
!= MDELETED
&& (mp
->m_flag
& MDELETED
) != 0)) {
672 fprintf(stderr
, _("%d: Inappropriate message\n"), mesg
);
688 int rv
, c
, inquote
, quotec
;
689 struct lex
const *lp
;
693 strncpy(lexstring
, string_stack
[regretp
], STRINGLEN
);
694 lexstring
[STRINGLEN
-1] = '\0';
695 lexnumber
= numberstack
[regretp
];
696 rv
= regretstack
[regretp
--];
704 /* strip away leading white space */
708 /* If no characters remain, we are at end of line, so report that */
715 /* Select members of a message thread */
718 if (*cp
== '\0' || spacechar(*cp
)) {
728 /* If the leading character is a digit, scan the number and convert it
729 * on the fly. Return TNUMBER when done */
732 while (digitchar(c
)) {
733 lexnumber
= lexnumber
*10 + c
- '0';
743 /* An IMAP SEARCH list. Note that TOPEN has always been included in
744 * singles[] in Mail and mailx. Thus although there is no formal
745 * definition for (LIST) lists, they do not collide with historical
746 * practice because a subject string (LIST) could never been matched
753 if ((c
= *cp
++&0377) == '\0') {
755 fprintf(stderr
, "Missing \")\".\n");
759 if (inquote
&& c
== '\\') {
772 else if (spacechar(c
)) {
773 /* Replace unquoted whitespace by single space characters, to make
774 * the string IMAP SEARCH conformant */
780 } while (c
!= ')' || level
> 0);
787 /* Check for single character tokens; return such if found */
788 for (lp
= _singles
; lp
->l_char
!= '\0'; ++lp
)
789 if (c
== lp
->l_char
) {
797 /* We've got a string! Copy all the characters of the string into
798 * lexstring, until we see a null, space, or tab. If the lead character is
799 * a " or ', save it and scan until you get another */
801 if (c
== '\'' || c
== '"') {
806 if (quotec
== 0 && c
== '\\' && *cp
!= '\0')
812 if (quotec
== 0 && blankchar(c
))
814 if (PTRCMP(cp2
- lexstring
, <, STRINGLEN
- 1))
818 if (quotec
&& c
== 0) {
819 fprintf(stderr
, _("Missing %c\n"), quotec
);
835 if (++regretp
>= REGDEP
)
836 panic(_("Too many regrets"));
837 regretstack
[regretp
] = token
;
838 lexstring
[STRINGLEN
-1] = '\0';
839 string_stack
[regretp
] = savestr(lexstring
);
840 numberstack
[regretp
] = lexnumber
;
854 _matchsender(struct message
*mp
, char const *str
, bool_t allnet
)
860 char *cp
= nameof(mp
, 0);
863 if ((*cp
== '@' || *cp
== '\0') && (*str
== '@' || *str
== '\0')) {
869 } while (++cp
, *str
++ != '\0');
873 rv
= !strcmp(str
, (*(ok_blook(showname
) ? &realname
: &skin
))(name1(mp
, 0)));
880 _matchmid(struct message
*mp
, char *id
, enum idfield idfield
)
886 if ((cp
= hfield1("message-id", mp
)) != NULL
) {
889 rv
= !msgidcmp(id
, cp
);
891 case ID_IN_REPLY_TO
: {
894 if ((np
= extract(id
, GREF
)) != NULL
)
896 if (!msgidcmp(np
->n_name
, cp
)) {
900 } while ((np
= np
->n_flink
) != NULL
);
912 _match_dash(struct message
*mp
, char const *str
)
914 static char lastscan
[128];
917 char *hfield
, *hbody
;
921 if (*++str
== '\0') {
924 strncpy(lastscan
, str
, sizeof lastscan
); /* XXX use new n_str object! */
925 lastscan
[sizeof lastscan
-1] = '\0';
928 /* Now look, ignoring case, for the word in the string */
929 if (ok_blook(searchheaders
) && (hfield
= strchr(str
, ':'))) {
930 size_t l
= PTR2SIZE(hfield
- str
);
931 hfield
= ac_alloc(l
+1);
932 memcpy(hfield
, str
, l
);
934 hbody
= hfieldX(hfield
, mp
);
936 hfield
= UNCONST(str
+ l
+ 1);
938 hfield
= UNCONST(str
);
939 hbody
= hfield1("subject", mp
);
947 in
.l
= strlen(hbody
);
948 mime_fromhdr(&in
, &out
, TD_ICONV
);
949 rv
= substr(out
.s
, hfield
);
957 _match_at(struct message
*mp
, struct search_expr
*sep
)
960 char *nfield
, *cfield
;
964 nfield
= savestr(sep
->ss_where
);
966 while ((cfield
= n_strsep(&nfield
, ',', TRU1
)) != NULL
) {
967 if (!asccasecmp(cfield
, "body") ||
968 (cfield
[1] == '\0' && cfield
[0] == '>')) {
971 if ((rv
= message_match(mp
, sep
, rv
)))
973 } else if (!asccasecmp(cfield
, "text") ||
974 (cfield
[1] == '\0' && cfield
[0] == '=')) {
977 } else if (!asccasecmp(cfield
, "header") ||
978 (cfield
[1] == '\0' && cfield
[0] == '<')) {
979 if ((rv
= header_match(mp
, sep
)))
981 } else if ((in
.s
= hfieldX(cfield
, mp
)) == NULL
)
985 mime_fromhdr(&in
, &out
, TD_ICONV
);
987 if (sep
->ss_sexpr
== NULL
)
988 rv
= (regexec(&sep
->ss_regex
, out
.s
, 0,NULL
, 0) != REG_NOMATCH
);
991 rv
= substr(out
.s
, sep
->ss_sexpr
);
1008 if (i
< 1 || UICMP(z
, i
, >, msgCount
))
1009 panic(_("Bad message number to unmark"));
1010 message
[i
- 1].m_flag
&= ~MMARK
;
1015 metamess(int meta
, int f
)
1023 case '^': /* First 'good' message left */
1024 mp
= mb
.mb_threaded
? threadroot
: message
;
1025 while (PTRCMP(mp
, <, message
+ msgCount
)) {
1026 if (!(mp
->m_flag
& MHIDDEN
) && (mp
->m_flag
& MDELETED
) == (ui32_t
)f
) {
1027 c
= (int)PTR2SIZE(mp
- message
+ 1);
1030 if (mb
.mb_threaded
) {
1031 mp
= next_in_thread(mp
);
1037 if (!(pstate
& PS_HOOK_MASK
))
1038 printf(_("No applicable messages\n"));
1041 case '$': /* Last 'good message left */
1043 ? this_in_thread(threadroot
, -1) : message
+ msgCount
- 1;
1044 while (mp
>= message
) {
1045 if (!(mp
->m_flag
& MHIDDEN
) && (mp
->m_flag
& MDELETED
) == (ui32_t
)f
) {
1046 c
= (int)PTR2SIZE(mp
- message
+ 1);
1049 if (mb
.mb_threaded
) {
1050 mp
= prev_in_thread(mp
);
1056 if (!(pstate
& PS_HOOK_MASK
))
1057 printf(_("No applicable messages\n"));
1061 /* Current message */
1062 m
= dot
- message
+ 1;
1063 if ((dot
->m_flag
& MHIDDEN
) || (dot
->m_flag
& MDELETED
) != (ui32_t
)f
) {
1064 printf(_("%d: Inappropriate message\n"), m
);
1071 /* Previously current message */
1072 if (prevdot
== NULL
) {
1073 fprintf(stderr
, _("No previously current message\n"));
1076 m
= prevdot
- message
+ 1;
1077 if ((prevdot
->m_flag
& MHIDDEN
) ||
1078 (prevdot
->m_flag
& MDELETED
) != (ui32_t
)f
) {
1079 fprintf(stderr
, _("%d: Inappropriate message\n"), m
);
1086 fprintf(stderr
, _("Unknown metachar (%c)\n"), c
);
1098 getmsglist(char *buf
, int *vector
, int flags
)
1104 pstate
&= ~PS_MSGLIST_MASK
;
1106 if (msgCount
== 0) {
1112 pstate
|= PS_MSGLIST_DIRECT
;
1114 if (markall(buf
, flags
) < 0) {
1120 if (pstate
& PS_HOOK_NEWMAIL
) {
1122 for (mp
= message
; PTRCMP(mp
, <, message
+ msgCount
); ++mp
)
1123 if (mp
->m_flag
& MMARK
) {
1124 if (!(mp
->m_flag
& MNEWEST
))
1125 unmark((int)PTR2SIZE(mp
- message
+ 1));
1135 if (mb
.mb_threaded
== 0) {
1136 for (mp
= message
; PTRCMP(mp
, <, message
+ msgCount
); ++mp
)
1137 if (mp
->m_flag
& MMARK
)
1138 *ip
++ = (int)PTR2SIZE(mp
- message
+ 1);
1140 for (mp
= threadroot
; mp
!= NULL
; mp
= next_in_thread(mp
))
1141 if (mp
->m_flag
& MMARK
)
1142 *ip
++ = (int)PTR2SIZE(mp
- message
+ 1);
1145 mc
= (int)PTR2SIZE(ip
- vector
);
1147 pstate
&= ~PS_MSGLIST_DIRECT
;
1154 getrawlist(char const *line
, size_t linesize
, char **argv
, int argc
,
1157 char c
, *cp2
, quotec
, *linebuf
;
1161 pstate
&= ~PS_MSGLIST_MASK
;
1163 linebuf
= ac_alloc(linesize
);
1166 if (!argn
|| !echolist
) {
1167 for (; blankchar(*line
); ++line
)
1172 if (argn
>= argc
- 1) {
1174 _("Too many elements in the list; excess discarded.\n"));
1179 for (quotec
= '\0'; ((c
= *line
++) != '\0');) {
1180 if (quotec
!= '\0') {
1185 } else if (c
== '\\') {
1186 switch (c
= *line
++) {
1192 if (line
[-1] != quotec
|| echolist
)
1198 } else if (c
== '"' || c
== '\'') {
1202 } else if (c
== '\\' && !echolist
)
1203 *cp2
++ = (*line
!= '\0') ? *line
++ : c
;
1204 else if (blankchar(c
))
1209 argv
[argn
++] = savestrbuf(linebuf
, PTR2SIZE(cp2
- linebuf
));
1227 if (msgCount
== 0) {
1235 mb
.mb_threaded
? (mp
!= NULL
) : PTRCMP(mp
, <, message
+ msgCount
);
1236 mb
.mb_threaded
? (mp
= next_in_thread(mp
)) : ++mp
) {
1237 if (!(mp
->m_flag
& MHIDDEN
) && (mp
->m_flag
& m
) == (ui32_t
)f
) {
1238 rv
= (int)PTR2SIZE(mp
- message
+ 1);
1243 if (dot
> message
) {
1244 for (mp
= dot
- 1; (mb
.mb_threaded
? (mp
!= NULL
) : (mp
>= message
));
1245 mb
.mb_threaded
? (mp
= prev_in_thread(mp
)) : --mp
) {
1246 if (!(mp
->m_flag
& MHIDDEN
) && (mp
->m_flag
& m
) == (ui32_t
)f
) {
1247 rv
= (int)PTR2SIZE(mp
- message
+ 1);
1259 mark(int mesg
, int f
)
1266 if (i
< 1 || i
> msgCount
)
1267 panic(_("Bad message number to mark"));
1268 if (mb
.mb_threaded
== 1 && threadflag
) {
1269 if (!(message
[i
- 1].m_flag
& MHIDDEN
)) {
1270 if (f
== MDELETED
|| !(message
[i
- 1].m_flag
& MDELETED
))
1271 message
[i
- 1].m_flag
|= MMARK
;
1274 if (message
[i
- 1].m_child
) {
1275 mp
= message
[i
- 1].m_child
;
1276 mark((int)PTR2SIZE(mp
- message
+ 1), f
);
1277 for (mp
= mp
->m_younger
; mp
!= NULL
; mp
= mp
->m_younger
)
1278 mark((int)PTR2SIZE(mp
- message
+ 1), f
);
1281 message
[i
- 1].m_flag
|= MMARK
;