1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Iterating over, and over such housekeeping message user commands.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
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 cmd_message
38 #ifndef HAVE_AMALGAMATION
42 /* Prepare and print "[Message: xy]:" intro */
43 static void _show_msg_overview(FILE *obuf
, struct message
*mp
, int msg_no
);
45 /* Show the requested messages */
46 static int _type1(int *msgvec
, bool_t doign
, bool_t dopage
, bool_t dopipe
,
47 bool_t donotdecode
, char *cmd
, ui64_t
*tstats
);
49 /* Pipe the requested messages */
50 static int _pipe1(char *str
, int doign
);
53 static int a_cmsg_top(void *vp
, struct n_ignore
const *itp
);
55 /* Delete the indicated messages. Set dot to some nice place afterwards */
56 static int delm(int *msgvec
);
59 _show_msg_overview(FILE *obuf
, struct message
*mp
, int msg_no
)
61 char const *cpre
, *csuf
;
64 cpre
= csuf
= n_empty
;
66 if (n_pstate
& n_PS_COLOUR_ACTIVE
) {
67 struct n_colour_pen
*cpen
;
69 if ((cpen
= n_colour_pen_create(n_COLOUR_ID_VIEW_MSGINFO
, NULL
)) != NULL
){
72 if ((sp
= n_colour_pen_to_str(cpen
)) != NULL
)
74 if ((sp
= n_colour_reset_to_str()) != NULL
)
79 /* XXX Message info uses wire format for line count */
80 fprintf(obuf
, _("%s[-- Message %2d -- %lu lines, %lu bytes --]:%s\n"),
81 cpre
, msg_no
, (ul_i
)mp
->m_lines
, (ul_i
)mp
->m_size
, csuf
);
86 _type1(int *msgvec
, bool_t doign
, bool_t dopage
, bool_t dopipe
,
87 bool_t donotdecode
, char *cmd
, ui64_t
*tstats
)
96 bool_t
volatile isrelax
= FAL0
;
99 enum sendaction
const action
= ((dopipe
&& ok_blook(piperaw
))
100 ? SEND_MBOX
: donotdecode
102 ? SEND_TODISP
: SEND_TODISP_ALL
);
103 bool_t
const volatile formfeed
= (dopipe
&& ok_blook(page
));
106 n_SIGMAN_ENTER_SWITCH(&sm
, n_SIGMAN_ALL
) {
114 if ((obuf
= Popen(cmd
, "w", ok_vlook(SHELL
), NULL
, 1)) == NULL
) {
118 } else if ((n_psonce
& n_PSO_TTYOUT
) && (dopage
||
119 ((n_psonce
& n_PSO_INTERACTIVE
) && (cp
= ok_vlook(crt
)) != NULL
))) {
125 for (ip
= msgvec
; *ip
&& PTRCMP(ip
- msgvec
, <, msgCount
); ++ip
) {
126 mp
= message
+ *ip
- 1;
127 if (!(mp
->m_content_info
& CI_HAVE_BODY
))
128 if (get_body(mp
) != OKAY
)
130 nlines
+= mp
->m_lines
+ 1; /* TODO BUT wire format, not display! */
134 /* >= not <: we return to the prompt */
135 if(dopage
|| nlines
>= (*cp
!= '\0'
136 ? (n_idec_uiz_cp(&lib
, cp
, 0, NULL
), lib
)
137 : (uiz_t
)n_realscreenheight
)){
138 if((obuf
= n_pager_open()) == NULL
)
142 if ((n_psonce
& n_PSO_INTERACTIVE
) &&
143 (action
== SEND_TODISP
|| action
== SEND_TODISP_ALL
))
144 n_colour_env_create(n_COLOUR_CTX_VIEW
, obuf
!= n_stdout
);
148 else if ((n_psonce
& n_PSO_INTERACTIVE
) &&
149 (action
== SEND_TODISP
|| action
== SEND_TODISP_ALL
))
150 n_colour_env_create(n_COLOUR_CTX_VIEW
, FAL0
);
153 /*TODO unless we have our signal manager special care must be taken */
156 for (ip
= msgvec
; *ip
&& PTRCMP(ip
- msgvec
, <, msgCount
); ++ip
) {
157 mp
= message
+ *ip
- 1;
160 n_pstate
|= n_PS_DID_PRINT_DOT
;
162 if (!dopipe
&& ip
!= msgvec
)
164 if (action
!= SEND_MBOX
)
165 _show_msg_overview(obuf
, mp
, *ip
);
166 sendmp(mp
, obuf
, (doign
? n_IGNORE_TYPE
: NULL
), NULL
, action
, mstats
);
168 if (formfeed
) /* TODO a nicer way to separate piped messages! */
171 tstats
[0] += mstats
[0];
178 n_sigman_cleanup_ping(&sm
);
182 n_COLOUR( n_colour_env_gut((sm
.sm_signo
!= SIGPIPE
) ? obuf
: NULL
); )
183 if (obuf
!= n_stdout
)
187 n_sigman_leave(&sm
, n_SIGMAN_VIPSIGS_NTTYOUT
);
192 _pipe1(char *str
, int doign
)
195 char const *cmd
, *cmdq
;
200 if ((cmd
= laststring(str
, &needs_list
, TRU1
)) == NULL
) {
202 if (cmd
== NULL
|| *cmd
== '\0') {
203 n_err(_("Variable *cmd* not set\n"));
208 msgvec
= salloc((msgCount
+ 2) * sizeof *msgvec
);
211 *msgvec
= first(0, MMNORM
);
213 if (n_pstate
& (n_PS_ROBOT
| n_PS_HOOK_MASK
)) {
217 fputs(_("No messages to pipe.\n"), n_stdout
);
221 } else if (getmsglist(str
, msgvec
, 0) < 0)
224 if (n_pstate
& (n_PS_ROBOT
| n_PS_HOOK_MASK
)) {
228 fprintf(n_stdout
, "No applicable messages.\n");
232 cmdq
= n_shexp_quote_cp(cmd
, FAL0
);
233 fprintf(n_stdout
, _("Pipe to: %s\n"), cmdq
);
235 if ((rv
= _type1(msgvec
, doign
, FAL0
, TRU1
, FAL0
, n_UNCONST(cmd
), stats
)
237 fprintf(n_stdout
, "%s %" PRIu64
" bytes\n", cmdq
, stats
[0]);
244 a_cmsg_top(void *vp
, struct n_ignore
const *itp
){
247 enum{a_NONE
, a_SQUEEZE
= 1u<<0,
248 a_EMPTY
= 1u<<8, a_STOP
= 1u<<9, a_WORKMASK
= 0xFF00u
} f
;
253 if((iobuf
= Ftmp(NULL
, "topio", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) == NULL
){
254 n_perr(_("`top': I/O temporary file"), 0);
258 if((pbuf
= Ftmp(NULL
, "toppag", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) == NULL
){
259 n_perr(_("`top': temporary pager file"), 0);
264 /* TODO In v15 we should query the m_message object, and directly send only
265 * TODO those parts, optionally over empty-line-squeeze and quote-strip
266 * TODO filters, in which we are interested in: only text content!
267 * TODO And: with *topsqueeze*, header/content separating empty line.. */
268 n_pstate
&= ~n_PS_MSGLIST_DIRECT
; /* TODO NO ATTACHMENTS */
272 if (n_psonce
& n_PSO_INTERACTIVE
)
273 n_colour_env_create(n_COLOUR_CTX_VIEW
, TRU1
);
275 n_string_creat_auto(&s
);
279 if((n_idec_siz_cp(&l
, ok_vlook(toplines
), 0, NULL
280 ) & (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
281 ) != n_IDEC_STATE_CONSUMED
)
284 tmax
= n_screensize();
292 f
= ok_blook(topsqueeze
) ? a_SQUEEZE
: a_NONE
;
294 for(ip
= msgvec
= vp
;
295 *ip
!= 0 && UICMP(z
, PTR2SIZE(ip
- msgvec
), <, msgCount
); ++ip
){
298 mp
= &message
[*ip
- 1];
301 n_pstate
|= n_PS_DID_PRINT_DOT
;
305 if(ftruncate(fileno(iobuf
), 0)){
306 n_perr(_("`top': ftruncate(2)"), 0);
310 if(sendmp(mp
, iobuf
, itp
, NULL
, SEND_TODISP_ALL
, NULL
) < 0){
311 n_err(_("`top': failed to prepare message %d\n"), *ip
);
315 fflush_rewind(iobuf
);
317 _show_msg_overview(pbuf
, mp
, *ip
);
322 n_string_trunc(&s
, 0);
323 for(l
= 0, f
&= ~a_WORKMASK
; !(f
& a_STOP
);){
326 if((c
= getc(iobuf
)) == EOF
){
332 n_string_push_c(&s
, c
);
333 else if((f
& a_SQUEEZE
) && s
.s_len
== 0){
334 if(!(f
& a_STOP
) && ((f
& a_EMPTY
) || tmax
- 1 <= l
))
336 if(putc('\n', pbuf
) == EOF
){
343 char const *cp
, *xcp
;
345 cp
= n_string_cp_const(&s
);
346 /* TODO Brute simple skip part overviews; see above.. */
349 else if(s
.s_len
> 8 &&
350 (xcp
= strstr(cp
, "[-- ")) != NULL
&&
351 strstr(&xcp
[1], " --]") != NULL
)
353 else for(; (c
= *cp
) != '\0'; ++cp
){
356 if(!blankspacechar(c
)){
365 if(fputs(n_string_cp_const(&s
), pbuf
) == EOF
||
366 putc('\n', pbuf
) == EOF
){
375 n_string_trunc(&s
, 0);
383 if(!(f
& a_EMPTY
) && putc('\n', pbuf
) == EOF
){
393 n_COLOUR( n_colour_env_gut(pbuf
); )
396 page_or_print(pbuf
, plines
);
410 int rv
= -1, *ip
, last
;
414 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
415 mp
= message
+ *ip
- 1;
417 mp
->m_flag
|= MDELETED
| MTOUCH
;
418 mp
->m_flag
&= ~(MPRESERVE
| MSAVED
| MBOX
);
422 setdot(message
+ last
- 1);
423 last
= first(0, MDELETED
);
425 setdot(message
+ last
- 1);
441 rv
= _type1(msgvec
, TRU1
, TRU1
, FAL0
, FAL0
, NULL
, NULL
);
452 rv
= _type1(msgvec
, FAL0
, TRU1
, FAL0
, FAL0
, NULL
, NULL
);
463 rv
= _type1(msgvec
, TRU1
, FAL0
, FAL0
, FAL0
, NULL
, NULL
);
474 rv
= _type1(msgvec
, FAL0
, FAL0
, FAL0
, FAL0
, NULL
, NULL
);
485 rv
= _type1(msgvec
, FAL0
, FAL0
, FAL0
, TRU1
, NULL
, NULL
);
516 struct n_ignore
*itp
;
520 if(n_ignore_is_any(n_IGNORE_TOP
))
523 itp
= n_ignore_new(TRU1
);
524 n_ignore_insert(itp
, TRU1
, "from", sizeof("from") -1);
525 n_ignore_insert(itp
, TRU1
, "to", sizeof("to") -1);
526 n_ignore_insert(itp
, TRU1
, "cc", sizeof("cc") -1);
527 n_ignore_insert(itp
, TRU1
, "subject", sizeof("subject") -1);
530 rv
= !a_cmsg_top(v
, itp
);
540 rv
= !a_cmsg_top(v
, n_IGNORE_TYPE
);
548 int list
[2], *ip
, *ip2
, mdot
, *msgvec
= v
, rv
= 1;
553 /* If some messages were supplied, find the first applicable one
554 * following dot using wrap around */
555 mdot
= (int)PTR2SIZE(dot
- message
+ 1);
557 /* Find first message in supplied message list which follows dot */
558 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
559 if ((mb
.mb_threaded
? message
[*ip
- 1].m_threadpos
> dot
->m_threadpos
567 mp
= message
+ *ip2
- 1;
568 if (!(mp
->m_flag
& MMNDEL
)) {
577 fprintf(n_stdout
, _("No messages applicable\n"));
581 /* If this is the first command, select message 1. Note that this must
582 * exist for us to get here at all */
583 if (!(n_pstate
& n_PS_SAW_COMMAND
)) {
589 /* Just find the next good message after dot, no wraparound */
590 if (mb
.mb_threaded
== 0) {
591 for (mp
= dot
+ !!(n_pstate
& n_PS_DID_PRINT_DOT
);
592 PTRCMP(mp
, <, message
+ msgCount
); ++mp
)
593 if (!(mp
->m_flag
& MMNORM
))
596 /* TODO The threading code had some bugs that caused crashes.
597 * TODO The last thing (before the deep look) happens here,
598 * TODO so let's not trust n_PS_DID_PRINT_DOT but check & hope it fixes */
599 if ((mp
= dot
) != NULL
&& (n_pstate
& n_PS_DID_PRINT_DOT
))
600 mp
= next_in_thread(mp
);
601 while (mp
!= NULL
&& (mp
->m_flag
& MMNORM
))
602 mp
= next_in_thread(mp
);
604 if (mp
== NULL
|| PTRCMP(mp
, >=, message
+ msgCount
)) {
606 fprintf(n_stdout
, _("At EOF\n"));
614 list
[0] = (int)PTR2SIZE(dot
- message
+ 1);
627 fprintf(n_stdout
, "%d\n", (int)PTR2SIZE(dot
- message
+ 1));
635 int *msgvec
= v
, *ip
, mesg
;
639 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
641 mp
= message
+ mesg
- 1;
642 fprintf(n_stdout
, "%d: ", mesg
);
643 if (mp
->m_xlines
> 0)
644 fprintf(n_stdout
, "%ld", mp
->m_xlines
);
647 fprintf(n_stdout
, "/%lu\n", (ul_i
)mp
->m_xsize
);
667 int list
[2], rv
= 0, *msgvec
= v
, lastdot
;
670 lastdot
= (int)PTR2SIZE(dot
- message
+ 1);
671 if (delm(msgvec
) >= 0) {
672 list
[0] = (int)PTR2SIZE(dot
- message
+ 1);
673 if (list
[0] > lastdot
) {
679 fprintf(n_stdout
, _("At EOF\n"));
681 fprintf(n_stdout
, _("No more messages\n"));
690 int *msgvec
= v
, *ip
;
694 for (ip
= msgvec
; *ip
!= 0 && UICMP(z
, PTR2SIZE(ip
- msgvec
), <, msgCount
);
696 mp
= message
+ *ip
- 1;
699 if (mp
->m_flag
& (MDELETED
| MSAVED
))
700 mp
->m_flag
&= ~(MDELETED
| MSAVED
);
702 mp
->m_flag
&= ~MDELETED
;
711 int *msgvec
= v
, *ip
;
714 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
715 setdot(message
+ *ip
- 1);
716 dot
->m_flag
|= MTOUCH
;
717 dot
->m_flag
&= ~MPRESERVE
;
718 n_pstate
|= n_PS_DID_PRINT_DOT
;
727 int *msgvec
= v
, *ip
;
730 if (n_pstate
& n_PS_EDIT
) {
731 n_err(_("`mbox' can only be used in a system mailbox\n")); /* TODO */
735 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
736 setdot(message
+ *ip
- 1);
737 dot
->m_flag
|= MTOUCH
| MBOX
;
738 dot
->m_flag
&= ~MPRESERVE
;
739 n_pstate
|= n_PS_DID_PRINT_DOT
;
749 int *msgvec
= v
, *ip
, mesg
, rv
= 1;
753 if (n_pstate
& n_PS_EDIT
) {
754 fprintf(n_stdout
, _("Cannot `preserve' in a system mailbox\n"));
758 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
760 mp
= message
+ mesg
- 1;
761 mp
->m_flag
|= MPRESERVE
;
764 n_pstate
|= n_PS_DID_PRINT_DOT
;
775 int *msgvec
= v
, *ip
;
778 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
779 setdot(message
+ *ip
- 1);
780 dot
->m_flag
&= ~(MREAD
| MTOUCH
);
781 dot
->m_flag
|= MSTATUS
;
782 n_pstate
|= n_PS_DID_PRINT_DOT
;
791 int *msgvec
= v
, *ip
;
794 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
795 struct message
*mp
= message
+ *ip
- 1;
807 int *msgvec
= v
, *ip
;
810 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
811 m
= message
+ *ip
- 1;
813 if (!(m
->m_flag
& (MFLAG
| MFLAGGED
)))
814 m
->m_flag
|= MFLAG
| MFLAGGED
;
824 int *msgvec
= v
, *ip
;
827 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
828 m
= message
+ *ip
- 1;
830 if (m
->m_flag
& (MFLAG
| MFLAGGED
)) {
831 m
->m_flag
&= ~(MFLAG
| MFLAGGED
);
832 m
->m_flag
|= MUNFLAG
;
843 int *msgvec
= v
, *ip
;
846 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
847 m
= message
+ *ip
- 1;
849 if (!(m
->m_flag
& (MANSWER
| MANSWERED
)))
850 m
->m_flag
|= MANSWER
| MANSWERED
;
857 c_unanswered(void *v
)
860 int *msgvec
= v
, *ip
;
863 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
864 m
= message
+ *ip
- 1;
866 if (m
->m_flag
& (MANSWER
| MANSWERED
)) {
867 m
->m_flag
&= ~(MANSWER
| MANSWERED
);
868 m
->m_flag
|= MUNANSWER
;
879 int *msgvec
= v
, *ip
;
882 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
883 m
= message
+ *ip
- 1;
885 if (!(m
->m_flag
& (MDRAFT
| MDRAFTED
)))
886 m
->m_flag
|= MDRAFT
| MDRAFTED
;
896 int *msgvec
= v
, *ip
;
899 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
900 m
= message
+ *ip
- 1;
902 if (m
->m_flag
& (MDRAFT
| MDRAFTED
)) {
903 m
->m_flag
&= ~(MDRAFT
| MDRAFTED
);
904 m
->m_flag
|= MUNDRAFT
;