1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2016 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
38 #ifndef HAVE_AMALGAMATION
44 /* Prepare and print "[Message: xy]:" intro */
45 static void _show_msg_overview(FILE *obuf
, struct message
*mp
, int msg_no
);
47 /* ... And place the extracted date in `date' */
48 static void _parse_from_(struct message
*mp
, char date
[FROM_DATEBUF
]);
50 /* Print out the header of a specific message
51 * __hprf: handle *headline*
52 * __subject: return -1 if Subject: yet seen, otherwise smalloc()d Subject:
53 * __putindent: print out the indenting in threaded display */
54 static void _print_head(size_t yetprinted
, size_t msgno
, FILE *f
,
56 static void __hprf(size_t yetprinted
, char const *fmt
, size_t msgno
,
57 FILE *f
, bool_t threaded
, char const *attrlist
);
58 static char * __subject(struct message
*mp
, bool_t threaded
,
60 static int __putindent(FILE *fp
, struct message
*mp
, int maxwidth
);
62 static int _dispc(struct message
*mp
, char const *a
);
64 /* Shared `z' implementation */
65 static int a_cmd_scroll(char const *arg
, bool_t onlynew
);
67 /* Shared `headers' implementation */
68 static int _headers(int msgspec
);
70 /* Show the requested messages */
71 static int _type1(int *msgvec
, bool_t doign
, bool_t dopage
, bool_t dopipe
,
72 bool_t dodecode
, char *cmd
, ui64_t
*tstats
);
74 /* Pipe the requested messages */
75 static int _pipe1(char *str
, int doign
);
78 static int a_cmd_top(void *vp
, struct ignoretab
*itp
);
81 _show_msg_overview(FILE *obuf
, struct message
*mp
, int msg_no
)
83 char const *cpre
, *csuf
;
86 cpre
= csuf
= n_empty
;
88 if (pstate
& PS_COLOUR_ACTIVE
) {
89 struct n_colour_pen
*cpen
;
91 if ((cpen
= n_colour_pen_create(n_COLOUR_ID_VIEW_MSGINFO
, NULL
)) != NULL
){
94 if ((sp
= n_colour_pen_to_str(cpen
)) != NULL
)
96 if ((sp
= n_colour_reset_to_str()) != NULL
)
101 fprintf(obuf
, _("%s[-- Message %2d -- %lu lines, %lu bytes --]:%s\n"),
102 cpre
, msg_no
, (ul_i
)mp
->m_lines
, (ul_i
)mp
->m_size
, csuf
);
107 _parse_from_(struct message
*mp
, char date
[FROM_DATEBUF
]) /* TODO line pool */
115 if ((ibuf
= setinput(&mb
, mp
, NEED_HEADER
)) != NULL
&&
116 (hlen
= readline_restart(ibuf
, &hline
, &hsize
, 0)) > 0)
117 extract_date_from_from_(hline
, hlen
, date
);
124 _print_head(size_t yetprinted
, size_t msgno
, FILE *f
, bool_t threaded
)
127 char attrlist
[attrlen
+1], *cp
;
131 if ((cp
= ok_vlook(attrlist
)) != NULL
) {
132 if (strlen(cp
) == attrlen
) {
133 memcpy(attrlist
, cp
, attrlen
+1);
136 n_err(_("*attrlist* is not of the correct length, using builtin\n"));
139 if (ok_blook(bsdcompat
) || ok_blook(bsdflags
)) {
140 char const bsdattr
[attrlen
+1] = "NU *HMFAT+-$~";
141 memcpy(attrlist
, bsdattr
, sizeof bsdattr
);
142 } else if (ok_blook(SYSV3
)) {
143 char const bsdattr
[attrlen
+1] = "NU *HMFAT+-$~";
144 memcpy(attrlist
, bsdattr
, sizeof bsdattr
);
145 OBSOLETE(_("*SYSV3*: please use *bsdcompat* or *bsdflags*, "
146 "or set *attrlist*"));
148 char const pattr
[attrlen
+1] = "NUROSPMFAT+-$~";
149 memcpy(attrlist
, pattr
, sizeof pattr
);
153 if ((fmt
= ok_vlook(headline
)) == NULL
) {
154 fmt
= ((ok_blook(bsdcompat
) || ok_blook(bsdheadline
))
155 ? "%>%a%m %-20f %16d %3l/%-5o %i%-S"
156 : "%>%a%m %-18f %16d %4l/%-5o %i%-s");
159 __hprf(yetprinted
, fmt
, msgno
, f
, threaded
, attrlist
);
164 __hprf(size_t yetprinted
, char const *fmt
, size_t msgno
, FILE *f
,
165 bool_t threaded
, char const *attrlist
)
167 char buf
[16], datebuf
[FROM_DATEBUF
], cbuf
[8], *cp
, *subjline
;
168 char const *datefmt
, *date
, *name
, *fp
n_COLOUR( COMMA
*colo_tag
);
169 int i
, n
, s
, wleft
, subjlen
;
172 n_COLOUR( struct n_colour_pen
*cpen_new COMMA
*cpen_cur COMMA
*cpen_bas
; )
179 _LOOP_MASK
= (1<<4) - 1,
180 _SFMT
= 1<<4, /* It is 'S' */
181 /* For the simple byte-based counts in wleft and n we sometimes need
182 * adjustments to compensate for additional bytes of UTF-8 sequences */
183 _PUTCB_UTF8_SHIFT
= 5,
184 _PUTCB_UTF8_MASK
= 3<<5
189 if ((mp
= message
+ msgno
- 1) == dot
)
193 n_COLOUR( colo_tag
= NULL
; )
195 datefmt
= ok_vlook(datefield
);
197 if (datefmt
!= NULL
) {
198 fp
= hfield1("date", mp
);/* TODO use m_date field! */
204 date
= fakedate(datet
);
205 fp
= ok_vlook(datefield_markout_older
);
206 i
= (*datefmt
!= '\0');
208 i
|= (*fp
!= '\0') ? 2 | 4 : 2; /* XXX no magics */
210 /* May we strftime(3)? */
212 memcpy(&time_current
.tc_local
, localtime(&datet
),
213 sizeof time_current
.tc_local
);
215 if ((i
& 2) && (datet
> time_current
.tc_time
+ DATE_SECSDAY
||
216 #define _6M ((DATE_DAYSYEAR / 2) * DATE_SECSDAY)
217 (datet
+ _6M
< time_current
.tc_time
))) {
219 if ((datefmt
= (i
& 4) ? fp
: NULL
) == NULL
) {
220 memset(datebuf
, ' ', FROM_DATEBUF
); /* xxx ur */
221 memcpy(datebuf
+ 4, date
+ 4, 7);
222 datebuf
[4 + 7] = ' ';
223 memcpy(datebuf
+ 4 + 7 + 1, date
+ 20, 4);
224 datebuf
[4 + 7 + 1 + 4] = '\0';
227 n_COLOUR( colo_tag
= n_COLOUR_TAG_SUM_OLDER
; )
228 } else if ((i
& 1) == 0)
230 } else if (datet
== (time_t)0 && !(mp
->m_flag
& MNOFROM
)) {
231 /* TODO eliminate this path, query the FROM_ date in setptr(),
232 * TODO all other codepaths do so by themselves ALREADY ?????
233 * TODO assert(mp->m_time != 0);, then
234 * TODO ALSO changes behaviour of datefield_markout_older */
235 _parse_from_(mp
, datebuf
);
238 date
= fakedate(datet
);
242 if (name
!= NULL
&& ok_blook(showto
) && is_myname(skin(name
))) {
243 if ((cp
= hfield1("to", mp
)) != NULL
) {
253 name
= ok_blook(showname
) ? realname(name
) : prstr(skin(name
));
257 /* Detect the width of the non-format characters in *headline*;
258 * like that we can simply use putc() in the next loop, since we have
259 * already calculated their column widths (TODO it's sick) */
260 wleft
= subjlen
= scrnwidth
;
262 for (fp
= fmt
; *fp
!= '\0'; ++fp
) {
268 if (digitchar(*fp
)) {
271 n
= 10*n
+ *fp
- '0';
272 while (++fp
, digitchar(*fp
));
282 if (mb_cur_max
> 1) {
284 if ((s
= mbtowc(&wc
, fp
, mb_cur_max
)) == -1)
286 else if ((n
= wcwidth(wc
)) == -1)
298 /* Walk *headline*, producing output TODO not (really) MB safe */
301 colo_tag
= n_COLOUR_TAG_SUM_DOT
;
302 cpen_bas
= n_colour_pen_create(n_COLOUR_ID_SUM_HEADER
, colo_tag
);
303 n_colour_pen_put(cpen_new
= cpen_cur
= cpen_bas
, f
);
306 for (fp
= fmt
; *fp
!= '\0'; ++fp
) {
309 if ((c
= *fp
& 0xFF) != '%') {
311 if ((cpen_new
= cpen_bas
) != cpen_cur
)
312 n_colour_pen_put(cpen_cur
= cpen_new
, f
);
321 if ((c
= *++fp
) == '-') {
326 if (digitchar(*fp
)) {
328 n
= 10*n
+ *fp
- '0';
329 while (++fp
, digitchar(*fp
));
332 if ((c
= *fp
& 0xFF) == '\0')
342 if (flags
& _ISDOT
) {
343 n_COLOUR( cpen_new
= n_colour_pen_create(n_COLOUR_ID_SUM_DOTMARK
,
345 if (options
& OPT_UNICODE
) {
347 /* 25B8;BLACK RIGHT-POINTING SMALL TRIANGLE: â–¸ */
348 cbuf
[1] = (char)0x96, cbuf
[2] = (char)0xB8;
350 /* 25C2;BLACK LEFT-POINTING SMALL TRIANGLE: â—‚ */
351 cbuf
[1] = (char)0x97, cbuf
[2] = (char)0x82;
354 flags
|= 2 << _PUTCB_UTF8_SHIFT
;
363 if (UICMP(32, n_ABS(n
), >, wleft
))
364 n
= (n
< 0) ? -wleft
: wleft
;
365 snprintf(buf
, sizeof buf
, "%u.%02u",
366 (mp
->m_spamscore
>> 8), (mp
->m_spamscore
& 0xFF));
367 n
= fprintf(f
, "%*s", n
, buf
);
368 wleft
= (n
>= 0) ? wleft
- n
: 0;
375 c
= _dispc(mp
, attrlist
);
378 if (cpen_new
== cpen_cur
)
380 if (cpen_new
!= cpen_cur
)
381 n_colour_pen_put(cpen_cur
= cpen_new
, f
);
383 if (UICMP(32, n_ABS(n
), >, wleft
))
384 n
= (n
< 0) ? -wleft
: wleft
;
386 n
= fprintf(f
, "%*s", n
, cbuf
);
389 if ((n
= (flags
& _PUTCB_UTF8_MASK
)) != 0) {
390 n
>>= _PUTCB_UTF8_SHIFT
;
394 wleft
= 0; /* TODO I/O error.. ? break? */
397 if ((cpen_new
= cpen_bas
) != cpen_cur
)
398 n_colour_pen_put(cpen_cur
= cpen_new
, f
);
402 if (datefmt
!= NULL
) {
403 i
= strftime(datebuf
, sizeof datebuf
, datefmt
,
404 &time_current
.tc_local
);
408 n_err(_("Ignored date format, it excesses the target "
409 "buffer (%lu bytes)\n"), (ul_i
)sizeof(datebuf
));
414 if (UICMP(32, n_ABS(n
), >, wleft
))
415 n
= (n
< 0) ? -wleft
: wleft
;
416 n
= fprintf(f
, "%*.*s", n
, n
, date
);
417 wleft
= (n
>= 0) ? wleft
- n
: 0;
422 if (UICMP(32, n_ABS(n
), >, wleft
))
423 n
= (n
< 0) ? -wleft
: wleft
;
424 n
= fprintf(f
, "%*u", n
, (threaded
== 1 ? mp
->m_level
: 0));
425 wleft
= (n
>= 0) ? wleft
- n
: 0;
436 n
= (n
< 0) ? -wleft
: wleft
;
438 if (flags
& _ISTO
) /* XXX tr()! */
440 n
= fprintf(f
, "%s%s", ((flags
& _ISTO
) ? "To " : n_empty
),
441 colalign(name
, i
, n
, &wleft
));
444 else if (flags
& _ISTO
)
450 cpen_new
= n_colour_pen_create(n_COLOUR_ID_SUM_THREAD
, colo_tag
);
451 if (cpen_new
!= cpen_cur
)
452 n_colour_pen_put(cpen_cur
= cpen_new
, f
);
454 n
= __putindent(f
, mp
, n_MIN(wleft
, scrnwidth
- 60));
455 wleft
= (n
>= 0) ? wleft
- n
: 0;
457 if ((cpen_new
= cpen_bas
) != cpen_cur
)
458 n_colour_pen_put(cpen_cur
= cpen_new
, f
);
465 if (UICMP(32, n_ABS(n
), >, wleft
))
466 n
= (n
< 0) ? -wleft
: wleft
;
468 n
= fprintf(f
, "%*ld", n
, mp
->m_xlines
);
469 wleft
= (n
>= 0) ? wleft
- n
: 0;
481 for (i
= msgCount
; i
> 999; i
/= 10)
484 if (UICMP(32, n_ABS(n
), >, wleft
))
485 n
= (n
< 0) ? -wleft
: wleft
;
486 n
= fprintf(f
, "%*lu", n
, (ul_i
)msgno
);
487 wleft
= (n
>= 0) ? wleft
- n
: 0;
492 if (UICMP(32, n_ABS(n
), >, wleft
))
493 n
= (n
< 0) ? -wleft
: wleft
;
494 n
= fprintf(f
, "%*lu", n
, (ul_i
)mp
->m_xsize
);
495 wleft
= (n
>= 0) ? wleft
- n
: 0;
507 if (UICMP(32, n_ABS(n
), >, subjlen
))
508 n
= (n
< 0) ? -subjlen
: subjlen
;
510 n
-= (n
< 0) ? -2 : 2;
513 if (subjline
== NULL
)
514 subjline
= __subject(mp
, (threaded
&& (flags
& _IFMT
)), yetprinted
);
515 if (subjline
== (char*)-1) {
516 n
= fprintf(f
, "%*s", n
, n_empty
);
517 wleft
= (n
>= 0) ? wleft
- n
: 0;
519 n
= fprintf(f
, ((flags
& _SFMT
) ? "\"%s\"" : "%s"),
520 colalign(subjline
, n_ABS(n
), n
, &wleft
));
525 case 'T': { /* Message recipient flags */
526 /* We never can reuse "name" since it's the full name */
527 struct name
const *np
= lextract(hfield1("to", mp
), GTO
| GSKIN
);
531 for (; np
!= NULL
; np
= np
->n_flink
) {
532 switch (is_mlist(np
->n_name
, FAL0
)) {
533 case MLIST_SUBSCRIBED
: c
= 'S'; goto jputcb
;
534 case MLIST_KNOWN
: c
= 'L'; goto jputcb
;
542 np
= lextract(hfield1("cc", mp
), GCC
| GSKIN
);
549 for (i
= msgCount
; i
> 999; i
/= 10)
552 if (UICMP(32, n_ABS(n
), >, wleft
))
553 n
= (n
< 0) ? -wleft
: wleft
;
554 n
= fprintf(f
, "%*lu",
555 n
, (threaded
? (ul_i
)mp
->m_threadpos
: (ul_i
)msgno
));
556 wleft
= (n
>= 0) ? wleft
- n
: 0;
559 if (options
& OPT_D_V
)
560 n_err(_("Unkown *headline* format: %%%c\n"), c
);
574 if (subjline
!= NULL
&& subjline
!= (char*)-1)
580 __subject(struct message
*mp
, bool_t threaded
, size_t yetprinted
)
583 char *rv
= (char*)-1, *ms
;
586 if ((ms
= hfield1("subject", mp
)) == NULL
)
589 in
.l
= strlen(in
.s
= ms
);
590 mime_fromhdr(&in
, &out
, TD_ICONV
| TD_ISPR
);
593 if (!threaded
|| mp
->m_level
== 0)
596 /* In a display thread - check whether this message uses the same
597 * Subject: as it's parent or elder neighbour, suppress printing it if
598 * this is the case. To extend this a bit, ignore any leading Re: or
599 * Fwd: plus follow-up WS. Ignore invisible messages along the way */
600 ms
= subject_re_trim(ms
);
602 for (; (mp
= prev_in_thread(mp
)) != NULL
&& yetprinted
-- > 0;) {
605 if (visible(mp
) && (os
= hfield1("subject", mp
)) != NULL
) {
609 in
.l
= strlen(in
.s
= os
);
610 mime_fromhdr(&in
, &oout
, TD_ICONV
| TD_ISPR
);
611 x
= asccasecmp(ms
, subject_re_trim(oout
.s
));
627 __putindent(FILE *fp
, struct message
*mp
, int maxwidth
)/* XXX no magic consts */
630 int *us
, indlvl
, indw
, i
, important
= MNEW
| MFLAGGED
;
634 if (mp
->m_level
== 0 || maxwidth
== 0) {
639 cs
= ac_alloc(mp
->m_level
);
640 us
= ac_alloc(mp
->m_level
* sizeof *us
);
643 if (mp
->m_younger
&& UICMP(32, i
+ 1, ==, mp
->m_younger
->m_level
)) {
644 if (mp
->m_parent
&& mp
->m_parent
->m_flag
& important
)
645 us
[i
] = mp
->m_flag
& important
? 0x2523 : 0x2520;
647 us
[i
] = mp
->m_flag
& important
? 0x251D : 0x251C;
650 if (mp
->m_parent
&& mp
->m_parent
->m_flag
& important
)
651 us
[i
] = mp
->m_flag
& important
? 0x2517 : 0x2516;
653 us
[i
] = mp
->m_flag
& important
? 0x2515 : 0x2514;
658 for (i
= mp
->m_level
- 2; i
>= 0; --i
) {
660 if (UICMP(32, i
, >, mq
->m_level
- 1)) {
665 if (mq
->m_parent
&& (mq
->m_parent
->m_flag
& important
))
678 for (indlvl
= indw
= 0; (ui8_t
)indlvl
< mp
->m_level
&& indw
< maxwidth
;
680 if (indw
< maxwidth
- 1)
681 indw
+= (int)putuc(us
[indlvl
], cs
[indlvl
] & 0xFF, fp
);
683 indw
+= (int)putuc(0x21B8, '^', fp
);
685 indw
+= putuc(0x25B8, '>', fp
);
695 _dispc(struct message
*mp
, char const *a
)
700 if ((mp
->m_flag
& (MREAD
| MNEW
)) == MREAD
)
702 if ((mp
->m_flag
& (MREAD
| MNEW
)) == (MREAD
| MNEW
))
704 if (mp
->m_flag
& MANSWERED
)
706 if (mp
->m_flag
& MDRAFTED
)
708 if ((mp
->m_flag
& (MREAD
| MNEW
)) == MNEW
)
710 if (!(mp
->m_flag
& (MREAD
| MNEW
)))
712 if (mp
->m_flag
& MSPAM
)
714 if (mp
->m_flag
& MSPAMUNSURE
)
716 if (mp
->m_flag
& MSAVED
)
718 if (mp
->m_flag
& MPRESERVE
)
720 if (mp
->m_flag
& (MBOX
| MBOXED
))
722 if (mp
->m_flag
& MFLAGGED
)
724 if (mb
.mb_threaded
== 1 && mp
->m_collapsed
> 0)
726 if (mb
.mb_threaded
== 1 && mp
->m_collapsed
< 0)
733 a_cmd_scroll(char const *arg
, bool_t onlynew
){
737 int msgspec
, size
, maxs
;
740 /* TODO scroll problem: we do not know whether + and $ have already reached
741 * TODO the last screen in threaded mode */
742 msgspec
= onlynew
? -1 : 0;
744 if((maxs
= msgCount
/ size
) > 0 && msgCount
% size
== 0)
773 case '1': case '2': case '3': case '4': case '5':
774 case '6': case '7': case '8': case '9': case '0':
777 l
= strtol(arg
, &eptr
, 10);
780 if(l
> maxs
- (isabs
? 0 : _screen
))
782 _screen
= isabs
? (int)l
: _screen
+ l
;
788 printf(_("On last screenful of messages\n"));
797 l
= strtol(arg
, &eptr
, 10);
807 printf(_("On first screenful of messages\n"));
814 n_err(_("Unrecognized scrolling command: %s\n"), arg
);
819 size
= _headers(msgspec
);
826 _headers(int msgspec
) /* TODO rework v15 */
829 bool_t
volatile isrelax
;
830 ui32_t
volatile flag
;
831 int g
, k
, mesg
, size
;
832 int volatile lastg
= 1;
833 struct message
*mp
, *mq
, *lastmq
= NULL
;
834 enum mflag fl
= MNEW
| MFLAGGED
;
837 time_current_update(&time_current
, FAL0
);
841 n_SIGMAN_ENTER_SWITCH(&sm
, n_SIGMAN_ALL
) {
849 if (options
& OPT_INTERACTIVE
)
850 n_colour_env_create(n_COLOUR_CTX_SUM
, FAL0
);
856 #if 0 /* FIXME original code path */
869 if (mb
.mb_threaded
== 0) {
872 for (mp
= message
; PTRCMP(mp
, <, message
+ msgCount
); ++mp
)
876 if (mp
->m_flag
& fl
) {
880 if ((msgspec
> 0 && PTRCMP(mp
, ==, message
+ msgspec
- 1)) ||
881 (msgspec
== 0 && g
== k
) ||
882 (msgspec
== -2 && g
== k
+ size
&& lastmq
) ||
883 (msgspec
< 0 && g
>= k
&& (mp
->m_flag
& fl
) != 0))
887 if (lastmq
&& (msgspec
== -2 ||
888 (msgspec
== -1 && PTRCMP(mp
, ==, message
+ msgCount
)))) {
895 mesg
= (int)PTR2SIZE(mp
- message
);
896 if (PTRCMP(dot
, !=, message
+ msgspec
- 1)) { /* TODO really?? */
897 for (mq
= mp
; PTRCMP(mq
, <, message
+ msgCount
); ++mq
)
906 for (; PTRCMP(mp
, <, message
+ msgCount
); ++mp
) {
910 if (UICMP(32, flag
++, >=, size
))
912 _print_head(0, mesg
, stdout
, 0);
917 } else { /* threaded */
920 for (mp
= threadroot
; mp
; mp
= next_in_thread(mp
))
922 (mp
->m_collapsed
<= 0 ||
923 PTRCMP(mp
, ==, message
+ msgspec
- 1))) {
926 if (mp
->m_flag
& fl
) {
930 if ((msgspec
> 0 && PTRCMP(mp
, ==, message
+ msgspec
- 1)) ||
931 (msgspec
== 0 && g
== k
) ||
932 (msgspec
== -2 && g
== k
+ size
&& lastmq
) ||
933 (msgspec
< 0 && g
>= k
&& (mp
->m_flag
& fl
) != 0))
937 if (lastmq
&& (msgspec
== -2 ||
938 (msgspec
== -1 && PTRCMP(mp
, ==, message
+ msgCount
)))) {
944 if (PTRCMP(dot
, !=, message
+ msgspec
- 1)) { /* TODO really?? */
945 for (mq
= mp
; mq
; mq
= next_in_thread(mq
))
946 if (visible(mq
) && mq
->m_collapsed
<= 0) {
956 (mp
->m_collapsed
<= 0 ||
957 PTRCMP(mp
, ==, message
+ msgspec
- 1))) {
958 if (UICMP(32, flag
++, >=, size
))
960 _print_head(flag
- 1, PTR2SIZE(mp
- message
+ 1), stdout
,
964 mp
= next_in_thread(mp
);
971 printf(_("No more mail.\n"));
972 if (pstate
& (PS_HOOK_MASK
| PS_ROBOT
))
976 n_sigman_cleanup_ping(&sm
);
980 n_COLOUR( n_colour_env_gut((sm
.sm_signo
!= SIGPIPE
) ? stdout
: NULL
); )
982 n_sigman_leave(&sm
, n_SIGMAN_VIPSIGS_NTTYOUT
);
987 _type1(int *msgvec
, bool_t doign
, bool_t dopage
, bool_t dopipe
,
988 bool_t dodecode
, char *cmd
, ui64_t
*tstats
)
996 FILE * volatile obuf
;
997 bool_t
volatile isrelax
= FAL0
;
1000 enum sendaction
const action
= ((dopipe
&& ok_blook(piperaw
))
1001 ? SEND_MBOX
: dodecode
1003 ? SEND_TODISP
: SEND_TODISP_ALL
);
1004 bool_t
const volatile formfeed
= (dopipe
&& ok_blook(page
));
1007 n_SIGMAN_ENTER_SWITCH(&sm
, n_SIGMAN_ALL
) {
1015 if ((obuf
= Popen(cmd
, "w", ok_vlook(SHELL
), NULL
, 1)) == NULL
) {
1019 } else if ((options
& OPT_TTYOUT
) && (dopage
||
1020 ((options
& OPT_INTERACTIVE
) && (cp
= ok_vlook(crt
)) != NULL
))) {
1024 for (ip
= msgvec
; *ip
&& PTRCMP(ip
- msgvec
, <, msgCount
); ++ip
) {
1025 mp
= message
+ *ip
- 1;
1026 if (!(mp
->m_have
& HAVE_BODY
))
1027 if (get_body(mp
) != OKAY
)
1028 goto jcleanup_leave
;
1029 nlines
+= mp
->m_lines
+ 1; /* Message info XXX and PARTS... */
1033 /* >= not <: we return to the prompt */
1034 if (dopage
|| UICMP(z
, nlines
, >=,
1035 (*cp
!= '\0' ? strtoul(cp
, NULL
, 0) : (size_t)realscreenheight
))) {
1036 if ((obuf
= n_pager_open()) == NULL
)
1040 if ((options
& OPT_INTERACTIVE
) &&
1041 (action
== SEND_TODISP
|| action
== SEND_TODISP_ALL
||
1042 action
== SEND_SHOW
))
1043 n_colour_env_create(n_COLOUR_CTX_VIEW
, obuf
!= stdout
);
1047 else if ((options
& OPT_INTERACTIVE
) &&
1048 (action
== SEND_TODISP
|| action
== SEND_TODISP_ALL
))
1049 n_colour_env_create(n_COLOUR_CTX_VIEW
, FAL0
);
1052 /*TODO unless we have our signal manager special care must be taken */
1055 for (ip
= msgvec
; *ip
&& PTRCMP(ip
- msgvec
, <, msgCount
); ++ip
) {
1056 mp
= message
+ *ip
- 1;
1059 pstate
|= PS_DID_PRINT_DOT
;
1061 if (!dopipe
&& ip
!= msgvec
)
1062 fprintf(obuf
, "\n");
1063 if (action
!= SEND_MBOX
)
1064 _show_msg_overview(obuf
, mp
, *ip
);
1065 sendmp(mp
, obuf
, (doign
? ignore
: NULL
), NULL
, action
, mstats
);
1067 if (formfeed
) /* TODO a nicer way to separate piped messages! */
1070 tstats
[0] += mstats
[0];
1077 n_sigman_cleanup_ping(&sm
);
1081 n_COLOUR( n_colour_env_gut((sm
.sm_signo
!= SIGPIPE
) ? obuf
: NULL
); )
1083 n_pager_close(obuf
);
1086 n_sigman_leave(&sm
, n_SIGMAN_VIPSIGS_NTTYOUT
);
1091 _pipe1(char *str
, int doign
)
1094 char const *cmd
, *cmdq
;
1095 int *msgvec
, rv
= 1;
1099 if ((cmd
= laststring(str
, &needs_list
, TRU1
)) == NULL
) {
1100 cmd
= ok_vlook(cmd
);
1101 if (cmd
== NULL
|| *cmd
== '\0') {
1102 n_err(_("Variable *cmd* not set\n"));
1107 msgvec
= salloc((msgCount
+ 2) * sizeof *msgvec
);
1110 *msgvec
= first(0, MMNORM
);
1112 if (pstate
& (PS_HOOK_MASK
| PS_ROBOT
)) {
1116 puts(_("No messages to pipe."));
1120 } else if (getmsglist(str
, msgvec
, 0) < 0)
1123 if (pstate
& (PS_HOOK_MASK
| PS_ROBOT
)) {
1127 printf("No applicable messages.\n");
1131 cmdq
= n_shexp_quote_cp(cmd
, FAL0
);
1132 printf(_("Pipe to: %s\n"), cmdq
);
1134 if ((rv
= _type1(msgvec
, doign
, FAL0
, TRU1
, FAL0
, n_UNCONST(cmd
), stats
)
1136 printf("%s %" PRIu64
" bytes\n", cmdq
, stats
[0]);
1143 a_cmd_top(void *vp
, struct ignoretab
*itp
){
1146 enum{a_NONE
, a_SQUEEZE
= 1u<<0,
1147 a_EMPTY
= 1u<<8, a_STOP
= 1u<<9, a_WORKMASK
= 0xFF00u
} f
;
1148 size_t tmax
, plines
;
1152 if((iobuf
= Ftmp(NULL
, "topio", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) == NULL
){
1153 n_perr(_("`top': I/O temporary file"), 0);
1157 if((pbuf
= Ftmp(NULL
, "toppag", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) == NULL
){
1158 n_perr(_("`top': temporary pager file"), 0);
1163 /* TODO In v15 we should query the m_message object, and directly send only
1164 * TODO those parts, optionally over empty-line-squeeze and quote-strip
1165 * TODO filters, in which we are interested in: only text content!
1166 * TODO And: with *topsqueeze*, header/content separating empty line.. */
1167 pstate
&= ~PS_MSGLIST_DIRECT
; /* TODO NO ATTACHMENTS */
1171 if (options
& OPT_INTERACTIVE
)
1172 n_colour_env_create(n_COLOUR_CTX_VIEW
, TRU1
);
1174 n_string_creat_auto(&s
);
1178 if((l
= strtol(ok_vlook(toplines
), NULL
, 0)) <= 0){
1179 tmax
= (size_t)screensize();
1187 f
= ok_blook(topsqueeze
) ? a_SQUEEZE
: a_NONE
;
1189 for(ip
= msgvec
= vp
;
1190 *ip
!= 0 && UICMP(z
, PTR2SIZE(ip
- msgvec
), <, msgCount
); ++ip
){
1193 mp
= &message
[*ip
- 1];
1196 pstate
|= PS_DID_PRINT_DOT
;
1200 if(ftruncate(fileno(iobuf
), 0)){
1201 n_perr(_("`top': ftruncate(2)"), 0);
1205 if(sendmp(mp
, iobuf
, itp
, NULL
, SEND_TODISP_ALL
, NULL
) < 0){
1206 n_err(_("`top': failed to prepare message %d\n"), *ip
);
1210 fflush_rewind(iobuf
);
1212 _show_msg_overview(pbuf
, mp
, *ip
);
1217 n_string_trunc(&s
, 0);
1218 for(l
= 0, f
&= ~a_WORKMASK
; !(f
& a_STOP
);){
1221 if((c
= getc(iobuf
)) == EOF
){
1227 n_string_push_c(&s
, c
);
1228 else if((f
& a_SQUEEZE
) && s
.s_len
== 0){
1229 if(!(f
& a_STOP
) && ((f
& a_EMPTY
) || tmax
- 1 <= l
))
1231 if(putc('\n', pbuf
) == EOF
){
1238 char const *cp
, *xcp
;
1240 cp
= n_string_cp_const(&s
);
1241 /* TODO Brute simple skip part overviews; see above.. */
1242 if(!(f
& a_SQUEEZE
))
1244 else if(s
.s_len
> 8 &&
1245 (xcp
= strstr(cp
, "[-- ")) != NULL
&&
1246 strstr(&xcp
[1], " --]") != NULL
)
1248 else for(; (c
= *cp
) != '\0'; ++cp
){
1251 if(!blankspacechar(c
)){
1260 if(fputs(n_string_cp_const(&s
), pbuf
) == EOF
||
1261 putc('\n', pbuf
) == EOF
){
1270 n_string_trunc(&s
, 0);
1278 if(!(f
& a_EMPTY
) && putc('\n', pbuf
) == EOF
){
1288 n_COLOUR( n_colour_env_gut(pbuf
); )
1291 page_or_print(pbuf
, plines
);
1298 return (vp
!= NULL
);
1302 c_cmdnotsupp(void *v
) /* TODO -> lex.c */
1306 n_err(_("The requested feature is not compiled in\n"));
1317 rv
= print_header_group((int*)v
);
1323 print_header_group(int *vector
)
1328 assert(vector
!= NULL
&& vector
!= (void*)-1);
1329 rv
= _headers(vector
[0]);
1340 rv
= a_cmd_scroll(v
, FAL0
);
1351 rv
= a_cmd_scroll(v
, TRU1
);
1360 int *msgvec
= v
, *ip
, n
;
1362 FILE * volatile obuf
;
1363 bool_t
volatile isrelax
;
1366 time_current_update(&time_current
, FAL0
);
1370 n_SIGMAN_ENTER_SWITCH(&sm
, n_SIGMAN_ALL
) {
1377 if (options
& OPT_INTERACTIVE
) {
1378 if ((cp
= ok_vlook(crt
)) != NULL
) {
1379 for (n
= 0, ip
= msgvec
; *ip
!= 0; ++ip
)
1381 if (UICMP(z
, n
, >, (*cp
== '\0'
1382 ? (size_t)screensize() : strtoul(cp
, NULL
, 0)) + 3) &&
1383 (obuf
= n_pager_open()) == NULL
)
1386 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM
, obuf
!= stdout
); )
1389 /* Update dot before display so that the dotmark etc. are correct */
1390 for (ip
= msgvec
; *ip
!= 0; ++ip
)
1393 setdot(message
+ *ip
- 1);
1397 for (n
= 0, ip
= msgvec
; *ip
!= 0; ++ip
) { /* TODO join into _print_head() */
1398 _print_head((size_t)n
++, (size_t)*ip
, obuf
, mb
.mb_threaded
);
1404 n_sigman_cleanup_ping(&sm
);
1408 n_COLOUR( n_colour_env_gut((sm
.sm_signo
!= SIGPIPE
) ? obuf
: NULL
); )
1410 n_pager_close(obuf
);
1412 n_sigman_leave(&sm
, n_SIGMAN_VIPSIGS_NTTYOUT
);
1417 print_headers(size_t bottom
, size_t topx
, bool_t only_marked
)
1420 bool_t
volatile isrelax
;
1424 time_current_update(&time_current
, FAL0
);
1427 n_SIGMAN_ENTER_SWITCH(&sm
, n_SIGMAN_ALL
) {
1435 if (options
& OPT_INTERACTIVE
)
1436 n_colour_env_create(n_COLOUR_CTX_SUM
, FAL0
);
1441 for (printed
= 0; bottom
<= topx
; ++bottom
) {
1442 struct message
*mp
= message
+ bottom
- 1;
1444 if (!(mp
->m_flag
& MMARK
))
1446 } else if (!visible(mp
))
1448 _print_head(printed
++, bottom
, stdout
, FAL0
);
1454 n_sigman_cleanup_ping(&sm
);
1458 n_COLOUR( n_colour_env_gut((sm
.sm_signo
!= SIGPIPE
) ? stdout
: NULL
); )
1460 n_sigman_leave(&sm
, n_SIGMAN_VIPSIGS_NTTYOUT
);
1468 printf("%d\n", (int)PTR2SIZE(dot
- message
+ 1));
1476 int *msgvec
= v
, rv
;
1479 rv
= _type1(msgvec
, TRU1
, TRU1
, FAL0
, FAL0
, NULL
, NULL
);
1487 int *msgvec
= v
, rv
;
1490 rv
= _type1(msgvec
, FAL0
, TRU1
, FAL0
, FAL0
, NULL
, NULL
);
1498 int *msgvec
= v
, rv
;
1501 rv
= _type1(msgvec
, TRU1
, FAL0
, FAL0
, FAL0
, NULL
, NULL
);
1509 int *msgvec
= v
, rv
;
1512 rv
= _type1(msgvec
, FAL0
, FAL0
, FAL0
, FAL0
, NULL
, NULL
);
1520 int *msgvec
= v
, rv
;
1523 rv
= _type1(msgvec
, FAL0
, FAL0
, FAL0
, TRU1
, NULL
, NULL
);
1535 rv
= _pipe1(str
, 1);
1547 rv
= _pipe1(str
, 0);
1554 struct ignoretab it
[2];
1558 n_ignoretab_creat(&it
[0], TRU1
);
1559 n_ignoretab_creat(&it
[1], TRU1
);
1560 n_ignoretab_insert(&it
[1], "from", sizeof("from") -1);
1561 n_ignoretab_insert(&it
[1], "to", sizeof("to") -1);
1562 n_ignoretab_insert(&it
[1], "cc", sizeof("cc") -1);
1563 n_ignoretab_insert(&it
[1], "subject", sizeof("subject") -1);
1565 rv
= !a_cmd_top(v
, it
);
1575 rv
= !a_cmd_top(v
, ignore
);
1590 if(*(argv
= v
) != NULL
){
1591 if((cp
= fexpand(*argv
, FEXP_NSHELL
| FEXP_LOCAL
)) == NULL
)
1594 cp
= folder_query();
1596 rv
= run_command(ok_vlook(LISTER
), 0, COMMAND_FD_PASS
, COMMAND_FD_PASS
, cp
,