1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Message, message array, getmsglist(), and related operations.
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. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 #define n_FILE message
38 #ifndef HAVE_AMALGAMATION
42 /* Token values returned by the scanner used for argument lists.
43 * Also, sizes of scanner-related things */
45 TEOL
= 0, /* End of the command line */
46 TNUMBER
= 1, /* A message number */
47 TDASH
= 2, /* A simple dash */
48 TSTRING
= 3, /* A string (possibly containing -) */
51 TDOLLAR
= 6, /* A "$" */
52 TSTAR
= 7, /* A "*" */
53 TOPEN
= 8, /* A '(' */
54 TCLOSE
= 9, /* A ')' */
55 TPLUS
= 10, /* A '+' */
56 TERROR
= 11, /* A lexical error */
57 TCOMMA
= 12, /* A ',' */
58 TSEMI
= 13, /* A ';' */
59 TBACK
= 14 /* A '`' */
62 #define REGDEP 2 /* Maximum regret depth. */
70 CMNEW
= 1<<0, /* New messages */
71 CMOLD
= 1<<1, /* Old messages */
72 CMUNREAD
= 1<<2, /* Unread messages */
73 CMDELETED
=1<<3, /* Deleted messages */
74 CMREAD
= 1<<4, /* Read messages */
75 CMFLAG
= 1<<5, /* Flagged messages */
76 CMANSWER
= 1<<6, /* Answered messages */
77 CMDRAFT
= 1<<7, /* Draft messages */
78 CMSPAM
= 1<<8, /* Spam messages */
79 CMSPAMUN
= 1<<9 /* Maybe spam messages (unsure) */
83 char co_char
; /* What to find past : */
84 int co_bit
; /* Associated modifier bit */
85 int co_mask
; /* m_status bits to mask */
86 int co_equal
; /* ... must equal this */
94 static struct coltab
const _coltab
[] = {
95 { 'n', CMNEW
, MNEW
, MNEW
},
96 { 'o', CMOLD
, MNEW
, 0 },
97 { 'u', CMUNREAD
, MREAD
, 0 },
98 { 'd', CMDELETED
, MDELETED
, MDELETED
},
99 { 'r', CMREAD
, MREAD
, MREAD
},
100 { 'f', CMFLAG
, MFLAGGED
, MFLAGGED
},
101 { 'a', CMANSWER
, MANSWERED
, MANSWERED
},
102 { 't', CMDRAFT
, MDRAFTED
, MDRAFTED
},
103 { 's', CMSPAM
, MSPAM
, MSPAM
},
104 { 'S', CMSPAMUN
, MSPAMUNSURE
, MSPAMUNSURE
},
108 static struct lex
const _singles
[] = {
123 /* Slots in ::message */
124 static size_t _message_space
;
126 static bool_t _list_saw_d
, _list_last_saw_d
; /* :d on its way HACK TODO */
127 static size_t STRINGLEN
;
128 static int lexnumber
; /* Number of TNUMBER from scan() */
129 static char *lexstring
; /* String from TSTRING, scan() */
130 static int regretp
; /* Pointer to TOS of regret tokens */
131 static int regretstack
[REGDEP
]; /* Stack of regretted tokens */
132 static char *string_stack
[REGDEP
]; /* Stack of regretted strings */
133 static int numberstack
[REGDEP
]; /* Stack of regretted numbers */
134 static int threadflag
; /* mark entire threads */
136 static enum okay
get_header(struct message
*mp
);
138 /* Append, taking care of resizes */
139 static char ** add_to_namelist(char ***namelist
, size_t *nmlsize
,
140 char **np
, char *string
);
142 /* Mark all messages that the user wanted from the command line in the message
143 * structure. Return 0 on success, -1 on error */
144 static int markall(char *buf
, int f
);
146 /* Turn the character after a colon modifier into a bit value */
147 static int evalcol(int col
);
149 /* Check the passed message number for legality and proper flags. If f is
150 * MDELETED, then either kind will do. Otherwise, the message has to be
152 static int check(int mesg
, int f
);
154 /* Scan out a single lexical item and return its token number, updating the
155 * string pointer passed **sp. Also, store the value of the number or string
156 * scanned in lexnumber or lexstring as appropriate. In any event, store the
157 * scanned "thing" in lexstring */
158 static int scan(char **sp
);
160 /* Unscan the named token by pushing it onto the regret stack */
161 static void regret(int token
);
163 /* Reset all the scanner global variables */
164 static void scaninit(void);
166 /* See if the passed name sent the passed message */
167 static bool_t
_matchsender(struct message
*mp
, char const *str
, bool_t allnet
);
169 static bool_t
_matchmid(struct message
*mp
, char *id
, enum idfield idfield
);
171 /* See if the given string matches.
172 * For the purpose of the scan, we ignore case differences.
173 * This is the engine behind the "/" search */
174 static bool_t
_match_dash(struct message
*mp
, char const *str
);
176 /* See if the given search expression matches.
177 * For the purpose of the scan, we ignore case differences.
178 * This is the engine behind the "@[..]@" search */
179 static bool_t
_match_at(struct message
*mp
, struct search_expr
*sep
);
181 /* Unmark the named message */
182 static void unmark(int mesg
);
184 /* Return the message number corresponding to the passed meta character */
185 static int metamess(int meta
, int f
);
188 get_header(struct message
*mp
)
194 switch (mb
.mb_type
) {
201 rv
= pop3_header(mp
);
214 add_to_namelist(char ***namelist
, size_t *nmlsize
, char **np
, char *string
)
219 if ((idx
= PTR2SIZE(np
- *namelist
)) >= *nmlsize
) {
220 *namelist
= srealloc(*namelist
, (*nmlsize
+= 8) * sizeof *np
);
221 np
= *namelist
+ idx
;
229 markall(char *buf
, int f
)
231 #define markall_ret(i) do { rv = i; goto jleave; } while (0);
233 /* TODO use a bit carrier for all the states */
234 char **np
, **nq
, **namelist
, *bufp
, *id
= NULL
, *cp
;
235 int rv
= 0, i
, tok
, beg
, other
, valdot
, colmod
, colresult
;
236 struct message
*mp
, *mx
;
237 bool_t mc
, star
, topen
, tback
;
239 enum idfield idfield
= ID_REFERENCES
;
242 lexstring
= ac_alloc(STRINGLEN
= 2 * strlen(buf
) +1);
243 valdot
= (int)PTR2SIZE(dot
- message
+ 1);
246 for (i
= 1; i
<= msgCount
; ++i
) {
249 mf
= message
[i
- 1].m_flag
;
255 message
[i
- 1].m_flag
= mf
;
258 np
= namelist
= smalloc((nmlsize
= 8) * sizeof *namelist
);
262 beg
= star
= other
= topen
= tback
= FAL0
;
264 for (tok
= scan(&bufp
); tok
!= TEOL
;) {
269 n_err(_("No numbers mixed with *\n"));
272 pstate
|= PS_MSGLIST_SAW_NO
;
275 if (check(lexnumber
, f
))
278 while (mb
.mb_threaded
? 1 : i
<= lexnumber
) {
279 if (!(message
[i
- 1].m_flag
& MHIDDEN
) &&
280 (f
== MDELETED
|| !(message
[i
- 1].m_flag
& MDELETED
)))
282 if (mb
.mb_threaded
) {
285 mx
= next_in_thread(&message
[i
- 1]);
288 i
= (int)PTR2SIZE(mx
- message
+ 1);
307 pstate
&= ~PS_MSGLIST_DIRECT
;
309 printf(_("Non-numeric second argument\n"));
314 if (mb
.mb_threaded
) {
315 mx
= next_in_thread(message
+ i
- 1);
316 i
= mx
? (int)PTR2SIZE(mx
- message
+ 1) : msgCount
+ 1;
320 n_err(_("Referencing beyond EOF\n"));
323 } while (message
[i
- 1].m_flag
== MHIDDEN
||
324 (message
[i
- 1].m_flag
& MDELETED
) != (unsigned)f
);
329 pstate
&= ~PS_MSGLIST_DIRECT
;
333 if (mb
.mb_threaded
) {
334 mx
= prev_in_thread(message
+ i
- 1);
335 i
= mx
? (int)PTR2SIZE(mx
- message
+ 1) : 0;
339 n_err(_("Referencing before 1\n"));
342 } while ((message
[i
- 1].m_flag
& MHIDDEN
) ||
343 (message
[i
- 1].m_flag
& MDELETED
) != (unsigned)f
);
349 pstate
&= ~PS_MSGLIST_DIRECT
;
351 n_err(_("Non-numeric second argument\n"));
355 if ((cp
= lexstring
)[0] == ':') {
356 while (*++cp
!= '\0') {
357 colresult
= evalcol(*cp
);
358 if (colresult
== 0) {
359 n_err(_("Unknown colon modifier \"%s\"\n"), lexstring
);
362 if (colresult
== CMDELETED
) {
369 np
= add_to_namelist(&namelist
, &nmlsize
, np
, savestr(lexstring
));
373 #ifdef HAVE_IMAP_SEARCH
374 pstate
&= ~PS_MSGLIST_DIRECT
;
375 if (imap_search(lexstring
, f
) == STOP
)
379 n_err(_("Optional selector is not available: \"%s\"\n"),
389 pstate
&= ~PS_MSGLIST_DIRECT
;
390 lexnumber
= metamess(lexstring
[0], f
);
396 pstate
&= ~PS_MSGLIST_DIRECT
;
398 for (i
= 1; i
<= msgCount
; i
++) {
399 struct message
const *mp_t
= message
+ i
- 1;
401 if (mp_t
->m_flag
& MHIDDEN
)
403 if ((mp_t
->m_flag
& MDELETED
) != (unsigned)f
) {
404 if (!_list_last_saw_d
)
408 if (mp_t
->m_flag
& MOLDMARK
)
414 pstate
&= ~PS_MSGLIST_DIRECT
;
416 n_err(_("Can't mix \"*\" with anything\n"));
423 pstate
&= ~PS_MSGLIST_DIRECT
;
424 if (id
== NULL
&& (cp
= hfield1("in-reply-to", dot
)) != NULL
) {
426 idfield
= ID_IN_REPLY_TO
;
428 if (id
== NULL
&& (cp
= hfield1("references", dot
)) != NULL
) {
431 if ((enp
= extract(cp
, GREF
)) != NULL
) {
432 while (enp
->n_flink
!= NULL
)
434 id
= savestr(enp
->n_name
);
435 idfield
= ID_REFERENCES
;
440 "Cannot determine parent Message-ID of the current message\n"));
446 pstate
&= ~PS_MSGLIST_DIRECT
;
447 pstate
|= PS_MSGLIST_SAW_NO
;
454 np
= add_to_namelist(&namelist
, &nmlsize
, np
, NULL
);
458 for (i
= 0; i
< msgCount
; ++i
) {
459 struct message
const *mp_t
= message
+ i
;
461 if (mp_t
->m_flag
& MHIDDEN
)
463 if (!_list_saw_d
&& (mp_t
->m_flag
& MDELETED
) != (unsigned)f
)
470 if (!(pstate
& PS_HOOK_MASK
))
471 printf(_("No applicable messages.\n"));
477 if ((topen
|| tback
) && !mc
) {
478 for (i
= 0; i
< msgCount
; ++i
)
479 if (message
[i
].m_flag
& MMARK
)
482 if (!(pstate
& PS_HOOK_MASK
)) {
484 n_err(_("No previously marked messages\n"));
486 printf(_("No messages satisfy (criteria)\n"));
492 /* If no numbers were given, mark all messages, so that we can unmark
493 * any whose sender was not selected if any user names were given */
494 if ((np
> namelist
|| colmod
!= 0 || id
) && !mc
) {
495 for (i
= 1; i
<= msgCount
; ++i
) {
496 if (!(message
[i
- 1].m_flag
& MHIDDEN
) &&
497 (message
[i
- 1].m_flag
& MDELETED
) == (unsigned)f
)
502 /* If any names were given, eliminate any messages which don't match */
503 if (np
> namelist
|| id
) {
504 struct search_expr
*sep
= NULL
;
507 /* The @ search works with struct search_expr, so build an array.
508 * To simplify array, i.e., regex_t destruction, and optimize for the
509 * common case we walk the entire array even in case of errors */
511 sep
= scalloc(PTR2SIZE(np
- namelist
), sizeof(*sep
));
512 for (j
= 0, nq
= namelist
; *nq
!= NULL
; ++j
, ++nq
) {
516 if (*x
!= '@' || rv
< 0)
519 for (y
= x
+ 1;; ++y
) {
520 if (*y
== '\0' || !fieldnamechar(*y
)) {
529 sep
[j
].ss_where
= (x
== NULL
|| x
- 1 == *nq
)
530 ? "subject" : savestrbuf(*nq
+ 1, PTR2SIZE(x
- *nq
) - 1);
532 x
= (x
== NULL
? *nq
: x
) + 1;
533 if (*x
== '\0') { /* XXX Simply remove from list instead? */
534 n_err(_("Empty \"[@..]@\" search expression\n"));
539 if (is_maybe_regex(x
)) {
540 sep
[j
].ss_sexpr
= NULL
;
541 if (regcomp(&sep
[j
].ss_regex
, x
,
542 REG_EXTENDED
| REG_ICASE
| REG_NOSUB
) != 0) {
543 n_err(_("Invalid regular expression: >>> %s <<<\n"), x
);
552 goto jnamesearch_sepfree
;
556 allnet
= ok_blook(allnet
);
557 for (i
= 1; i
<= msgCount
; ++i
) {
558 mp
= message
+ i
- 1;
561 for (nq
= namelist
; *nq
!= NULL
; ++nq
) {
563 if (_match_at(mp
, sep
+ PTR2SIZE(nq
- namelist
))) {
567 } else if (**nq
== '/') {
568 if (_match_dash(mp
, *nq
)) {
572 } else if (_matchsender(mp
, *nq
, allnet
)) {
578 if (j
== 0 && id
&& _matchmid(mp
, id
, idfield
))
581 mp
->m_flag
&= ~MMARK
;
586 /* Make sure we got some decent messages */
588 for (i
= 1; i
<= msgCount
; ++i
)
589 if (message
[i
- 1].m_flag
& MMARK
) {
594 if (!(pstate
& PS_HOOK_MASK
) && np
> namelist
) {
595 printf(_("No applicable messages from {%s"), namelist
[0]);
596 for (nq
= namelist
+ 1; *nq
!= NULL
; ++nq
)
597 printf(_(", %s"), *nq
);
600 printf(_("Parent message not found\n"));
602 goto jnamesearch_sepfree
;
608 for (j
= PTR2SIZE(np
- namelist
); j
-- != 0;)
609 if (sep
[j
].ss_sexpr
== NULL
)
610 regfree(&sep
[j
].ss_regex
);
618 /* If any colon modifiers were given, go through and unmark any
619 * messages which do not satisfy the modifiers */
621 for (i
= 1; i
<= msgCount
; ++i
) {
622 struct coltab
const *colp
;
625 mp
= message
+ i
- 1;
626 for (colp
= _coltab
; colp
->co_char
!= '\0'; ++colp
)
627 if ((colp
->co_bit
& colmod
) &&
628 ((mp
->m_flag
& colp
->co_mask
) == (unsigned)colp
->co_equal
)) {
636 for (mp
= message
; PTRCMP(mp
, <, message
+ msgCount
); ++mp
)
637 if (mp
->m_flag
& MMARK
)
640 if (PTRCMP(mp
, >=, message
+ msgCount
)) {
641 struct coltab
const *colp
;
643 if (!(pstate
& PS_HOOK_MASK
)) {
644 printf(_("No messages satisfy"));
645 for (colp
= _coltab
; colp
->co_char
!= '\0'; ++colp
)
646 if (colp
->co_bit
& colmod
)
647 printf(" :%c", colp
->co_char
);
667 struct coltab
const *colp
;
672 for (colp
= _coltab
; colp
->co_char
!= '\0'; ++colp
)
673 if (colp
->co_char
== col
) {
682 check(int mesg
, int f
)
687 if (mesg
< 1 || mesg
> msgCount
) {
688 printf(_("%d: Invalid message number\n"), mesg
);
691 mp
= message
+ mesg
- 1;
692 if (mp
->m_flag
& MHIDDEN
||
693 (f
!= MDELETED
&& (mp
->m_flag
& MDELETED
) != 0)) {
694 n_err(_("%d: inappropriate message\n"), mesg
);
710 int rv
, c
, inquote
, quotec
;
711 struct lex
const *lp
;
715 strncpy(lexstring
, string_stack
[regretp
], STRINGLEN
);
716 lexstring
[STRINGLEN
-1] = '\0';
717 lexnumber
= numberstack
[regretp
];
718 rv
= regretstack
[regretp
--];
726 /* strip away leading white space */
730 /* If no characters remain, we are at end of line, so report that */
737 /* Select members of a message thread */
740 if (*cp
== '\0' || spacechar(*cp
)) {
750 /* If the leading character is a digit, scan the number and convert it
751 * on the fly. Return TNUMBER when done */
754 while (digitchar(c
)) {
755 lexnumber
= lexnumber
*10 + c
- '0';
765 /* An IMAP SEARCH list. Note that TOPEN has always been included in
766 * singles[] in Mail and mailx. Thus although there is no formal
767 * definition for (LIST) lists, they do not collide with historical
768 * practice because a subject string (LIST) could never been matched
775 if ((c
= *cp
++&0377) == '\0') {
777 n_err(_("Missing \")\"\n"));
781 if (inquote
&& c
== '\\') {
794 else if (spacechar(c
)) {
795 /* Replace unquoted whitespace by single space characters, to make
796 * the string IMAP SEARCH conformant */
802 } while (c
!= ')' || level
> 0);
809 /* Check for single character tokens; return such if found */
810 for (lp
= _singles
; lp
->l_char
!= '\0'; ++lp
)
811 if (c
== lp
->l_char
) {
819 /* We've got a string! Copy all the characters of the string into
820 * lexstring, until we see a null, space, or tab. If the lead character is
821 * a " or ', save it and scan until you get another */
823 if (c
== '\'' || c
== '"') {
828 if (quotec
== 0 && c
== '\\' && *cp
!= '\0')
834 if (quotec
== 0 && blankchar(c
))
836 if (PTRCMP(cp2
- lexstring
, <, STRINGLEN
- 1))
840 if (quotec
&& c
== 0) {
841 n_err(_("Missing %c\n"), quotec
);
857 if (++regretp
>= REGDEP
)
858 n_panic(_("Too many regrets"));
859 regretstack
[regretp
] = token
;
860 lexstring
[STRINGLEN
-1] = '\0';
861 string_stack
[regretp
] = savestr(lexstring
);
862 numberstack
[regretp
] = lexnumber
;
876 _matchsender(struct message
*mp
, char const *str
, bool_t allnet
)
878 char const *str_base
, *np_base
, *np
;
883 /* Empty string doesn't match */
884 if (*(str_base
= str
) == '\0') {
889 /* *allnet* is POSIX and, since it explicitly mentions login and user names,
890 * most likely case-sensitive. XXX Still allow substr matching, though
891 * XXX possibly the first letter should be case-insensitive, then? */
893 np_base
= np
= nameof(mp
, 0);
895 if ((sc
= *str
++) == '@')
897 if ((nc
= *np
++) == '@' || nc
== '\0' || sc
== '\0')
906 char const *real_base
= name1(mp
, 0);
907 bool_t again
= ok_blook(showname
);
909 /* TODO POSIX says ~"match any address as shown in header overview",
910 * TODO but a normalized match would be more sane i guess.
911 * TODO struct name should gain a comparison method, normalize realname
912 * TODO content (in TODO) and thus match as likewise
913 * TODO "Buddy (Today) <here>" and "(Now) Buddy <here>" */
915 np_base
= np
= again
? realname(real_base
) : skin(real_base
);
918 if ((nc
= *np
++) == '\0' || sc
== '\0')
928 /* And really if i want to match 'on@' then i want it to match even if
929 * *showname* is set! */
930 if (!(rv
= (sc
== '\0')) && again
) {
941 _matchmid(struct message
*mp
, char *id
, enum idfield idfield
)
947 if ((cp
= hfield1("message-id", mp
)) != NULL
) {
950 rv
= !msgidcmp(id
, cp
);
952 case ID_IN_REPLY_TO
: {
955 if ((np
= extract(id
, GREF
)) != NULL
)
957 if (!msgidcmp(np
->n_name
, cp
)) {
961 } while ((np
= np
->n_flink
) != NULL
);
973 _match_dash(struct message
*mp
, char const *str
)
975 static char lastscan
[128];
978 char *hfield
, *hbody
;
982 if (*++str
== '\0') {
985 strncpy(lastscan
, str
, sizeof lastscan
); /* XXX use new n_str object! */
986 lastscan
[sizeof lastscan
-1] = '\0';
989 /* Now look, ignoring case, for the word in the string */
990 if (ok_blook(searchheaders
) && (hfield
= strchr(str
, ':'))) {
991 size_t l
= PTR2SIZE(hfield
- str
);
992 hfield
= ac_alloc(l
+1);
993 memcpy(hfield
, str
, l
);
995 hbody
= hfieldX(hfield
, mp
);
997 hfield
= UNCONST(str
+ l
+ 1);
999 hfield
= UNCONST(str
);
1000 hbody
= hfield1("subject", mp
);
1002 if (hbody
== NULL
) {
1008 in
.l
= strlen(hbody
);
1009 mime_fromhdr(&in
, &out
, TD_ICONV
);
1010 rv
= substr(out
.s
, hfield
);
1018 _match_at(struct message
*mp
, struct search_expr
*sep
)
1026 nfield
= savestr(sep
->ss_where
);
1028 while ((cfield
= n_strsep(&nfield
, ',', TRU1
)) != NULL
) {
1029 if (!asccasecmp(cfield
, "body") ||
1030 (cfield
[1] == '\0' && cfield
[0] == '>')) {
1033 if ((rv
= message_match(mp
, sep
, rv
)))
1037 if (!asccasecmp(cfield
, "text") ||
1038 (cfield
[1] == '\0' && cfield
[0] == '=')) {
1043 if (!asccasecmp(cfield
, "header") ||
1044 (cfield
[1] == '\0' && cfield
[0] == '<')) {
1045 if ((rv
= header_match(mp
, sep
)))
1050 /* This is not a special name, so take care for the "skin" prefix !
1051 * and possible abbreviations */
1056 if ((doskin
= (*cfield
== '~')))
1058 if (cfield
[0] != '\0' && cfield
[1] == '\0') {
1059 char const x
[][8] = {
1060 "from", "to", "cc", "bcc", "subject"
1063 char c1
= lowerconv(cfield
[0]);
1065 for (i
= 0; i
< NELEM(x
); ++i
) {
1066 if (c1
== x
[i
][0]) {
1072 if ((in
.s
= hfieldX(cfield
, mp
)) == NULL
)
1075 /* Shall we split into address list and match the addresses only? */
1077 np
= lextract(in
.s
, GSKIN
);
1083 in
.l
= strlen(in
.s
);
1084 mime_fromhdr(&in
, &out
, TD_ICONV
);
1088 if (sep
->ss_sexpr
== NULL
)
1089 rv
= (regexec(&sep
->ss_regex
, out
.s
, 0,NULL
, 0) != REG_NOMATCH
);
1092 rv
= substr(out
.s
, sep
->ss_sexpr
);
1097 if (np
!= NULL
&& (np
= np
->n_flink
) != NULL
) {
1114 if (i
< 1 || UICMP(z
, i
, >, msgCount
))
1115 n_panic(_("Bad message number to unmark"));
1116 message
[i
- 1].m_flag
&= ~MMARK
;
1121 metamess(int meta
, int f
)
1129 case '^': /* First 'good' message left */
1130 mp
= mb
.mb_threaded
? threadroot
: message
;
1131 while (PTRCMP(mp
, <, message
+ msgCount
)) {
1132 if (!(mp
->m_flag
& MHIDDEN
) && (mp
->m_flag
& MDELETED
) == (ui32_t
)f
) {
1133 c
= (int)PTR2SIZE(mp
- message
+ 1);
1136 if (mb
.mb_threaded
) {
1137 mp
= next_in_thread(mp
);
1143 if (!(pstate
& PS_HOOK_MASK
))
1144 printf(_("No applicable messages\n"));
1147 case '$': /* Last 'good message left */
1149 ? this_in_thread(threadroot
, -1) : message
+ msgCount
- 1;
1150 while (mp
>= message
) {
1151 if (!(mp
->m_flag
& MHIDDEN
) && (mp
->m_flag
& MDELETED
) == (ui32_t
)f
) {
1152 c
= (int)PTR2SIZE(mp
- message
+ 1);
1155 if (mb
.mb_threaded
) {
1156 mp
= prev_in_thread(mp
);
1162 if (!(pstate
& PS_HOOK_MASK
))
1163 printf(_("No applicable messages\n"));
1167 /* Current message */
1168 m
= dot
- message
+ 1;
1169 if ((dot
->m_flag
& MHIDDEN
) || (dot
->m_flag
& MDELETED
) != (ui32_t
)f
) {
1170 printf(_("%d: inappropriate message\n"), m
);
1177 /* Previously current message */
1178 if (prevdot
== NULL
) {
1179 n_err(_("No previously current message\n"));
1182 m
= prevdot
- message
+ 1;
1183 if ((prevdot
->m_flag
& MHIDDEN
) ||
1184 (prevdot
->m_flag
& MDELETED
) != (ui32_t
)f
) {
1185 n_err(_("%d: inappropriate message\n"), m
);
1192 n_err(_("Unknown metachar (%c)\n"), c
);
1204 setinput(struct mailbox
*mp
, struct message
*m
, enum needspec need
)
1207 enum okay ok
= STOP
;
1212 ok
= (m
->m_have
& HAVE_HEADER
) ? OKAY
: get_header(m
);
1215 ok
= (m
->m_have
& HAVE_BODY
) ? OKAY
: get_body(m
);
1225 if (fseek(mp
->mb_itf
, (long)mailx_positionof(m
->m_block
, m
->m_offset
),
1227 n_perr(_("fseek"), 0);
1228 n_panic(_("temporary file seek"));
1237 get_body(struct message
*mp
)
1243 switch (mb
.mb_type
) {
1266 if (message
!= NULL
) {
1276 message_append(struct message
*mp
)
1279 if (UICMP(z
, msgCount
+ 1, >=, _message_space
)) {
1280 /* XXX remove _message_space magics (or use s_Vector) */
1281 _message_space
= (_message_space
>= 128 && _message_space
<= 1000000)
1282 ? _message_space
<< 1 : _message_space
+ 64;
1283 message
= srealloc(message
, _message_space
* sizeof *message
);
1287 message
[msgCount
- 1] = *mp
;
1289 memset(message
+ msgCount
- 1, 0, sizeof *message
);
1295 message_append_null(void)
1299 message_append(NULL
);
1301 message
[msgCount
].m_size
= 0;
1302 message
[msgCount
].m_lines
= 0;
1307 message_match(struct message
*mp
, struct search_expr
const *sep
,
1308 bool_t with_headers
)
1311 size_t *linesize
, cnt
;
1316 if ((fp
= Ftmp(NULL
, "mpmatch", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) == NULL
)
1319 if (sendmp(mp
, fp
, NULL
, NULL
, SEND_TOSRCH
, NULL
) < 0)
1324 line
= &termios_state
.ts_linebuf
; /* XXX line pool */
1325 linesize
= &termios_state
.ts_linesize
; /* XXX line pool */
1328 while (fgetline(line
, linesize
, &cnt
, NULL
, fp
, 0))
1332 while (fgetline(line
, linesize
, &cnt
, NULL
, fp
, 0)) {
1334 if (sep
->ss_sexpr
== NULL
) {
1335 if (regexec(&sep
->ss_regex
, *line
, 0,NULL
, 0) == REG_NOMATCH
)
1339 if (!substr(*line
, sep
->ss_sexpr
))
1353 setdot(struct message
*mp
)
1358 pstate
&= ~PS_DID_PRINT_DOT
;
1361 uncollapse1(dot
, 0);
1367 touch(struct message
*mp
)
1370 mp
->m_flag
|= MTOUCH
;
1371 if (!(mp
->m_flag
& MREAD
))
1372 mp
->m_flag
|= MREAD
| MSTATUS
;
1377 getmsglist(char *buf
, int *vector
, int flags
)
1383 pstate
&= ~PS_ARGLIST_MASK
;
1384 _list_last_saw_d
= _list_saw_d
;
1387 if (msgCount
== 0) {
1393 pstate
|= PS_MSGLIST_DIRECT
;
1395 if (markall(buf
, flags
) < 0) {
1401 if (pstate
& PS_HOOK_NEWMAIL
) {
1403 for (mp
= message
; PTRCMP(mp
, <, message
+ msgCount
); ++mp
)
1404 if (mp
->m_flag
& MMARK
) {
1405 if (!(mp
->m_flag
& MNEWEST
))
1406 unmark((int)PTR2SIZE(mp
- message
+ 1));
1416 if (mb
.mb_threaded
== 0) {
1417 for (mp
= message
; PTRCMP(mp
, <, message
+ msgCount
); ++mp
)
1418 if (mp
->m_flag
& MMARK
)
1419 *ip
++ = (int)PTR2SIZE(mp
- message
+ 1);
1421 for (mp
= threadroot
; mp
!= NULL
; mp
= next_in_thread(mp
))
1422 if (mp
->m_flag
& MMARK
)
1423 *ip
++ = (int)PTR2SIZE(mp
- message
+ 1);
1426 mc
= (int)PTR2SIZE(ip
- vector
);
1428 pstate
&= ~PS_MSGLIST_DIRECT
;
1441 if (msgCount
== 0) {
1449 mb
.mb_threaded
? (mp
!= NULL
) : PTRCMP(mp
, <, message
+ msgCount
);
1450 mb
.mb_threaded
? (mp
= next_in_thread(mp
)) : ++mp
) {
1451 if (!(mp
->m_flag
& MHIDDEN
) && (mp
->m_flag
& m
) == (ui32_t
)f
) {
1452 rv
= (int)PTR2SIZE(mp
- message
+ 1);
1457 if (dot
> message
) {
1458 for (mp
= dot
- 1; (mb
.mb_threaded
? (mp
!= NULL
) : (mp
>= message
));
1459 mb
.mb_threaded
? (mp
= prev_in_thread(mp
)) : --mp
) {
1460 if (!(mp
->m_flag
& MHIDDEN
) && (mp
->m_flag
& m
) == (ui32_t
)f
) {
1461 rv
= (int)PTR2SIZE(mp
- message
+ 1);
1473 mark(int mesg
, int f
)
1480 if (i
< 1 || i
> msgCount
)
1481 n_panic(_("Bad message number to mark"));
1483 if (mb
.mb_threaded
== 1 && threadflag
) {
1484 struct message
*mp_t
= message
+ i
- 1;
1486 if (!(mp_t
->m_flag
& MHIDDEN
)) {
1487 if (f
== MDELETED
|| !(mp_t
->m_flag
& MDELETED
) || _list_saw_d
)
1488 mp_t
->m_flag
|= MMARK
;
1491 if (mp_t
->m_child
!= NULL
) {
1493 mark((int)PTR2SIZE(mp
- message
+ 1), f
);
1494 for (mp
= mp
->m_younger
; mp
!= NULL
; mp
= mp
->m_younger
)
1495 mark((int)PTR2SIZE(mp
- message
+ 1), f
);
1498 message
[i
- 1].m_flag
|= MMARK
;