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>.
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
40 #ifndef HAVE_AMALGAMATION
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 */
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 */
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
},
86 static struct lex
const _singles
[] = {
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
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
);
157 add_to_namelist(char ***namelist
, size_t *nmlsize
, char **np
, char *string
)
162 if ((idx
= PTR2SIZE(np
- *namelist
)) >= *nmlsize
) {
163 *namelist
= srealloc(*namelist
, (*nmlsize
+= 8) * sizeof *np
);
164 np
= &(*namelist
)[idx
];
172 markall(char *buf
, int f
)
174 #define markall_ret(i) do { rv = i; goto jleave; } while (0);
178 struct message
*mp
, *mx
;
179 char **namelist
, *bufp
, *id
= NULL
, *cp
;
180 int tok
, beg
, mc
, star
, other
, valdot
, colmod
, colresult
, topen
, tback
;
182 enum idfield idfield
= ID_REFERENCES
;
188 lexstring
= ac_alloc(STRINGLEN
= 2 * strlen(buf
) + 1);
189 valdot
= (int)PTR2SIZE(dot
- message
+ 1);
192 for (i
= 1; i
<= msgCount
; ++i
) {
195 mf
= message
[i
- 1].m_flag
;
201 message
[i
- 1].m_flag
= mf
;
204 namelist
= smalloc((nmlsize
= 8) * sizeof *namelist
);
208 beg
= mc
= star
= other
= topen
= tback
= 0;
213 for (tok
= scan(&bufp
); tok
!= TEOL
;) {
218 fprintf(stderr
, tr(112, "No numbers mixed with *\n"));
221 list_saw_numbers
= TRU1
;
225 if (check(lexnumber
, f
))
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
)))
232 if (mb
.mb_threaded
) {
235 mx
= next_in_thread(&message
[i
- 1]);
238 i
= (int)PTR2SIZE(mx
- message
+ 1);
257 msglist_is_single
= FAL0
;
259 printf(tr(113, "Non-numeric second argument\n"));
264 if (mb
.mb_threaded
) {
265 mx
= next_in_thread(&message
[i
- 1]);
266 i
= mx
? (int)PTR2SIZE(mx
- message
+ 1) : msgCount
+ 1;
270 fprintf(stderr
, tr(114, "Referencing beyond EOF\n"));
273 } while (message
[i
-1].m_flag
== MHIDDEN
||
274 (message
[i
- 1].m_flag
& MDELETED
) != (unsigned)f
);
279 msglist_is_single
= FAL0
;
283 if (mb
.mb_threaded
) {
284 mx
= prev_in_thread(&message
[i
- 1]);
285 i
= mx
? (int)PTR2SIZE(mx
- message
+ 1) : 0;
289 fprintf(stderr
, tr(115, "Referencing before 1\n"));
292 } while ((message
[i
- 1].m_flag
& MHIDDEN
) ||
293 (message
[i
- 1].m_flag
& MDELETED
) != (unsigned)f
);
299 msglist_is_single
= FAL0
;
301 fprintf(stderr
, tr(116, "Non-numeric second argument\n"));
305 if (lexstring
[0] == ':') {
306 colresult
= evalcol(lexstring
[1]);
307 if (colresult
== 0) {
308 fprintf(stderr
, tr(117, "Unknown colon modifier \"%s\"\n"),
315 np
= add_to_namelist(&namelist
, &nmlsize
, np
, savestr(lexstring
));
319 msglist_is_single
= FAL0
;
320 if (imap_search(lexstring
, f
) == STOP
) /* TODO IMAP_SEARCH->option */
329 msglist_is_single
= FAL0
;
330 lexnumber
= metamess(lexstring
[0], f
);
336 msglist_is_single
= FAL0
;
338 for (i
= 1; i
<= msgCount
; i
++) {
339 if ((message
[i
- 1].m_flag
& MHIDDEN
) ||
340 (message
[i
- 1].m_flag
& MDELETED
) != (unsigned)f
)
342 if (message
[i
- 1].m_flag
& MOLDMARK
)
348 msglist_is_single
= FAL0
;
350 fprintf(stderr
, tr(118, "Can't mix \"*\" with anything\n"));
357 msglist_is_single
= FAL0
;
359 if (mb
.mb_type
== MB_IMAP
&& gotheaders
++ == 0)
360 imap_getheaders(1, msgCount
);
362 if (id
== NULL
&& (cp
= hfield1("in-reply-to", dot
)) != NULL
) {
364 idfield
= ID_IN_REPLY_TO
;
366 if (id
== NULL
&& (cp
= hfield1("references", dot
)) != NULL
) {
369 if ((enp
= extract(cp
, GREF
)) != NULL
) {
370 while (enp
->n_flink
!= NULL
)
372 id
= savestr(enp
->n_name
);
373 idfield
= ID_REFERENCES
;
378 "Cannot determine parent Message-ID of the current message\n"));
384 list_saw_numbers
= TRU1
;
385 msglist_is_single
= FAL0
;
393 np
= add_to_namelist(&namelist
, &nmlsize
, np
, NULL
);
397 for (i
= 0; i
< msgCount
; ++i
) {
398 if (!(message
[i
].m_flag
& MHIDDEN
) &&
399 (message
[i
].m_flag
& MDELETED
) == (unsigned)f
) {
406 printf(tr(119, "No applicable messages.\n"));
412 if ((topen
|| tback
) && mc
== 0) {
413 for (i
= 0; i
< msgCount
; ++i
)
414 if (message
[i
].m_flag
& MMARK
)
419 fprintf(stderr
, tr(131, "No previously marked messages.\n"));
421 printf("No messages satisfy (criteria).\n");/*TODO tr*/
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
)
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
);
442 if (mb
.mb_type
== MB_IMAP
&& gotheaders
++ == 0)
443 imap_getheaders(1, msgCount
);
446 for (i
= 1; i
<= msgCount
; ++i
) {
449 for (nq
= namelist
; *nq
!= NULL
; ++nq
)
451 if (matchsubj(*nq
, i
)) {
455 } else if (matchsender(*nq
, i
, allnet
)) {
460 if (mc
== 0 && id
&& matchmid(id
, idfield
, i
))
468 /* Make sure we got some decent messages */
470 for (i
= 1; i
<= msgCount
; ++i
)
471 if (message
[i
- 1].m_flag
& MMARK
) {
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"));
482 printf(tr(227, "Parent message not found\n"));
487 /* If any colon modifiers were given, go through and unmark any
488 * messages which do not satisfy the modifiers */
490 for (i
= 1; i
<= msgCount
; ++i
) {
491 struct coltab
const *colp
;
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
))
502 for (mp
= message
; PTRCMP(mp
, <, message
+ msgCount
); ++mp
)
503 if (mp
->m_flag
& MMARK
)
505 if (PTRCMP(mp
, >=, message
+ msgCount
)) {
506 struct coltab
const *colp
;
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
);
532 struct coltab
const *colp
;
540 for (colp
= _coltab
; colp
->co_char
!= '\0'; ++colp
)
541 if (colp
->co_char
== col
) {
551 check(int mesg
, int f
)
556 if (mesg
< 1 || mesg
> msgCount
) {
557 printf(tr(124, "%d: Invalid message number\n"), mesg
);
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
);
579 int rv
, c
, inquote
, quotec
;
580 struct lex
const *lp
;
584 strncpy(lexstring
, string_stack
[regretp
], STRINGLEN
);
585 lexstring
[STRINGLEN
-1]='\0';
586 lexnumber
= numberstack
[regretp
];
587 rv
= regretstack
[regretp
--];
595 /* strip away leading white space */
599 /* If no characters remain, we are at end of line, so report that */
606 /* Select members of a message thread */
609 if (*cp
== '\0' || spacechar(*cp
)) {
619 /* If the leading character is a digit, scan the number and convert it
620 * on the fly. Return TNUMBER when done */
623 while (digitchar(c
)) {
624 lexnumber
= lexnumber
*10 + c
- '0';
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
644 if ((c
= *cp
++&0377) == '\0') {
646 fprintf(stderr
, "Missing \")\".\n");
650 if (inquote
&& c
== '\\') {
663 else if (spacechar(c
)) {
664 /* Replace unquoted whitespace by single space characters, to make
665 * the string IMAP SEARCH conformant */
671 } while (c
!= ')' || level
> 0);
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
) {
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 */
692 if (c
== '\'' || c
== '"') {
697 if (quotec
== 0 && c
== '\\' && *cp
)
703 if (quotec
== 0 && blankchar(c
))
705 if (PTRCMP(cp2
- lexstring
, <, STRINGLEN
- 1))
709 if (quotec
&& c
== 0) {
710 fprintf(stderr
, tr(127, "Missing %c\n"), quotec
);
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
;
745 matchsender(char *str
, int mesg
, int allnet
)
751 char *cp
= nameof(&message
[mesg
- 1], 0);
754 if ((*cp
== '@' || *cp
== '\0') && (*str
== '@' || *str
== '\0')) {
760 } while (cp
++, *str
++ != '\0');
765 (ok_blook(showname
) ? realname
: skin
)(name1(&message
[mesg
- 1], 0)));
772 matchmid(char *id
, enum idfield idfield
, int mesg
)
779 if ((cp
= hfield1("message-id", &message
[mesg
- 1])) != NULL
) {
782 rv
= (msgidcmp(id
, cp
) == 0);
785 if ((np
= extract(id
, GREF
)) != NULL
)
787 if (msgidcmp(np
->n_name
, cp
) == 0) {
791 } while ((np
= np
->n_flink
) != NULL
);
802 matchsubj(char *str
, int mesg
) /* FIXME regex-enable; funbody=only matching!! */
804 static char lastscan
[128];
813 if (strlen(str
) == 0) {
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
, ':'))) {
825 cp2
= hfieldX(str
, mp
);
829 cp2
= hfield1("subject", mp
);
838 mime_fromhdr(&in
, &out
, TD_ICONV
);
839 i
= substr(out
.s
, cp
);
853 if (i
< 1 || UICMP(z
, i
, >, msgCount
))
854 panic(tr(130, "Bad message number to unmark"));
855 message
[i
- 1].m_flag
&= ~MMARK
;
860 metamess(int meta
, int f
)
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);
875 if (mb
.mb_threaded
) {
876 mp
= next_in_thread(mp
);
883 printf(tr(132, "No applicable messages\n"));
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);
894 if (mb
.mb_threaded
) {
895 mp
= prev_in_thread(mp
);
902 printf(tr(132, "No applicable messages\n"));
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
);
916 /* Previously current message */
917 if (prevdot
== NULL
) {
918 fprintf(stderr
, tr(228, "No previously current message\n"));
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
);
931 fprintf(stderr
, tr(134, "Unknown metachar (%c)\n"), c
);
943 getmsglist(char *buf
, int *vector
, int flags
)
950 msglist_is_single
= FAL0
;
958 msglist_is_single
= TRU1
;
959 if (markall(buf
, flags
) < 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));
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);
985 for (mp
= threadroot
; mp
!= NULL
; mp
= next_in_thread(mp
))
986 if (mp
->m_flag
& MMARK
)
987 *ip
++ = (int)PTR2SIZE(mp
- message
+ 1);
990 mc
= (int)PTR2SIZE(ip
- vector
);
991 msglist_is_single
= (mc
== 1);
998 getrawlist(char const *line
, size_t linesize
, char **argv
, int argc
,
1001 char c
, *cp2
, quotec
, *linebuf
;
1006 list_saw_numbers
= FAL0
;
1010 linebuf
= ac_alloc(linesize
+ 1);
1012 for (; blankchar(*cp
); ++cp
)
1016 if (argn
>= argc
- 1) {
1017 printf(tr(126, "Too many elements in the list; excess discarded.\n"));
1022 while ((c
= *cp
) != '\0') {
1024 if (quotec
!= '\0') {
1029 } else if (c
== '\\')
1030 switch (c
= *cp
++) {
1036 case '0': case '1': case '2': case '3':
1037 case '4': case '5': case '6': case '7':
1039 if (*cp >= '0' && *cp <= '7')
1040 c = c * 8 + *cp++ - '0';
1041 if (*cp >= '0' && *cp <= '7')
1042 c = c * 8 + *cp++ - '0';
1065 if (cp
[-1] != quotec
|| echolist
)
1069 /*else if (c == '^') {
1073 /\* null doesn't show up anyway *\/
1074 else if ((c >= 'A' && c <= '_') ||
1075 (c >= 'a' && c <= 'z'))
1083 } else if (c
== '"' || c
== '\'') {
1087 } else if (c
== '\\' && !echolist
) {
1092 } else if (blankchar(c
))
1098 argv
[argn
++] = savestr(linebuf
);
1113 if (msgCount
== 0) {
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);
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);
1145 mark(int mesg
, int f
)
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
);
1167 message
[i
- 1].m_flag
|= MMARK
;
1171 /* vim:set fenc=utf-8:s-it-mode */