1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: mailview.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2006-2009 University of Washington
8 * Copyright 2013-2017 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
22 Implements message data gathering and formatting
28 #include "../pith/mailview.h"
29 #include "../pith/conf.h"
30 #include "../pith/msgno.h"
31 #include "../pith/editorial.h"
32 #include "../pith/mimedesc.h"
33 #include "../pith/margin.h"
34 #include "../pith/color.h"
35 #include "../pith/strlst.h"
36 #include "../pith/charset.h"
37 #include "../pith/status.h"
38 #include "../pith/maillist.h"
39 #include "../pith/mailcmd.h"
40 #include "../pith/mailindx.h"
41 #include "../pith/imap.h"
42 #include "../pith/detach.h"
43 #include "../pith/text.h"
44 #include "../pith/url.h"
45 #include "../pith/rfc2231.h"
46 #include "../pith/list.h"
47 #include "../pith/stream.h"
48 #include "../pith/send.h"
49 #include "../pith/filter.h"
50 #include "../pith/string.h"
51 #include "../pith/ablookup.h"
52 #include "../pith/escapes.h"
53 #include "../pith/keyword.h"
54 #include "../pith/smime.h"
59 #define ISRFCEOL(S) (*(S) == '\015' && *((S)+1) == '\012')
63 * This is a list of header fields that are represented canonically
64 * by the c-client's ENVELOPE structure. The list is used by the
65 * two functions below to decide if a given field is included in this
68 static struct envelope_s
{
73 {"sender", FE_SENDER
},
78 {"newsgroups", FE_NEWSGROUPS
},
79 {"subject", FE_SUBJECT
},
80 {"message-id", FE_MESSAGEID
},
81 {"reply-to", FE_REPLYTO
},
82 {"followup-to", FE_FOLLOWUPTO
},
83 {"in-reply-to", FE_INREPLYTO
},
84 /* {"return-path", FE_RETURNPATH}, not usually filled in */
85 {"references", FE_REFERENCES
},
91 * Hook for optional display of rfc2369 content
93 int (*pith_opt_rfc2369_editorial
)(long, HANDLE_S
**, int, int, gf_io_t
);
101 int format_blip_seen(long);
102 int is_an_env_hdr(char *);
103 int is_an_addr_hdr(char *);
104 void format_env_hdr(MAILSTREAM
*, long, char *, ENVELOPE
*,
105 fmt_env_t
, gf_io_t
, char *, char *, int);
106 int delineate_this_header(char *, char *, char **, char **);
107 char *url_embed(int);
108 int color_headers(long, char *, LT_INS_S
**, void *);
109 int url_hilite_hdr(long, char *, LT_INS_S
**, void *);
110 int pad_to_right_edge(long, char *, LT_INS_S
**, void *);
111 int url_bogus_imap(char **, char *, char *);
112 int format_raw_header(MAILSTREAM
*, long, char *, gf_io_t
);
113 void format_envelope(MAILSTREAM
*, long, char *, ENVELOPE
*,
114 gf_io_t
, long, char *, int);
115 int any_hdr_color(char *);
116 void format_addr_string(MAILSTREAM
*, long, char *, char *,
117 ADDRESS
*, int, char *, gf_io_t
);
118 void pine_rfc822_write_address_noquote(ADDRESS
*, gf_io_t
, int *);
119 void format_newsgroup_string(char *, char *, int, gf_io_t
);
120 int format_raw_hdr_string(char *, char *, gf_io_t
, char *, int);
121 int format_env_puts(char *, gf_io_t
);
122 int find_field(char **, char *, size_t);
123 int embed_color(COLOR_PAIR
*, gf_io_t
);
124 COLOR_PAIR
*get_cur_embedded_color(void);
125 void clear_cur_embedded_color(void);
130 /*----------------------------------------------------------------------
131 Format a message message for viewing
133 Args: msgno -- The number of the message to view
134 env -- pointer to the message's envelope
135 body -- pointer to the message's body
136 handlesp -- address of pointer to the message's handles
137 flgs -- possible flags listed in pith/mailview.h with
139 pc -- write to this function
141 Result: Returns true if no problems encountered, else false.
143 First the envelope is formatted; next a list of all attachments is
144 formatted if there is more than one. Then all the body parts are
145 formatted, fetching them as needed. This includes headers of included
146 message. Richtext is also formatted. An entry is made in the text for
147 parts that are not displayed or can't be displayed.
151 format_message(long int msgno
, ENVELOPE
*env
, struct mail_bodystruct
*body
,
152 HANDLE_S
**handlesp
, int flgs
, gf_io_t pc
)
154 char *decode_err
= NULL
;
158 clear_cur_embedded_color();
160 if(!(flgs
& FM_DISPLAY
))
163 width
= (flgs
& FM_DISPLAY
) ? ps_global
->ttyo
->screen_cols
: 80;
165 /*---- format and copy envelope ----*/
166 if(!(flgs
& FM_NOEDITORIAL
)){
167 if(ps_global
->full_header
== 1)
168 /* TRANSLATORS: User is viewing a message and all the quoted text is
170 q_status_message(SM_INFO
, 0, 3, _("All quoted text being included"));
171 else if(ps_global
->full_header
== 2)
172 q_status_message(SM_INFO
, 0, 3,
173 /* TRANSLATORS: User is viewing a message and all of
174 the header text is being shown. */
175 _("Full header mode ON. All header text being included"));
178 HD_INIT(&h
, ps_global
->VAR_VIEW_HEADERS
, ps_global
->view_all_except
, FE_DEFAULT
);
179 switch(format_header(ps_global
->mail_stream
, msgno
, NULL
,
180 env
, &h
, NULL
, handlesp
, flgs
, NULL
, pc
)){
182 case -1 : /* write error */
185 case 1 : /* fetch error */
186 if(!(gf_puts("[ Error fetching header ]", pc
)
187 && !gf_puts(NEWLINE
, pc
)))
194 || (ps_global
->full_header
== 2
195 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT
, ps_global
))))
196 format_attachment_list(msgno
, body
, handlesp
, flgs
, width
, pc
);
198 /* write delimiter and body */
199 if(gf_puts(NEWLINE
, pc
)
200 && (decode_err
= format_body(msgno
, body
, handlesp
, &h
, flgs
, width
, pc
)) == NULL
)
206 if(!(flgs
& FM_DISPLAY
))
207 q_status_message1(SM_ORDER
, 3, 4, _("Error writing message: %s"),
208 decode_err
? decode_err
: error_description(errno
));
215 format_body(long int msgno
, BODY
*body
, HANDLE_S
**handlesp
, HEADER_S
*hp
, int flgs
, int width
, gf_io_t pc
)
217 int filt_only_c0
= 0, wrapflags
, error_found
= 0;
218 int is_in_sig
= OUT_SIG_BLOCK
;
219 char *charset
, *decode_err
= NULL
, *tmp1
, *description
;
225 || (ps_global
->full_header
== 2
226 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT
, ps_global
))) {
228 /*--- Server is not an IMAP2bis, It can't parse MIME
229 so we just show the text here. Hopefully the
230 message isn't a MIME message
234 if((text2
= (void *)pine_mail_fetch_text(ps_global
->mail_stream
,
235 msgno
, NULL
, NULL
, NIL
)) != NULL
){
237 if(!gf_puts(NEWLINE
, pc
)) /* write delimiter */
238 return("Write Error");
240 gf_set_readc(&gc
, text2
, (unsigned long)strlen(text2
), CharStar
, 0);
244 * We need to translate the message
245 * into UTF-8, but that's trouble in the full header case
246 * because we don't know what to translate from. We'll just
247 * take a guess by looking for the first text part and
250 if(body
&& body
->type
== TYPETEXT
)
251 charset
= parameter_val(body
->parameter
, "charset");
252 else if(body
&& body
->type
== TYPEMULTIPART
&& body
->nested
.part
253 && body
->nested
.part
->body
.type
== TYPETEXT
)
254 charset
= parameter_val(body
->nested
.part
->body
.parameter
, "charset");
256 charset
= ps_global
->display_charmap
;
258 if(strucmp(charset
, "us-ascii") && strucmp(charset
, "utf-8")){
259 /* transliterate message text to UTF-8 */
260 gf_link_filter(gf_utf8
, gf_utf8_opt(charset
));
263 /* link in filters, similar to what is done in decode_text() */
264 if(!ps_global
->pass_ctrl_chars
){
265 gf_link_filter(gf_escape_filter
, NULL
);
267 gf_link_filter(gf_control_filter
,
268 gf_control_filter_opt(&filt_only_c0
));
271 gf_link_filter(gf_tag_filter
, NULL
);
273 if((F_ON(F_VIEW_SEL_URL
, ps_global
)
274 || F_ON(F_VIEW_SEL_URL_HOST
, ps_global
)
275 || F_ON(F_SCAN_ADDR
, ps_global
))
277 gf_link_filter(gf_line_test
,
278 gf_line_test_opt(url_hilite
,
279 gf_url_hilite_opt(&uh
,handlesp
,0)));
282 if((flgs
& FM_DISPLAY
)
283 && !(flgs
& FM_NOCOLOR
)
285 && ps_global
->VAR_SIGNATURE_FORE_COLOR
286 && ps_global
->VAR_SIGNATURE_BACK_COLOR
){
287 gf_link_filter(gf_line_test
, gf_line_test_opt(color_signature
, &is_in_sig
));
290 if((flgs
& FM_DISPLAY
)
291 && !(flgs
& FM_NOCOLOR
)
293 && ps_global
->VAR_QUOTE1_FORE_COLOR
294 && ps_global
->VAR_QUOTE1_BACK_COLOR
){
295 gf_link_filter(gf_line_test
, gf_line_test_opt(color_a_quote
, NULL
));
298 if(!(flgs
& FM_NOWRAP
)){
299 wrapflags
= (flgs
& FM_DISPLAY
) ? (GFW_HANDLES
|GFW_SOFTHYPHEN
) : GFW_NONE
;
301 && !(flgs
& FM_NOCOLOR
)
302 && pico_usingcolor())
303 wrapflags
|= GFW_USECOLOR
;
304 gf_link_filter(gf_wrap
, gf_wrap_filter_opt(width
, width
,
306 ? NULL
: format_view_margin(),
311 gf_link_filter(gf_nvtnl_local
, NULL
);
312 if((decode_err
= gf_pipe(gc
, pc
)) != NULL
){
313 /* TRANSLATORS: There was an error putting together a message for
314 viewing. The arg is the description of the error. */
315 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, _("Formatting error: %s"), decode_err
);
316 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
317 if(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
318 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
319 && gf_puts(NEWLINE
, pc
))
327 if(!gf_puts(NEWLINE
, pc
)
328 || !gf_puts(_(" [ERROR fetching text of message]"), pc
)
329 || !gf_puts(NEWLINE
, pc
)
330 || !gf_puts(NEWLINE
, pc
))
331 return("Write Error");
337 /*======== Now loop through formatting all the parts =======*/
338 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++) {
340 if(a
->body
->type
== TYPEMULTIPART
){
342 if(strucmp(a
->body
->subtype
, OUR_PKCS7_ENCLOSURE_SUBTYPE
)==0){
344 if(!(!format_editorial(a
->description
, width
, flgs
, handlesp
, pc
)
345 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
346 return("Write Error");
354 if(a
->suppress_editorial
)
357 if(!(flgs
& FM_NOEDITORIAL
)
358 && (!gf_puts(NEWLINE
, pc
)
359 || (decode_err
= part_desc(a
->number
, a
->body
,
361 ? (a
->can_display
!= MCD_NONE
)
363 : 3, width
, flgs
, pc
))))
364 return("Write Error");
369 switch(a
->body
->type
){
373 * If a message is multipart *and* the first part of it
374 * is text *and that text is empty, there is a good chance that
375 * there was actually something there that c-client was
376 * unable to parse. Here we report the empty message body
377 * and insert the raw RFC822.TEXT (if full-headers are
380 if(body
->type
== TYPEMULTIPART
381 && a
== ps_global
->atmts
382 && a
->body
->size
.bytes
== 0
383 && F_ON(F_ENABLE_FULL_HDR
, ps_global
)){
386 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
387 "Empty or malformed message%s.",
388 ps_global
->full_header
== 2
389 ? ". Displaying raw text"
390 : ". Use \"H\" to see raw text");
391 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
393 if(!(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
394 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
395 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
396 return("Write Error");
398 if(ps_global
->full_header
== 2
399 && (err
= detach_raw(ps_global
->mail_stream
, msgno
,
400 a
->number
, pc
, flgs
))){
401 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
402 "%s%s [ Formatting error: %s ]%s%s",
403 NEWLINE
, NEWLINE
, err
, NEWLINE
, NEWLINE
);
404 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
405 if(!gf_puts(tmp_20k_buf
, pc
))
406 return("Write Error");
413 * Don't write our delimiter if this text part is
414 * the first part of a message/rfc822 segment...
416 if(show_parts
&& a
!= ps_global
->atmts
417 && !((a
[-1].body
&& a
[-1].body
->type
== TYPEMESSAGE
)
419 || (a
[-1].body
->type
== TYPEMULTIPART
420 && a
[-1].body
->subtype
421 && (strucmp(a
[-1].body
->subtype
, OUR_PKCS7_ENCLOSURE_SUBTYPE
)==0)
422 && &a
[-1] != ps_global
->atmts
423 && a
[-2].body
&& a
[-2].body
->type
== TYPEMESSAGE
)
426 && !(flgs
& FM_NOEDITORIAL
)){
427 tmp1
= a
->body
->description
? a
->body
->description
429 description
= iutf8ncpy((char *)(tmp_20k_buf
+10000),
430 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf
+15000), 5000, tmp1
), 5000);
432 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "Part %s: \"%.1024s\"", a
->number
,
434 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
435 if(!(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
436 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
437 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
438 return("Write Error");
441 error_found
+= decode_text(a
, msgno
, pc
, handlesp
,
442 (flgs
& FM_DISPLAY
) ? InLine
: QStatus
,
447 tmp1
= a
->body
->description
? a
->body
->description
448 : (strucmp(a
->body
->subtype
, "delivery-status") == 0)
450 : "Included Message";
451 description
= iutf8ncpy((char *)(tmp_20k_buf
+10000),
452 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf
+15000), 5000, tmp1
), 5000);
454 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "Part %s: \"%.1024s\"", a
->number
,
456 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
458 if(!(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
459 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
460 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
461 return("Write Error");
463 if(a
->body
->subtype
&& strucmp(a
->body
->subtype
, "rfc822") == 0){
464 /* imapenvonly, we may not have all the headers we need */
465 if(a
->body
->nested
.msg
->env
->imapenvonly
)
466 mail_fetch_header(ps_global
->mail_stream
, msgno
,
467 a
->number
, NULL
, NULL
, FT_PEEK
);
468 switch(format_header(ps_global
->mail_stream
, msgno
, a
->number
,
469 a
->body
->nested
.msg
->env
, hp
,
470 NULL
, handlesp
, flgs
, NULL
, pc
)){
471 case -1 : /* write error */
472 return("Write Error");
474 case 1 : /* fetch error */
475 if(!(gf_puts("[ Error fetching header ]", pc
)
476 && !gf_puts(NEWLINE
, pc
)))
477 return("Write Error");
482 else if(a
->body
->subtype
&& strucmp(a
->body
->subtype
, "external-body") == 0){
483 int *margin
, avail
, m1
, m2
;
486 margin
= (flgs
& FM_NOINDENT
) ? NULL
: format_view_margin();
488 m1
= MAX(MIN(margin
? margin
[0] : 0, avail
), 0);
491 m2
= MAX(MIN(margin
? margin
[1] : 0, avail
), 0);
494 if(format_editorial("This part is not included and can be fetched as follows:", avail
, flgs
, handlesp
, pc
)
495 || !gf_puts(NEWLINE
, pc
)
496 || format_editorial(display_parameters(a
->body
->parameter
), avail
, flgs
, handlesp
, pc
))
497 return("Write Error");
500 error_found
+= decode_text(a
, msgno
, pc
, handlesp
,
501 (flgs
&FM_DISPLAY
) ? InLine
: QStatus
,
504 if(!gf_puts(NEWLINE
, pc
))
505 return("Write Error");
510 if((decode_err
= part_desc(a
->number
, a
->body
,
511 (flgs
& FM_DISPLAY
) ? 1 : 3,
512 width
, flgs
, pc
)) != NULL
)
513 return("Write Error");
520 && (pith_opt_rfc2369_editorial
? (*pith_opt_rfc2369_editorial
)(msgno
, handlesp
, flgs
, width
, pc
) : 1)
521 && format_blip_seen(msgno
)))
522 return("Cannot format body.");
530 format_attachment_list(long int msgno
, BODY
*body
, HANDLE_S
**handlesp
, int flgs
, int width
, gf_io_t pc
)
534 if(flgs
& FM_NEW_MESS
) {
535 zero_atmts(ps_global
->atmts
);
536 describe_mime(body
, "", 1, 1, 0, flgs
);
539 /*----- First do the list of parts/attachments if needed ----*/
540 if((flgs
& FM_DISPLAY
)
541 && (ps_global
->atmts
[1].description
542 || (ps_global
->atmts
[0].body
543 && ps_global
->atmts
[0].body
->type
!= TYPETEXT
))){
544 char tmp
[6*MAX_SCREEN_COLS
+ 1], *tmpp
;
545 int i
, n
, maxnumwid
= 0, maxsizewid
= 0, *margin
;
546 int avail
, m1
, m2
, hwid
, s1
, s2
, s3
, s4
, s5
, dwid
, shownwid
;
547 int sizewid
, descwid
, dashwid
, partwid
, padwid
;
548 COLOR_PAIR
*hdrcolor
= NULL
;
550 if((flgs
& FM_DISPLAY
)
551 && !(flgs
& FM_NOCOLOR
)
553 && ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
554 && ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
555 && ps_global
->VAR_NORM_FORE_COLOR
556 && ps_global
->VAR_NORM_BACK_COLOR
557 && (colorcmp(ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
,
558 ps_global
->VAR_NORM_FORE_COLOR
)
559 || colorcmp(ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
,
560 ps_global
->VAR_NORM_BACK_COLOR
))){
562 if((hdrcolor
= new_color_pair(ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
,
563 ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
)) != NULL
){
564 if(!pico_is_good_colorpair(hdrcolor
))
565 free_color_pair(&hdrcolor
);
569 margin
= (flgs
& FM_NOINDENT
) ? NULL
: format_view_margin();
572 * Attachment list header
577 m1
= MAX(MIN(margin
? margin
[0] : 0, avail
), 0);
580 m2
= MAX(MIN(margin
? margin
[1] : 0, avail
), 0);
583 hwid
= MAX(avail
, 0);
585 i
= utf8_width(_("Parts/Attachments:"));
586 partwid
= MIN(i
, hwid
);
587 padwid
= hdrcolor
? (hwid
-partwid
) : 0;
590 snprintf(tmp
, sizeof(tmp
), "%*.*s", m1
, m1
, "");
591 if(!gf_puts(tmp
, pc
))
595 utf8_snprintf(tmp
, sizeof(tmp
),
597 /* TRANSLATORS: A label */
598 partwid
, partwid
, _("Parts/Attachments:"),
601 if(!((!hdrcolor
|| embed_color(hdrcolor
, pc
)) && gf_puts(tmp
, pc
) && gf_puts(NEWLINE
, pc
)))
605 /*----- Figure max display widths -----*/
606 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++){
607 if((n
= utf8_width(a
->number
)) > maxnumwid
)
610 if((n
= utf8_width(a
->size
)) > maxsizewid
)
615 * ----- adjust max lengths for nice display -----
617 * marg _ D _ number _ Shown _ _ _ size _ _ description marg
621 avail
= width
- m1
- m2
;
623 s1
= MAX(MIN(1, avail
), 0);
626 dwid
= MAX(MIN(1, avail
), 0);
629 s2
= MAX(MIN(1, avail
), 0);
632 maxnumwid
= MIN(maxnumwid
, width
/3);
633 maxnumwid
= MAX(MIN(maxnumwid
, avail
), 0);
636 s3
= MAX(MIN(1, avail
), 0);
639 shownwid
= MAX(MIN(5, avail
), 0);
642 s4
= MAX(MIN(3, avail
), 0);
645 sizewid
= MAX(MIN(maxsizewid
, avail
), 0);
648 s5
= MAX(MIN(2, avail
), 0);
651 descwid
= MAX(0, avail
);
653 /*----- Format the list of attachments -----*/
654 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++){
655 COLOR_PAIR
*lastc
= NULL
;
657 int thisdescwid
, padwid
;
660 if(a
->body
->type
== TYPEMULTIPART
661 && (strucmp(a
->body
->subtype
, OUR_PKCS7_ENCLOSURE_SUBTYPE
)==0))
665 i
= utf8_width((descwid
> 2 && a
->description
) ? a
->description
: "");
666 thisdescwid
= MIN(i
, descwid
);
667 padwid
= hdrcolor
? (descwid
-thisdescwid
) : 0;
670 snprintf(tmp
, sizeof(tmp
), "%*.*s", m1
, m1
, "");
671 if(!gf_puts(tmp
, pc
))
675 utf8_snprintf(tmp
, sizeof(tmp
),
676 "%*.*s%*.*w%*.*s%-*.*w%*.*s%*.*w%*.*s%*.*w%*.*s%-*.*w",
679 msgno_part_deleted(ps_global
->mail_stream
, msgno
, a
->number
) ? "D" : "",
681 maxnumwid
, maxnumwid
,
683 ? short_str(a
->number
, numbuf
, sizeof(numbuf
), maxnumwid
, FrontDots
)
688 (a
->can_display
!= MCD_NONE
&& !(a
->can_display
& MCD_EXT_PROMPT
))
692 a
->size
? a
->size
: "",
694 thisdescwid
, thisdescwid
,
695 (descwid
> 2 && a
->description
) ? a
->description
: "");
697 if(!(!hdrcolor
|| embed_color(hdrcolor
, pc
)))
700 if(F_ON(F_VIEW_SEL_ATTACH
, ps_global
) && handlesp
){
701 char buf
[16], color
[64];
705 for(tmpp
= tmp
; *tmpp
&& *tmpp
== ' '; tmpp
++)
709 h
= new_handle(handlesp
);
713 snprintf(buf
, sizeof(buf
), "%d", h
->key
);
714 buf
[sizeof(buf
)-1] = '\0';
716 if(!(flgs
& FM_NOCOLOR
)
717 && handle_start_color(color
, sizeof(color
), &l
, 1)){
718 lastc
= get_cur_embedded_color();
719 if(!gf_nputs(color
, (long) l
, pc
))
722 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)
723 && (!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDON
))))
726 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_HANDLE
)
727 && (*pc
)(strlen(buf
)) && gf_puts(buf
, pc
)))
733 if(!format_env_puts(tmpp
, pc
))
736 if(F_ON(F_VIEW_SEL_ATTACH
, ps_global
) && handlesp
){
738 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
739 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
)))
743 if(!embed_color(lastc
, pc
))
746 free_color_pair(&lastc
);
748 else if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
)))
751 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_INVOFF
)))
756 snprintf(tmp
, sizeof(tmp
), "%*.*s", padwid
, padwid
, "");
757 if(!gf_puts(tmp
, pc
))
761 if(!gf_puts(NEWLINE
, pc
))
766 * Dashed line after list
770 avail
= width
- m1
- m2
;
771 hwid
= MAX(avail
, 0);
773 dashwid
= MAX(MIN(40, hwid
-2), 0);
774 padwid
= hwid
- dashwid
;
776 snprintf(tmp
, sizeof(tmp
), "%*.*s", m1
, m1
, "");
777 if(!gf_puts(tmp
, pc
))
781 snprintf(tmp
, sizeof(tmp
),
783 repeat_char(dashwid
, '-'),
787 avail
= width
- m1
-2;
789 dashwid
= MAX(MIN(40, avail
), 0);
792 snprintf(tmp
, sizeof(tmp
),
795 repeat_char(dashwid
, '-'));
798 if(!((!hdrcolor
|| embed_color(hdrcolor
, pc
)) && gf_puts(tmp
, pc
) && gf_puts(NEWLINE
, pc
)))
802 free_color_pair(&hdrcolor
);
811 * format_blip_seen - if seen bit (which is usually cleared as a side-effect
812 * of body part fetches as we're formatting) for the
813 * given message isn't set (likely because there
814 * weren't any parts suitable for display), then make
815 * sure to set it here.
818 format_blip_seen(long int msgno
)
822 if(msgno
> 0L && ps_global
->mail_stream
823 && msgno
<= ps_global
->mail_stream
->nmsgs
824 && (mc
= mail_elt(ps_global
->mail_stream
, msgno
))
826 && !ps_global
->mail_stream
->rdonly
)
827 mail_flag(ps_global
->mail_stream
, long2string(msgno
), "\\SEEN", ST_SET
);
834 * is_an_env_hdr - is this name a header in the envelope structure?
836 * name - the header name to check
839 is_an_env_hdr(char *name
)
843 for(i
= 0; envelope_hdrs
[i
].name
; i
++)
844 if(!strucmp(name
, envelope_hdrs
[i
].name
))
854 * is_an_addr_hdr - is this an address header?
856 * name - the header name to check
859 is_an_addr_hdr(char *fieldname
)
861 char fbuf
[FBUF_LEN
+1];
863 static char *addr_headers
[] = {
879 /* so it is pointing to NULL */
880 char **p
= addr_headers
+ sizeof(addr_headers
)/sizeof(*addr_headers
) - 1;
882 if((colon
= strindex(fieldname
, ':')) != NULL
){
883 strncpy(fbuf
, fieldname
, MIN(colon
-fieldname
,sizeof(fbuf
)));
884 fbuf
[MIN(colon
-fieldname
,sizeof(fbuf
)-1)] = '\0';
891 for(p
= addr_headers
; *p
; p
++)
892 if(!strucmp(fname
, *p
))
896 return((*p
) ? 1 : 0);
901 * Format a single field from the envelope
904 format_env_hdr(MAILSTREAM
*stream
, long int msgno
, char *section
, ENVELOPE
*env
,
905 fmt_env_t fmt_env
, gf_io_t pc
, char *field
, char *oacs
, int flags
)
910 fmt_env
= format_envelope
;
912 for(i
= 0; envelope_hdrs
[i
].name
; i
++)
913 if(!strucmp(field
, envelope_hdrs
[i
].name
)){
914 (*fmt_env
)(stream
, msgno
, section
, env
, pc
, envelope_hdrs
[i
].val
, oacs
, flags
);
921 * Look through header string beginning with "begin", for the next
922 * occurrence of header "field". Set "start" to that. Set "end" to point one
923 * position past all of the continuation lines that go with "field".
924 * That is, if "end" is converted to a null
925 * character then the string "start" will be the next occurence of header
926 * "field" including all of its continuation lines. Assume we
927 * have CRLF's as end of lines.
929 * If "field" is NULL, then we just leave "start" pointing to "begin" and
930 * make "end" the end of that header.
932 * Returns 1 if found, 0 if not.
935 delineate_this_header(char *field
, char *begin
, char **start
, char **end
)
937 char tmpfield
[MAILTMPLEN
+2]; /* copy of field with colon appended */
942 if(!begin
|| !*begin
|| isspace((unsigned char)*begin
))
948 strncpy(tmpfield
, field
, sizeof(tmpfield
)-2);
949 tmpfield
[sizeof(tmpfield
)-2] = '\0';
950 strncat(tmpfield
, ":", sizeof(tmpfield
)-strlen(tmpfield
)-1);
951 tmpfield
[sizeof(tmpfield
)-1] = '\0';
954 * We require that start is at the beginning of a line, so
955 * either it equals begin (which we assume is the beginning of a
956 * line) or it is preceded by a CRLF.
959 *start
= srchstr(begin_srch
, tmpfield
);
960 while(*start
&& *start
!= begin
961 && !(*start
- 2 >= begin
&& ISRFCEOL(*start
- 2))){
962 begin_srch
= *start
+ 1;
963 *start
= srchstr(begin_srch
, tmpfield
);
970 for(p
= *start
; *p
; p
++){
972 && (!isspace((unsigned char)*(p
+2)) || *(p
+2) == '\015')){
974 * The final 015 in the test above is to test for the end
991 handle_start_color(char *colorstring
, size_t buflen
, int *len
, int use_hdr_color
)
995 if(pico_usingcolor()){
996 char *fg
= NULL
, *bg
= NULL
, *s
;
997 char *basefg
= NULL
, *basebg
= NULL
;
999 basefg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
1000 : ps_global
->VAR_NORM_FORE_COLOR
;
1001 basebg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
1002 : ps_global
->VAR_NORM_BACK_COLOR
;
1004 if(ps_global
->VAR_SLCTBL_FORE_COLOR
1005 && colorcmp(ps_global
->VAR_SLCTBL_FORE_COLOR
, basefg
))
1006 fg
= ps_global
->VAR_SLCTBL_FORE_COLOR
;
1008 if(ps_global
->VAR_SLCTBL_BACK_COLOR
1009 && colorcmp(ps_global
->VAR_SLCTBL_BACK_COLOR
, basebg
))
1010 bg
= ps_global
->VAR_SLCTBL_BACK_COLOR
;
1016 * The blacks are just known good colors for
1017 * testing whether the other color is good.
1019 if((tmp
= new_color_pair(fg
? fg
: colorx(COL_BLACK
),
1020 bg
? bg
: colorx(COL_BLACK
))) != NULL
){
1021 if(pico_is_good_colorpair(tmp
))
1022 for(s
= color_embed(fg
, bg
);
1023 (*len
) < buflen
&& (colorstring
[*len
] = *s
);
1027 free_color_pair(&tmp
);
1031 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
1032 strncpy(colorstring
+ (*len
), url_embed(TAG_BOLDON
), MIN(3,buflen
-(*len
)));
1037 colorstring
[buflen
-1] = '\0';
1044 handle_end_color(char *colorstring
, size_t buflen
, int *len
)
1047 if(pico_usingcolor()){
1048 char *fg
= NULL
, *bg
= NULL
, *s
;
1051 * We need to change the fg and bg colors back even if they
1052 * are the same as the Normal Colors so that color_a_quote
1053 * will have a chance to pick up those colors as the ones to
1054 * switch to. We don't do this before the handle above so that
1055 * the quote color will flow into the selectable item when
1056 * the selectable item color is partly the same as the
1057 * normal color. That is, suppose the normal color was black on
1058 * cyan and the selectable color was blue on cyan, only a fg color
1059 * change. We preserve the only-a-fg-color-change in a quote by
1060 * letting the quote background color flow into the selectable text.
1062 if(ps_global
->VAR_SLCTBL_FORE_COLOR
)
1063 fg
= ps_global
->VAR_NORM_FORE_COLOR
;
1065 if(ps_global
->VAR_SLCTBL_BACK_COLOR
)
1066 bg
= ps_global
->VAR_NORM_BACK_COLOR
;
1068 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
1069 strncpy(colorstring
, url_embed(TAG_BOLDOFF
), MIN(3,buflen
));
1074 for(s
= color_embed(fg
, bg
); (*len
) < buflen
&& (colorstring
[*len
] = *s
); s
++, (*len
)++)
1078 colorstring
[buflen
-1] = '\0';
1085 url_embed(int embed
)
1087 static char buf
[3] = {TAG_EMBED
};
1095 * Paint the signature.
1098 color_signature(long int linenum
, char *line
, LT_INS_S
**ins
, void *is_in_sig
)
1100 struct variable
*vars
= ps_global
->vars
;
1102 COLOR_PAIR
*col
= NULL
;
1104 if(is_in_sig
== NULL
)
1107 in_sig_block
= (int *) is_in_sig
;
1109 if(!strcmp(line
, SIGDASHES
))
1110 *in_sig_block
= START_SIG_BLOCK
;
1111 else if(*line
== '\0')
1113 * Suggested by Eduardo: allow for a blank line right after
1116 *in_sig_block
= (*in_sig_block
== START_SIG_BLOCK
)
1117 ? IN_SIG_BLOCK
: OUT_SIG_BLOCK
;
1119 *in_sig_block
= (*in_sig_block
!= OUT_SIG_BLOCK
)
1120 ? IN_SIG_BLOCK
: OUT_SIG_BLOCK
;
1122 if(*in_sig_block
!= OUT_SIG_BLOCK
1123 && VAR_SIGNATURE_FORE_COLOR
&& VAR_SIGNATURE_BACK_COLOR
1124 && (col
= new_color_pair(VAR_SIGNATURE_FORE_COLOR
,
1125 VAR_SIGNATURE_BACK_COLOR
))){
1126 if(!pico_is_good_colorpair(col
))
1127 free_color_pair(&col
);
1131 char *p
, fg
[RGBLEN
+ 1], bg
[RGBLEN
+ 1], rgbbuf
[RGBLEN
+ 1];
1133 ins
= gf_line_test_new_ins(ins
, line
,
1134 color_embed(col
->fg
, col
->bg
),
1137 strncpy(fg
, color_to_asciirgb(VAR_NORM_FORE_COLOR
), sizeof(fg
));
1138 fg
[sizeof(fg
)-1] = '\0';
1139 strncpy(bg
, color_to_asciirgb(VAR_NORM_BACK_COLOR
), sizeof(bg
));
1140 bg
[sizeof(bg
)-1] = '\0';
1143 * Loop watching colors, and override with
1144 * signature color whenever the normal foreground and background
1145 * colors are in force.
1149 if(*p
++ == TAG_EMBED
){
1153 p
+= *p
+ 1; /* skip handle key */
1157 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1158 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1159 p
+= RGBLEN
; /* advance past color value */
1161 if(!colorcmp(rgbbuf
, VAR_NORM_FORE_COLOR
)
1162 && !colorcmp(bg
, VAR_NORM_BACK_COLOR
))
1163 ins
= gf_line_test_new_ins(ins
, p
,
1164 color_embed(col
->fg
,NULL
),
1169 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1170 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1171 p
+= RGBLEN
; /* advance past color value */
1173 if(!colorcmp(rgbbuf
, VAR_NORM_BACK_COLOR
)
1174 && !colorcmp(fg
, VAR_NORM_FORE_COLOR
))
1175 ins
= gf_line_test_new_ins(ins
, p
,
1176 color_embed(NULL
,col
->bg
),
1186 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
1187 color_embed(VAR_NORM_FORE_COLOR
,
1188 VAR_NORM_BACK_COLOR
),
1190 free_color_pair(&col
);
1198 * Line filter to add color to displayed headers.
1201 color_headers(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1203 static char field
[FBUF_LEN
+ 1];
1204 char fg
[RGBLEN
+ 1], bg
[RGBLEN
+ 1], rgbbuf
[RGBLEN
+ 1];
1205 char *p
, *q
, *value
, *beg
;
1207 int in_quote
= 0, in_comment
= 0, did_color
= 0;
1208 struct variable
*vars
= ps_global
->vars
;
1210 field
[FBUF_LEN
] = '\0';
1212 if(isspace((unsigned char)*line
)) /* continuation line */
1215 if(!(value
= strindex(line
, ':')))
1218 memset(field
, 0, sizeof(field
));
1219 strncpy(field
, line
, MIN(value
-line
, sizeof(field
)-1));
1222 for(value
++; isspace((unsigned char)*value
); value
++)
1225 strncpy(fg
, color_to_asciirgb(VAR_HEADER_GENERAL_FORE_COLOR
), sizeof(fg
));
1226 fg
[sizeof(fg
)-1] = '\0';
1227 strncpy(bg
, color_to_asciirgb(VAR_HEADER_GENERAL_BACK_COLOR
), sizeof(bg
));
1228 bg
[sizeof(bg
)-1] = '\0';
1231 * Split into two cases depending on whether this is a header which
1232 * contains addresses or not. We may color addresses separately.
1234 if(is_an_addr_hdr(field
)){
1237 * If none of the patterns are for this header, don't bother parsing
1238 * and checking each address.
1240 if(!any_hdr_color(field
))
1244 * First check for patternless patterns which color whole line.
1246 if((color
= hdr_color(field
, NULL
, ps_global
->hdr_colors
)) != NULL
){
1247 if(pico_is_good_colorpair(color
)){
1248 ins
= gf_line_test_new_ins(ins
, value
,
1249 color_embed(color
->fg
, color
->bg
),
1251 strncpy(fg
, color_to_asciirgb(color
->fg
), sizeof(fg
));
1252 fg
[sizeof(fg
)-1] = '\0';
1253 strncpy(bg
, color_to_asciirgb(color
->bg
), sizeof(bg
));
1254 bg
[sizeof(bg
)-1] = '\0';
1258 free_color_pair(&color
);
1262 * Then go through checking address by address.
1263 * Keep track of quotes and watch for color changes, and override
1264 * with most recent header color whenever the normal foreground
1265 * and background colors are in force.
1271 /* skip next character */
1272 if(*(p
+1) && (in_comment
|| in_quote
))
1281 in_quote
= 1 - in_quote
;
1299 if(!(in_quote
|| in_comment
)){
1300 /* we reached the end of this address */
1303 free_color_pair(&color
);
1305 if((color
= hdr_color(field
, beg
,
1306 ps_global
->hdr_colors
)) != NULL
){
1307 if(pico_is_good_colorpair(color
)){
1309 ins
= gf_line_test_new_ins(ins
, beg
,
1310 color_embed(color
->fg
,
1314 for(q
= p
; q
> beg
&&
1315 isspace((unsigned char)*(q
-1)); q
--)
1318 ins
= gf_line_test_new_ins(ins
, q
,
1319 color_embed(fg
, bg
),
1323 free_color_pair(&color
);
1328 for(p
++; isspace((unsigned char)*p
); p
++)
1342 p
+= *p
+ 1; /* skip handle key */
1347 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1348 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1349 p
+= RGBLEN
; /* advance past color value */
1351 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_FORE_COLOR
))
1352 ins
= gf_line_test_new_ins(ins
, p
,
1353 color_embed(color
->fg
,NULL
),
1359 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1360 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1361 p
+= RGBLEN
; /* advance past color value */
1363 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_BACK_COLOR
))
1364 ins
= gf_line_test_new_ins(ins
, p
,
1365 color_embed(NULL
,color
->bg
),
1382 for(q
= beg
; *q
&& isspace((unsigned char)*q
); q
++)
1385 if(*q
&& !(in_quote
|| in_comment
)){
1386 /* we reached the end of this address */
1388 free_color_pair(&color
);
1390 if((color
= hdr_color(field
, beg
, ps_global
->hdr_colors
)) != NULL
){
1391 if(pico_is_good_colorpair(color
)){
1393 ins
= gf_line_test_new_ins(ins
, beg
,
1394 color_embed(color
->fg
,
1397 for(q
= p
; q
> beg
&& isspace((unsigned char)*(q
-1)); q
--)
1400 ins
= gf_line_test_new_ins(ins
, q
,
1401 color_embed(fg
, bg
),
1405 free_color_pair(&color
);
1410 free_color_pair(&color
);
1413 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
1414 color_embed(VAR_HEADER_GENERAL_FORE_COLOR
,
1415 VAR_HEADER_GENERAL_BACK_COLOR
),
1420 color
= hdr_color(field
, value
, ps_global
->hdr_colors
);
1423 if(pico_is_good_colorpair(color
)){
1424 ins
= gf_line_test_new_ins(ins
, value
,
1425 color_embed(color
->fg
, color
->bg
),
1429 * Loop watching colors, and override with header
1430 * color whenever the normal foreground and background
1431 * colors are in force.
1435 if(*p
++ == TAG_EMBED
){
1439 p
+= *p
+ 1; /* skip handle key */
1443 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1444 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1445 p
+= RGBLEN
; /* advance past color value */
1447 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_FORE_COLOR
)
1448 && !colorcmp(bg
, VAR_HEADER_GENERAL_BACK_COLOR
))
1449 ins
= gf_line_test_new_ins(ins
, p
,
1450 color_embed(color
->fg
,NULL
),
1455 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1456 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1457 p
+= RGBLEN
; /* advance past color value */
1459 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_BACK_COLOR
)
1460 && !colorcmp(fg
, VAR_HEADER_GENERAL_FORE_COLOR
))
1461 ins
= gf_line_test_new_ins(ins
, p
,
1462 color_embed(NULL
,color
->bg
),
1472 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
1473 color_embed(VAR_HEADER_GENERAL_FORE_COLOR
,
1474 VAR_HEADER_GENERAL_BACK_COLOR
),
1478 free_color_pair(&color
);
1487 url_hilite(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1489 register char *lp
, *up
= NULL
, *urlp
= NULL
,
1490 *weburlp
= NULL
, *mailurlp
= NULL
;
1491 int n
, n1
, n2
, n3
, l
;
1492 char buf
[256], color
[256];
1496 for(lp
= line
; ; lp
= up
+ n
){
1497 /* scan for all of them so we can choose the first */
1498 if(F_ON(F_VIEW_SEL_URL
,ps_global
))
1499 urlp
= rfc1738_scan(lp
, &n1
);
1500 if(F_ON(F_VIEW_SEL_URL_HOST
,ps_global
))
1501 weburlp
= web_host_scan(lp
, &n2
);
1502 if(F_ON(F_SCAN_ADDR
,ps_global
))
1503 mailurlp
= mail_addr_scan(lp
, &n3
);
1505 if(urlp
|| weburlp
|| mailurlp
){
1507 weburlp
? weburlp
: mailurlp
;
1508 if(up
== urlp
&& weburlp
&& weburlp
< up
)
1510 if(mailurlp
&& mailurlp
< up
)
1515 weburlp
= mailurlp
= NULL
;
1517 else if(up
== weburlp
){
1529 uh
= (URL_HILITE_S
*) local
;
1531 h
= new_handle(uh
->handlesp
);
1533 h
->h
.url
.path
= (char *) fs_get((n
+ 10) * sizeof(char));
1534 snprintf(h
->h
.url
.path
, n
+10, "%s%.*s",
1535 weburlp
? "http://" : (mailurlp
? "mailto:" : ""), n
, up
);
1536 h
->h
.url
.path
[n
+10-1] = '\0';
1538 if(handle_start_color(color
, sizeof(color
), &l
, uh
->hdr_color
))
1539 ins
= gf_line_test_new_ins(ins
, up
, color
, l
);
1540 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
))
1541 ins
= gf_line_test_new_ins(ins
, up
, url_embed(TAG_BOLDON
), 2);
1544 buf
[1] = TAG_HANDLE
;
1545 snprintf(&buf
[3], sizeof(buf
)-3, "%d", h
->key
);
1546 buf
[sizeof(buf
)-1] = '\0';
1547 buf
[2] = strlen(&buf
[3]);
1548 ins
= gf_line_test_new_ins(ins
, up
, buf
, (int) buf
[2] + 3);
1550 /* in case it was the current selection */
1551 ins
= gf_line_test_new_ins(ins
, up
+ n
, url_embed(TAG_INVOFF
), 2);
1553 if(scroll_handle_end_color(color
, sizeof(color
), &l
, uh
->hdr_color
))
1554 ins
= gf_line_test_new_ins(ins
, up
+ n
, color
, l
);
1556 ins
= gf_line_test_new_ins(ins
, up
+ n
, url_embed(TAG_BOLDOFF
), 2);
1558 urlp
= weburlp
= mailurlp
= NULL
;
1566 url_hilite_hdr(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1568 static int check_for_urls
= 0;
1571 if(isspace((unsigned char)*line
)) /* continuation, check or not
1572 depending on last line */
1576 if((lp
= strchr(line
, ':')) != NULL
){ /* there ought to always be a colon */
1581 if(((ft
= pine_header_standard(line
)) == FreeText
1583 || ft
== TypeUnknown
)
1584 && strucmp(line
, "message-id")
1585 && strucmp(line
, "newsgroups")
1586 && strucmp(line
, "references")
1587 && strucmp(line
, "in-reply-to")
1588 && strucmp(line
, "received")
1589 && strucmp(line
, "date")){
1598 (void) url_hilite(linenum
, lp
+ 1, ins
, local
);
1605 pad_to_right_edge(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1610 struct variable
*vars
= ps_global
->vars
;
1615 total_wid
= *((int *) local
);
1617 /* calculate width of line */
1627 p
+= *p
+ 1; /* skip handle key */
1632 p
+= (RGBLEN
+ 1); /* 1 for TAG, RGBLEN for color */
1644 default: /* literal embed char */
1652 while(((++wid
) & 0x07) != 0) /* add tab's spaces */
1658 wid
+= width_at_this_position((unsigned char *) p
, strlen(p
));
1664 if(total_wid
> wid
){
1665 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
1666 color_embed(VAR_HEADER_GENERAL_FORE_COLOR
,
1667 VAR_HEADER_GENERAL_BACK_COLOR
),
1669 ins
= gf_line_test_new_ins(ins
, line
+strlen(line
),
1670 repeat_char(total_wid
-wid
, ' '), total_wid
-wid
);
1671 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
1672 color_embed(VAR_NORM_FORE_COLOR
,
1673 VAR_NORM_BACK_COLOR
),
1685 url_external_specific_handler(char *url
, int len
)
1687 static char list
[UES_LEN
* UES_MAX
];
1693 for(i
= 0; i
< UES_MAX
&& *(p
= &list
[i
* UES_LEN
]); i
++)
1694 if(strlen(p
) == len
&& !struncmp(p
, url
, len
))
1697 else{ /* initialize! */
1698 char **l
, *test
, *cmd
, *p
, *p2
;
1701 memset(list
, 0, sizeof(list
));
1702 for(l
= ps_global
->VAR_BROWSER
; l
&& *l
; l
++){
1703 get_pair(*l
, &test
, &cmd
, 1, 1);
1705 if((p
= srchstr(test
, "_scheme(")) && (p2
= strstr(p
+8, ")_"))){
1708 for(p
+= 8; *p
&& i
< UES_MAX
; p
+= n
)
1709 if((p2
= strchr(p
, ',')) != NULL
){
1710 if((n
= p2
- p
) < UES_LEN
){
1711 strncpy(&list
[i
* UES_LEN
], p
, MIN(n
, sizeof(list
)-(i
* UES_LEN
)));
1716 "* * * HANLDER TOO LONG: %.*s\n", n
,
1722 if(strlen(p
) <= UES_LEN
){
1723 strncpy(&list
[i
* UES_LEN
], p
, sizeof(list
)-(i
* UES_LEN
));
1732 fs_give((void **) &test
);
1735 fs_give((void **) &cmd
);
1744 url_imap_folder(char *true_url
, char **folder
, imapuid_t
*uid_val
,
1745 imapuid_t
*uid
, char **search
, int silent
)
1747 char *url
, *scheme
, *p
, *cmd
, *server
= NULL
,
1748 *user
= NULL
, *auth
= NULL
, *mailbox
= NULL
,
1751 int rv
= URL_IMAP_ERROR
;
1754 * Since we're planting nulls, operate on a temporary copy...
1756 scheme
= silent
? NULL
: "IMAP";
1757 url
= cpystr(true_url
+ 7);
1759 /* Try to pick apart the "iserver" portion */
1760 if((cmd
= strchr(url
, '/')) != NULL
){ /* iserver "/" [mailbox] ? */
1764 dprint((2, "-- URL IMAP FOLDER: missing: %s\n",
1766 cmd
= &url
[strlen(url
)-1]; /* assume only iserver */
1769 if((p
= strchr(url
, '@')) != NULL
){ /* user | auth | pass? */
1771 server
= rfc1738_str(p
);
1773 /* only ";auth=*" supported (and also ";auth=anonymous") */
1774 if((p
= srchstr(url
, ";auth=")) != NULL
){
1776 auth
= rfc1738_str(p
+ 6);
1780 user
= rfc1738_str(url
);
1783 server
= rfc1738_str(url
);
1786 return(url_bogus_imap(&url
, scheme
, "No server specified"));
1789 * "iserver" in hand, pick apart the "icommand"...
1792 if(!*cmd
|| (p
= srchstr(cmd
, ";type="))){
1796 * No "icommand" (all top-level folders) or "imailboxlist"...
1799 *p
= '\0'; /* tie off criteria */
1800 criteria
= rfc1738_str(cmd
); /* get "enc_list_mailbox" */
1801 if(!strucmp(p
= rfc1738_str(p
+6), "lsub"))
1802 rv
|= URL_IMAP_IMBXLSTLSUB
;
1803 else if(strucmp(p
, "list"))
1804 return(url_bogus_imap(&url
, scheme
,
1805 "Invalid list type specified"));
1808 rv
|= URL_IMAP_ISERVERONLY
;
1812 /* build folder list from specified server/criteria/list-method */
1813 l
= strlen(server
) + strlen(criteria
) + 10 + (user
? (strlen(user
)+2) : 9);
1814 *folder
= (char *) fs_get((l
+1) * sizeof(char));
1815 snprintf(*folder
, l
+1, "{%s/%s%s%s}%s%s%s", server
,
1816 user
? "user=\"" : "Anonymous",
1819 *criteria
? "[" : "", criteria
, *criteria
? "[" : "");
1820 (*folder
)[l
] = '\0';
1821 rv
|= URL_IMAP_IMAILBOXLIST
;
1824 if((p
= srchstr(cmd
, "/;uid=")) != NULL
){ /* "imessagepart" */
1825 *p
= '\0'; /* tie off mailbox [uidvalidity] */
1826 if((section
= srchstr(p
+= 6, "/;section=")) != NULL
){
1827 *section
= '\0'; /* tie off UID */
1828 section
= rfc1738_str(section
+ 10);
1829 /* BUG: verify valid section spec ala rfc 2060 */
1831 "-- URL IMAP FOLDER: section not used: %s\n",
1832 section
? section
: "?"));
1835 if(!(*uid
= rfc1738_num(&p
)) || *p
) /* decode UID */
1836 return(url_bogus_imap(&url
, scheme
, "Invalid data in UID"));
1838 /* optional "uidvalidity"? */
1839 if((p
= srchstr(cmd
, ";uidvalidity=")) != NULL
){
1842 if(!(*uid_val
= rfc1738_num(&p
)) || *p
)
1843 return(url_bogus_imap(&url
, scheme
,
1844 "Invalid UIDVALIDITY"));
1847 mailbox
= rfc1738_str(cmd
);
1848 rv
= URL_IMAP_IMESSAGEPART
;
1850 else{ /* "imessagelist" */
1851 /* optional "uidvalidity"? */
1852 if((p
= srchstr(cmd
, ";uidvalidity=")) != NULL
){
1855 if(!(*uid_val
= rfc1738_num(&p
)) || *p
)
1856 return(url_bogus_imap(&url
, scheme
,
1857 "Invalid UIDVALIDITY"));
1860 /* optional "enc_search"? */
1861 if((p
= strchr(cmd
, '?')) != NULL
){
1864 *search
= cpystr(rfc1738_str(p
+ 1));
1865 /* BUG: verify valid search spec ala rfc 2060 */
1868 mailbox
= rfc1738_str(cmd
);
1869 rv
= URL_IMAP_IMESSAGELIST
;
1872 if(auth
&& *auth
!= '*' && strucmp(auth
, "anonymous"))
1873 q_status_message(SM_ORDER
, 3, 3,
1874 "Unsupported authentication method. Using standard login.");
1877 * At this point our structure should contain the
1878 * digested url. Now put it together for c-client...
1880 l
= strlen(server
) + 8 + (mailbox
? strlen(mailbox
) : 0)
1881 + (user
? (strlen(user
)+2) : 9);
1882 *folder
= (char *) fs_get((l
+1) * sizeof(char));
1883 snprintf(*folder
, l
+1, "{%s%s%s%s%s}%s", server
,
1884 (user
|| !(auth
&& strucmp(auth
, "anonymous"))) ? "/" : "",
1885 user
? "user=\"" : ((auth
&& strucmp(auth
, "anonymous")) ? "" : "Anonymous"),
1889 (*folder
)[l
] = '\0';
1892 fs_give((void **) &url
);
1898 url_bogus_imap(char **freeme
, char *url
, char *problem
)
1900 fs_give((void **) freeme
);
1901 (void) url_bogus(url
, problem
);
1902 return(URL_IMAP_ERROR
);
1907 * url_bogus - report url syntax errors and such
1910 url_bogus(char *url
, char *reason
)
1912 dprint((2, "-- bogus url \"%s\": %s\n",
1913 url
? url
: "<NULL URL>", reason
? reason
: "?"));
1915 q_status_message3(SM_ORDER
|SM_DING
, 2, 3,
1916 "Malformed \"%.*s\" URL: %.200s",
1917 (void *) (strchr(url
, ':') - url
), url
, reason
);
1924 /*----------------------------------------------------------------------
1925 Format header text suitable for display
1927 Args: stream -- mail stream for various header text fetches
1928 msgno -- sequence number in stream of message we're interested in
1929 section -- which section of message
1930 env -- pointer to msg's envelope
1931 hdrs -- struct containing what's to get formatted
1932 prefix -- prefix to append to each output line
1933 handlesp -- address of pointer to the message's handles
1934 flags -- FM_ flags, see pith/mailview.h
1935 final_pc -- function to write header text with
1937 Result: 0 if all's well, -1 if write error, 1 if fetch error
1939 NOTE: Blank-line delimiter is NOT written here. Newlines are written
1940 in the local convention.
1944 #define FHT_WRTERR -1
1945 #define FHT_FTCHERR 1
1947 format_header(MAILSTREAM
*stream
, long int msgno
, char *section
, ENVELOPE
*env
,
1948 HEADER_S
*hdrs
, char *prefix
, HANDLE_S
**handlesp
, int flags
,
1949 fmt_env_t fmt_env
, gf_io_t final_pc
)
1953 char *h
= NULL
, **fields
= NULL
, **v
, *q
, *start
,
1957 gf_io_t tmp_pc
, tmp_gc
;
1958 struct variable
*vars
= ps_global
->vars
;
1960 if((tmp_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
)
1961 gf_set_so_writec(&tmp_pc
, tmp_store
);
1966 fmt_env
= format_envelope
;
1968 if(ps_global
->full_header
== 2){
1969 rv
= format_raw_header(stream
, msgno
, section
, tmp_pc
);
1973 * First, calculate how big a fields array we need.
1976 /* Custom header viewing list specified */
1977 if(hdrs
->type
== HD_LIST
){
1978 /* view all these headers */
1980 for(nfields
= 0, v
= hdrs
->h
.l
; (q
= *v
) != NULL
; v
++)
1981 if(!is_an_env_hdr(q
))
1984 /* view all except these headers */
1986 for(nfields
= 0, v
= hdrs
->h
.l
; *v
!= NULL
; v
++)
1990 nfields
--; /* subtract one for ALL_EXCEPT field */
1994 nfields
= 6; /* default view */
1996 /* allocate pointer space */
1998 fields
= (char **)fs_get((size_t)(nfields
+1) * sizeof(char *));
1999 memset(fields
, 0, (size_t)(nfields
+1) * sizeof(char *));
2002 if(hdrs
->type
== HD_LIST
){
2003 /* view all these headers */
2005 /* put the non-envelope headers in fields */
2007 for(i
= 0, v
= hdrs
->h
.l
; (q
= *v
) != NULL
; v
++)
2008 if(!is_an_env_hdr(q
))
2011 /* view all except these headers */
2013 /* put the list of headers not to view in fields */
2015 for(i
= 0, v
= hdrs
->h
.l
; (q
= *v
) != NULL
; v
++)
2016 if(strucmp(ALL_EXCEPT
, q
))
2024 fields
[i
= 0] = "Resent-Date";
2025 fields
[++i
] = "Resent-From";
2026 fields
[++i
] = "Resent-To";
2027 fields
[++i
] = "Resent-cc";
2028 fields
[++i
] = "Resent-Subject";
2034 /* custom view with exception list */
2035 if(hdrs
->type
== HD_LIST
&& hdrs
->except
){
2037 * Go through each header in h and print it.
2038 * First we check to see if it is an envelope header so we
2039 * can print our envelope version of it instead of the raw version.
2042 /* fetch all the other headers */
2044 h
= pine_fetchheader_lines_not(stream
, msgno
, section
, fields
);
2047 h
&& delineate_this_header(NULL
, current
, &start
, &finish
);
2049 char tmp
[MAILTMPLEN
+1];
2052 colon_loc
= strindex(start
, ':');
2053 if(colon_loc
&& colon_loc
< finish
){
2054 strncpy(tmp
, start
, MIN(colon_loc
-start
, sizeof(tmp
)-1));
2055 tmp
[MIN(colon_loc
-start
, sizeof(tmp
)-1)] = '\0';
2060 if(colon_loc
&& is_an_env_hdr(tmp
)){
2061 char *dummystart
, *dummyfinish
;
2064 * Pretty format for env hdrs.
2065 * If the same header appears more than once, only
2066 * print the last to avoid duplicates.
2067 * They should have been combined in the env when parsed.
2069 if(!delineate_this_header(tmp
, current
+1, &dummystart
,
2071 format_env_hdr(stream
, msgno
, section
, env
,
2072 fmt_env
, tmp_pc
, tmp
, hdrs
->charset
, flags
);
2075 if((rv
= format_raw_hdr_string(start
, finish
, tmp_pc
,
2076 hdrs
->charset
, flags
)) != 0)
2083 /* custom view or default */
2085 /* fetch the non-envelope headers */
2087 h
= pine_fetchheader_lines(stream
, msgno
, section
, fields
);
2089 /* default envelope for default view */
2090 if(hdrs
->type
== HD_BFIELD
)
2091 (*fmt_env
)(stream
, msgno
, section
, env
, tmp_pc
, hdrs
->h
.b
, hdrs
->charset
, flags
);
2093 /* go through each header in list, v initialized above */
2094 for(; (q
= *v
) != NULL
; v
++){
2095 if(is_an_env_hdr(q
)){
2096 /* pretty format for env hdrs */
2097 format_env_hdr(stream
, msgno
, section
, env
,
2098 fmt_env
, tmp_pc
, q
, hdrs
->charset
, flags
);
2102 * Go through h finding all occurences of this header
2103 * and all continuation lines, and output.
2106 h
&& delineate_this_header(q
,current
,&start
,&finish
);
2108 if((rv
= format_raw_hdr_string(start
, finish
, tmp_pc
,
2109 hdrs
->charset
, flags
)) != 0)
2122 gf_clear_so_writec(tmp_store
);
2124 if(!rv
){ /* valid data? Do wrapping and filtering... */
2126 char *errstr
, *display_filter
= NULL
, trigger
[MAILTMPLEN
];
2127 STORE_S
*df_store
= NULL
;
2129 so_seek(tmp_store
, 0L, 0);
2131 column
= (flags
& FM_DISPLAY
) ? ps_global
->ttyo
->screen_cols
: 80;
2134 * Test for and act on any display filter
2135 * This barely makes sense. The display filter is going
2136 * to be getting UTF-8'ized headers here. In pre-alpine
2137 * pine the display filter was being fed already decoded
2138 * headers in whatever character set they were in.
2139 * The good news is that that didn't make much
2140 * sense either, so this shouldn't break anything.
2141 * It seems unlikely that anybody is doing anything useful
2142 * with the header part of display filters.
2144 if(ps_global
->tools
.display_filter
2145 && ps_global
->tools
.display_filter_trigger
2146 && (display_filter
= (*ps_global
->tools
.display_filter_trigger
)(NULL
, trigger
, sizeof(trigger
)))){
2147 if((df_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
2149 gf_set_so_writec(&tmp_pc
, df_store
);
2150 gf_set_so_readc(&tmp_gc
, df_store
);
2151 if((errstr
= (*ps_global
->tools
.display_filter
)(display_filter
, tmp_store
, tmp_pc
, NULL
)) != NULL
){
2152 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
2153 _("Formatting error: %s"), errstr
);
2157 so_seek(df_store
, 0L, 0);
2159 gf_clear_so_writec(df_store
);
2162 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
2163 "No space for filtered text.");
2168 so_seek(tmp_store
, 0L, 0);
2169 gf_set_so_readc(&tmp_gc
, tmp_store
);
2173 int *margin
, wrapflags
= GFW_ONCOMMA
;
2176 gf_link_filter(gf_local_nvtnl
, NULL
);
2178 if((F_ON(F_VIEW_SEL_URL
, ps_global
)
2179 || F_ON(F_VIEW_SEL_URL_HOST
, ps_global
)
2180 || F_ON(F_SCAN_ADDR
, ps_global
))
2182 gf_link_filter(gf_line_test
,
2183 gf_line_test_opt(url_hilite_hdr
,
2184 gf_url_hilite_opt(&uh
,handlesp
,1)));
2185 wrapflags
|= GFW_HANDLES
;
2188 if((flags
& FM_DISPLAY
)
2189 && !(flags
& FM_NOCOLOR
)
2190 && pico_usingcolor()
2191 && ((VAR_NORM_FORE_COLOR
2192 && VAR_HEADER_GENERAL_FORE_COLOR
2193 && colorcmp(VAR_NORM_FORE_COLOR
, VAR_HEADER_GENERAL_FORE_COLOR
))
2195 (VAR_NORM_BACK_COLOR
2196 && VAR_HEADER_GENERAL_BACK_COLOR
2197 && colorcmp(VAR_NORM_BACK_COLOR
, VAR_HEADER_GENERAL_BACK_COLOR
))))
2198 wrapflags
|= GFW_HDRCOLOR
;
2200 if((flags
& FM_DISPLAY
)
2201 && !(flags
& FM_NOCOLOR
)
2202 && pico_usingcolor()
2203 && ps_global
->hdr_colors
){
2204 gf_link_filter(gf_line_test
,
2205 gf_line_test_opt(color_headers
, NULL
));
2206 wrapflags
|= (GFW_HANDLES
| GFW_HDRCOLOR
);
2209 if(prefix
&& *prefix
)
2210 column
= MAX(column
-strlen(prefix
), 50);
2212 margin
= format_view_margin();
2214 if(!(flags
& FM_NOWRAP
))
2215 gf_link_filter(gf_wrap
,
2216 gf_wrap_filter_opt(column
, column
,
2217 (flags
& FM_NOINDENT
) ? NULL
: margin
,
2220 if(prefix
&& *prefix
)
2221 gf_link_filter(gf_prefix
, gf_prefix_opt(prefix
));
2223 if((flags
& FM_DISPLAY
)
2224 && !(flags
& FM_NOCOLOR
)
2225 && pico_usingcolor()
2226 && ((VAR_NORM_FORE_COLOR
2227 && VAR_HEADER_GENERAL_FORE_COLOR
2228 && colorcmp(VAR_NORM_FORE_COLOR
, VAR_HEADER_GENERAL_FORE_COLOR
))
2230 (VAR_NORM_BACK_COLOR
2231 && VAR_HEADER_GENERAL_BACK_COLOR
2232 && colorcmp(VAR_NORM_BACK_COLOR
, VAR_HEADER_GENERAL_BACK_COLOR
)))){
2236 right_margin
= margin
? margin
[1] : 0;
2237 total_wid
= column
- right_margin
;
2239 gf_link_filter(gf_line_test
,
2240 gf_line_test_opt(pad_to_right_edge
, (void *) &total_wid
));
2241 wrapflags
|= GFW_HANDLES
;
2244 gf_link_filter(gf_nvtnl_local
, NULL
);
2246 if((errstr
= gf_pipe(tmp_gc
, final_pc
)) != NULL
){
2248 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
2249 "Can't build header : %.200s", errstr
);
2254 gf_clear_so_readc(df_store
);
2258 gf_clear_so_readc(tmp_store
);
2261 so_give(&tmp_store
);
2264 fs_give((void **)&h
);
2267 fs_give((void **)&fields
);
2273 /*----------------------------------------------------------------------
2274 Format RAW header text for display
2276 Args: stream -- mail stream for various header text fetches
2277 rawno -- sequence number in stream of message we're interested in
2278 section -- which section of message
2279 pc -- function to write header text with
2281 Result: 0 if all's well, -1 if write error, 1 if fetch error
2283 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2284 in the local convention.
2288 format_raw_header(MAILSTREAM
*stream
, long int msgno
, char *section
, gf_io_t pc
)
2290 char *h
= mail_fetch_header(stream
, msgno
, section
, NULL
, NULL
, FT_PEEK
);
2297 if(!gf_puts(NEWLINE
, pc
))
2300 if(ISRFCEOL(h
)) /* all done! */
2303 else if((unsigned char)(*h
) < 0x80 && FILTER_THIS(*h
) &&
2304 !(*(h
+1) && *h
== ESCAPE
&& match_escapes(h
+1))){
2305 c
= (unsigned char) *h
++;
2306 if(!((*pc
)(c
>= 0x80 ? '~' : '^')
2307 && (*pc
)((c
== 0x7f) ? '?' : (c
& 0x1f) + '@')))
2310 else if(!(*pc
)(*h
++))
2315 return(FHT_FTCHERR
);
2322 /*----------------------------------------------------------------------
2323 Format c-client envelope data suitable for display
2325 Args: s -- mail stream for various header text fetches
2326 n -- raw sequence number in stream of message we're interested in
2327 sect -- which section of message
2328 e -- pointer to msg's envelope
2329 pc -- function to write header text with
2330 which -- which header lines to write
2332 flags -- FM_ flags, see pith/mailview.h
2334 Result: 0 if all's well, -1 if write error, 1 if fetch error
2336 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2337 in the local convention.
2341 format_envelope(MAILSTREAM
*s
, long int n
, char *sect
, ENVELOPE
*e
, gf_io_t pc
,
2342 long int which
, char *oacs
, int flags
)
2344 char *q
, *p2
, buftmp
[MAILTMPLEN
];
2349 if((which
& FE_DATE
) && e
->date
) {
2351 snprintf(buftmp
, sizeof(buftmp
), "%s",
2352 F_ON(F_DATES_TO_LOCAL
,ps_global
)
2353 ? convert_date_to_local((char *) e
->date
) : (char *) e
->date
);
2354 buftmp
[sizeof(buftmp
)-1] = '\0';
2355 p2
= (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf
,
2356 SIZEOF_20KBUF
, buftmp
);
2358 format_env_puts(p2
, pc
);
2359 gf_puts(NEWLINE
, pc
);
2362 if((which
& FE_FROM
) && e
->from
)
2363 format_addr_string(s
, n
, sect
, "From: ", e
->from
, flags
, oacs
, pc
);
2365 if((which
& FE_REPLYTO
) && e
->reply_to
2366 && (!e
->from
|| !address_is_same(e
->reply_to
, e
->from
)))
2367 format_addr_string(s
, n
, sect
, "Reply-To: ", e
->reply_to
, flags
, oacs
, pc
);
2369 if((which
& FE_TO
) && e
->to
)
2370 format_addr_string(s
, n
, sect
, "To: ", e
->to
, flags
, oacs
, pc
);
2372 if((which
& FE_CC
) && e
->cc
)
2373 format_addr_string(s
, n
, sect
, "Cc: ", e
->cc
, flags
, oacs
, pc
);
2375 if((which
& FE_BCC
) && e
->bcc
)
2376 format_addr_string(s
, n
, sect
, "Bcc: ", e
->bcc
, flags
, oacs
, pc
);
2378 if((which
& FE_RETURNPATH
) && e
->return_path
)
2379 format_addr_string(s
, n
, sect
, "Return-Path: ", e
->return_path
,
2382 if((which
& FE_NEWSGROUPS
) && e
->newsgroups
){
2384 format_newsgroup_string("Newsgroups: ", e
->newsgroups
, flags
, pc
);
2385 if (!e
->ngpathexists
&& e
->message_id
&&
2386 strncmp (e
->message_id
,"<alpine.",8) &&
2387 strncmp (e
->message_id
,"<Pine.",6) &&
2388 strncmp (e
->message_id
,"<MS-C.",6) &&
2389 strncmp (e
->message_id
,"<MailManager.",13) &&
2390 strncmp (e
->message_id
,"<EasyMail.",11) &&
2391 strncmp (e
->message_id
,"<ML-",4)) bogus
= T
;
2394 q_status_message(SM_ORDER
, 0, 3,
2395 "Unverified Newsgroup header -- Message MAY or MAY NOT have been posted");
2398 if((which
& FE_FOLLOWUPTO
) && e
->followup_to
)
2399 format_newsgroup_string("Followup-To: ", e
->followup_to
, flags
, pc
);
2401 if((which
& FE_SUBJECT
) && e
->subject
&& e
->subject
[0]){
2402 char *freeme
= NULL
;
2407 p2
= iutf8ncpy((char *)(tmp_20k_buf
+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, 10000, e
->subject
), SIZEOF_20KBUF
-10000);
2409 if(flags
& FM_DISPLAY
2410 && (ps_global
->display_keywords_in_subject
2411 || ps_global
->display_keywordinits_in_subject
)){
2413 /* don't bother if no keywords are defined */
2414 if(some_user_flags_defined(s
))
2415 p2
= freeme
= prepend_keyword_subject(s
, n
, p2
,
2416 ps_global
->display_keywords_in_subject
? KW
: KWInit
,
2417 NULL
, ps_global
->VAR_KW_BRACES
);
2420 format_env_puts(p2
, pc
);
2423 fs_give((void **) &freeme
);
2425 gf_puts(NEWLINE
, pc
);
2428 if((which
& FE_SENDER
) && e
->sender
2429 && (!e
->from
|| !address_is_same(e
->sender
, e
->from
)))
2430 format_addr_string(s
, n
, sect
, "Sender: ", e
->sender
, flags
, oacs
, pc
);
2432 if((which
& FE_MESSAGEID
) && e
->message_id
){
2435 p2
= iutf8ncpy((char *)(tmp_20k_buf
+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, 10000, e
->message_id
), SIZEOF_20KBUF
-10000);
2436 format_env_puts(p2
, pc
);
2437 gf_puts(NEWLINE
, pc
);
2440 if((which
& FE_INREPLYTO
) && e
->in_reply_to
){
2441 q
= "In-Reply-To: ";
2443 p2
= iutf8ncpy((char *)(tmp_20k_buf
+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, 10000, e
->in_reply_to
), SIZEOF_20KBUF
-10000);
2444 format_env_puts(p2
, pc
);
2445 gf_puts(NEWLINE
, pc
);
2448 if((which
& FE_REFERENCES
) && e
->references
) {
2451 p2
= iutf8ncpy((char *)(tmp_20k_buf
+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, 10000, e
->references
), SIZEOF_20KBUF
-10000);
2452 format_env_puts(p2
, pc
);
2453 gf_puts(NEWLINE
, pc
);
2461 * The argument fieldname is something like "Subject:..." or "Subject".
2462 * Look through the specs in speccolor for a match of the fieldname,
2463 * and return the color that goes with any match, or NULL.
2464 * Caller should free the color.
2467 hdr_color(char *fieldname
, char *value
, SPEC_COLOR_S
*speccolor
)
2469 SPEC_COLOR_S
*hc
= NULL
;
2470 COLOR_PAIR
*color_pair
= NULL
;
2471 char *colon
, *fname
;
2472 char fbuf
[FBUF_LEN
+1];
2476 colon
= strindex(fieldname
, ':');
2478 strncpy(fbuf
, fieldname
, MIN(colon
-fieldname
,FBUF_LEN
));
2479 fbuf
[MIN(colon
-fieldname
,FBUF_LEN
)] = '\0';
2486 for(hc
= speccolor
; hc
; hc
= hc
->next
)
2487 if(hc
->spec
&& !strucmp(fname
, hc
->spec
)){
2492 for(pat
= hc
->val
; !gotit
&& pat
; pat
= pat
->next
)
2493 if(srchstr(value
, pat
->substring
))
2500 if(hc
&& hc
->fg
&& hc
->fg
[0] && hc
->bg
&& hc
->bg
[0])
2501 color_pair
= new_color_pair(hc
->fg
, hc
->bg
);
2508 * The argument fieldname is something like "Subject:..." or "Subject".
2509 * Look through the specs in hdr_colors for a match of the fieldname,
2510 * and return 1 if that fieldname is in one of the patterns, 0 otherwise.
2513 any_hdr_color(char *fieldname
)
2515 SPEC_COLOR_S
*hc
= NULL
;
2516 char *colon
, *fname
;
2517 char fbuf
[FBUF_LEN
+1];
2519 colon
= strindex(fieldname
, ':');
2521 strncpy(fbuf
, fieldname
, MIN(colon
-fieldname
,FBUF_LEN
));
2522 fbuf
[MIN(colon
-fieldname
,FBUF_LEN
)] = '\0';
2529 for(hc
= ps_global
->hdr_colors
; hc
; hc
= hc
->next
)
2530 if(hc
->spec
&& !strucmp(fname
, hc
->spec
))
2537 /*----------------------------------------------------------------------
2538 Format an address field, wrapping lines nicely at commas
2540 Args: field_name -- The name of the field we're formatting ("TO: ", ...)
2541 addr -- ADDRESS structure to format
2543 Result: A formatted, malloced string is returned.
2544 ----------------------------------------------------------------------*/
2546 format_addr_string(MAILSTREAM
*stream
, long int msgno
, char *section
, char *field_name
,
2547 struct mail_address
*addr
, int flags
, char *oacs
, gf_io_t pc
)
2550 int trailing
= 0, group
= 0;
2557 * quickly run down address list to make sure none are patently bogus.
2558 * If so, just blat raw field out.
2560 for(atmp
= addr
; stream
&& atmp
; atmp
= atmp
->next
)
2561 if(atmp
->host
&& atmp
->host
[0] == '.'){
2562 char *field
, *fields
[2];
2565 fields
[0] = cpystr(field_name
);
2566 if((ptmp
= strchr(fields
[0], ':')) != NULL
)
2569 if((field
= pine_fetchheader_lines(stream
, msgno
, section
, fields
)) != NULL
){
2572 for(t
= h
= field
; *h
; t
++)
2573 if(*t
== '\015' && *(t
+1) == '\012'){
2574 *t
= '\0'; /* tie off line */
2575 format_env_puts(h
, pc
);
2576 if(*(h
= (++t
) + 1)) /* set new h and skip CRLF */
2577 gf_puts(NEWLINE
, pc
); /* more to write */
2581 else if(!*t
){ /* shouldn't happen much */
2583 format_env_puts(h
, pc
);
2588 fs_give((void **)&field
);
2591 fs_give((void **)&fields
[0]);
2592 gf_puts(NEWLINE
, pc
);
2593 dprint((2, "Error in \"%s\" field address\n",
2594 field_name
? field_name
: "?"));
2598 gf_puts(field_name
, pc
);
2601 atmp
= addr
->next
; /* remember what's next */
2603 if(!addr
->host
&& addr
->mailbox
){
2604 mtmp
= addr
->mailbox
;
2605 addr
->mailbox
= cpystr((char *)rfc1522_decode_to_utf8(
2606 (unsigned char *)tmp_20k_buf
,
2607 SIZEOF_20KBUF
, addr
->mailbox
));
2610 ptmp
= addr
->personal
; /* RFC 1522 personal name? */
2611 addr
->personal
= iutf8ncpy((char *)tmp_20k_buf
, (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf
+10000), SIZEOF_20KBUF
-10000, addr
->personal
), 10000);
2612 tmp_20k_buf
[10000-1] = '\0';
2614 if(!trailing
) /* 1st pass, just address */
2616 else{ /* else comma, unless */
2617 if(!((group
== 1 && addr
->host
) /* 1st addr in group, */
2618 || (!addr
->host
&& !addr
->mailbox
))){ /* or end of group */
2621 gf_puts(NEWLINE
, pc
); /* ONE address/line please */
2629 pine_rfc822_write_address_noquote(addr
, pc
, &group
);
2631 addr
->personal
= ptmp
; /* restore old personal ptr */
2632 if(!addr
->host
&& addr
->mailbox
){
2633 fs_give((void **)&addr
->mailbox
);
2634 addr
->mailbox
= mtmp
;
2641 gf_puts(NEWLINE
, pc
);
2647 const char *rspecials_minus_quote_and_dot
= "()<>@,;:\\[]";
2648 /* RFC822 continuation, must start with CRLF */
2649 #define RFC822CONT "\015\012 "
2651 /* Write RFC822 address with some quoting turned off.
2653 * address to interpret
2655 * (This is a copy of c-client's rfc822_write_address except
2656 * we don't quote double quote and dot in personal names. It writes
2657 * to a gf_io_t instead of to a buffer so that we don't have to worry
2658 * about fixed sized buffer overflowing. It's also special cased to deal
2659 * with only a single address.)
2661 * The idea is that there are some places where we'd just like to display
2662 * the personal name as is before applying confusing quoting. However,
2663 * we do want to be careful not to break things that should be quoted so
2664 * we'll only use this where we are sure. Quoting may look ugly but it
2665 * doesn't usually break anything.
2668 pine_rfc822_write_address_noquote(struct mail_address
*adr
, gf_io_t pc
, int *group
)
2670 extern const char *rspecials
;
2672 if (adr
->host
) { /* ordinary address? */
2673 if (!(adr
->personal
|| adr
->adl
)) pine_rfc822_address (adr
, pc
);
2674 else { /* no, must use phrase <route-addr> form */
2676 pine_rfc822_cat (adr
->personal
, rspecials_minus_quote_and_dot
, pc
);
2678 gf_puts(" <", pc
); /* write address delimiter */
2679 pine_rfc822_address(adr
, pc
);
2680 gf_puts (">", pc
); /* closing delimiter */
2686 else if (adr
->mailbox
) { /* start of group? */
2687 /* yes, write group name */
2688 pine_rfc822_cat (adr
->mailbox
, rspecials
, pc
);
2690 gf_puts (": ", pc
); /* write group identifier */
2691 *group
= 1; /* in a group */
2693 else if (*group
) { /* must be end of group (but be paranoid) */
2695 *group
= 0; /* no longer in that group */
2700 /* Write RFC822 route-address to string
2702 * address to interpret
2706 pine_rfc822_address(struct mail_address
*adr
, gf_io_t pc
)
2708 extern char *wspecials
;
2710 if (adr
&& adr
->host
) { /* no-op if no address */
2711 if (adr
->adl
) { /* have an A-D-L? */
2712 gf_puts (adr
->adl
, pc
);
2715 /* write mailbox name */
2716 pine_rfc822_cat (adr
->mailbox
, wspecials
, pc
);
2717 if (*adr
->host
!= '@') { /* unless null host (HIGHLY discouraged!) */
2718 gf_puts ("@", pc
); /* host delimiter */
2719 gf_puts (adr
->host
, pc
); /* write host name */
2725 /* Concatenate RFC822 string
2727 * pointer to string to concatenate
2728 * list of special characters
2732 pine_rfc822_cat(char *src
, const char *specials
, gf_io_t pc
)
2736 if (strpbrk (src
,specials
)) { /* any specials present? */
2737 gf_puts ("\"", pc
); /* opening quote */
2738 /* truly bizarre characters in there? */
2739 while ((s
= strpbrk (src
,"\\\"")) != NULL
) {
2742 /* turn it into a null-terminated piece */
2746 gf_puts (src
, pc
); /* yes, output leader */
2748 gf_puts ("\\", pc
); /* quoting */
2749 gf_puts (save
, pc
); /* output the bizarre character */
2750 src
= ++s
; /* continue after the bizarre character */
2752 if (*src
) gf_puts (src
, pc
);/* output non-bizarre string */
2753 gf_puts ("\"", pc
); /* closing quote */
2755 else gf_puts (src
, pc
); /* otherwise it's the easy case */
2759 /*----------------------------------------------------------------------
2760 Format an address field, wrapping lines nicely at commas
2762 Args: field_name -- The name of the field we're formatting ("TO:", Cc:...)
2763 newsgrps -- ADDRESS structure to format
2765 Result: A formatted, malloced string is returned.
2767 The resuling lines formatted are 80 columns wide.
2768 ----------------------------------------------------------------------*/
2770 format_newsgroup_string(char *field_name
, char *newsgrps
, int flags
, gf_io_t pc
)
2772 char buf
[MAILTMPLEN
];
2773 int trailing
= 0, llen
, alen
;
2776 if(!newsgrps
|| !*newsgrps
)
2779 gf_puts(field_name
, pc
);
2781 llen
= strlen(field_name
);
2783 for(next_ng
= newsgrps
; *next_ng
&& *next_ng
!= ','; next_ng
++);
2784 strncpy(buf
, newsgrps
, MIN(next_ng
- newsgrps
, sizeof(buf
)-1));
2785 buf
[MIN(next_ng
- newsgrps
, sizeof(buf
)-1)] = '\0';
2790 if(!trailing
){ /* first time thru, just address */
2794 else{ /* else preceding comma */
2798 if(alen
+ llen
+ 1 > 76){
2799 gf_puts(NEWLINE
, pc
);
2809 if(alen
&& llen
> 76){ /* handle long addresses */
2810 register char *q
, *p
= &buf
[alen
-1];
2813 if(isspace((unsigned char)*p
)
2814 && (llen
- (alen
- (int)(p
- buf
))) < 76){
2815 for(q
= buf
; q
< p
; q
++)
2816 (*pc
)(*q
); /* write character */
2818 gf_puts(NEWLINE
, pc
);
2827 if(p
== buf
) /* no reasonable break point */
2834 gf_puts(NEWLINE
, pc
);
2839 /*----------------------------------------------------------------------
2840 Format a text field that's part of some raw (non-envelope) message header
2846 Result: Semi-digested text (RFC 1522 decoded, anyway) written with "pc"
2848 ----------------------------------------------------------------------*/
2850 format_raw_hdr_string(char *start
, char *finish
, gf_io_t pc
, char *oacs
, int flags
)
2852 register char *current
;
2853 unsigned char *p
, *tmp
= NULL
, c
;
2861 if((n
= 4*(finish
-start
)) > SIZEOF_20KBUF
-1){
2863 p
= tmp
= (unsigned char *) fs_get(len
* sizeof(unsigned char));
2866 len
= SIZEOF_20KBUF
;
2867 p
= (unsigned char *) tmp_20k_buf
;
2870 if(islower((unsigned char)(*start
)))
2871 *start
= toupper((unsigned char)(*start
));
2873 current
= (char *) rfc1522_decode_to_utf8(p
, len
, start
);
2875 /* output from start to finish */
2876 while(*current
&& rv
== FHT_OK
)
2877 if(ISRFCEOL(current
)){
2878 if(!gf_puts(NEWLINE
, pc
))
2883 else if((unsigned char)(*current
) < 0x80 && FILTER_THIS(*current
) &&
2884 !(*(current
+1) && *current
== ESCAPE
&& match_escapes(current
+1))){
2885 c
= (unsigned char) *current
++;
2886 if(!((*pc
)(c
>= 0x80 ? '~' : '^')
2887 && (*pc
)((c
== 0x7f) ? '?' : (c
& 0x1f) + '@')))
2890 else if(!(*pc
)(*current
++))
2894 fs_give((void **) &tmp
);
2904 /*----------------------------------------------------------------------
2905 Format a text field that's part of some raw (non-envelope) message header
2912 ----------------------------------------------------------------------*/
2914 format_env_puts(char *s
, gf_io_t pc
)
2916 if(ps_global
->pass_ctrl_chars
)
2917 return(gf_puts(s
, pc
));
2920 if((unsigned char)(*s
) < 0x80 && FILTER_THIS(*s
) && !(*(s
+1) && *s
== ESCAPE
&& match_escapes(s
+1))){
2921 if(!((*pc
)((unsigned char) (*s
) >= 0x80 ? '~' : '^')
2922 && (*pc
)((*s
== 0x7f) ? '?' : (*s
& 0x1f) + '@')))
2933 display_parameters(PARAMETER
*params
)
2938 PARMLIST_S
*parmlist
;
2940 for(p
= params
; p
; p
= p
->next
) /* ok if we include *'s */
2941 if(p
->attribute
&& (n
= strlen(p
->attribute
)) > longest
)
2942 longest
= MIN(32, n
); /* shouldn't be any bigger than 32 */
2945 tmp_20k_buf
[0] = '\0';
2946 if((parmlist
= rfc2231_newparmlist(params
)) != NULL
){
2947 n
= 0; /* n overloaded */
2948 while(rfc2231_list_params(parmlist
) && d
< tmp_20k_buf
+ 10000){
2950 snprintf(d
, 10000-(d
-tmp_20k_buf
), "\n");
2951 tmp_20k_buf
[10000-1] = '\0';
2955 if(parmlist
->value
){
2956 if(parmlist
->attrib
&& strucmp(parmlist
->attrib
, "url") == 0){
2957 snprintf(printme
= tmp_20k_buf
+ 11000, 1000, "%s", parmlist
->value
);
2961 printme
= strsquish(tmp_20k_buf
+ 11000, 1000, parmlist
->value
, 100);
2966 snprintf(d
, 10000-(d
-tmp_20k_buf
), "%-*s: %s", longest
,
2967 parmlist
->attrib
? parmlist
->attrib
: "", printme
);
2969 tmp_20k_buf
[10000-1] = '\0';
2973 rfc2231_free_parmlist(&parmlist
);
2976 return(tmp_20k_buf
);
2980 /*----------------------------------------------------------------------
2981 Fetch the requested header fields from the msgno specified
2983 Args: stream -- mail stream of open folder
2984 msgno -- number of message to get header lines from
2985 fields -- array of pointers to desired fields
2987 Returns: allocated string containing matched header lines,
2991 pine_fetch_header(MAILSTREAM
*stream
, long int msgno
, char *section
, char **fields
, long int flags
)
2994 char *p
, *m
, *h
= NULL
, *match
= NULL
, *free_this
, tmp
[MAILTMPLEN
];
2995 char **pflds
= NULL
, **pp
= NULL
, **qq
;
2998 * If the user misconfigures it is possible to have one of the fields
2999 * set to the empty string instead of a header name. We want to catch
3000 * that here instead of asking the server the nonsensical question.
3002 for(pp
= fields
? &fields
[0] : NULL
; pp
&& *pp
; pp
++)
3006 if(pp
&& *pp
){ /* found an empty header field, fix it */
3007 pflds
= copy_list_array(fields
);
3008 for(pp
= pflds
; pp
&& *pp
; pp
++){
3009 if(!**pp
){ /* scoot rest of the lines up */
3011 for(qq
= pp
; *qq
; qq
++)
3015 fs_give((void **) &free_this
);
3019 /* no headers to look for, return NULL */
3020 if(pflds
&& !*pflds
&& !(flags
& FT_NOT
)){
3021 free_list_array(&pflds
);
3028 sl
= (pflds
&& *pflds
) ? new_strlst(pflds
) : NULL
; /* package up fields */
3029 h
= mail_fetch_header(stream
, msgno
, section
, sl
, NULL
, flags
| FT_PEEK
);
3034 if(pflds
&& pflds
!= fields
)
3035 free_list_array(&pflds
);
3040 while(find_field(&h
, tmp
, sizeof(tmp
))){
3041 for(pp
= &pflds
[0]; *pp
&& strucmp(tmp
, *pp
); pp
++)
3044 /* interesting field? */
3045 if((p
= (flags
& FT_NOT
) ? ((*pp
) ? NULL
: tmp
) : *pp
) != NULL
){
3047 * Hold off allocating space for matching fields until
3048 * we at least find one to copy...
3051 match
= m
= fs_get(strlen(h
) + strlen(p
) + 1);
3053 while(*p
) /* copy field name */
3056 while(*h
&& (*m
++ = *h
++)) /* header includes colon */
3057 if(*(m
-1) == '\n' && (*h
== '\r' || !isspace((unsigned char)*h
)))
3060 *m
= '\0'; /* tie off match string */
3062 else{ /* no match, pass this field */
3063 while(*h
&& !(*h
++ == '\n'
3064 && (*h
== '\r' || !isspace((unsigned char)*h
))))
3069 if(pflds
&& pflds
!= fields
)
3070 free_list_array(&pflds
);
3072 return(match
? match
: cpystr(""));
3077 find_field(char **h
, char *tmp
, size_t ntmp
)
3081 if(!h
|| !*h
|| !**h
|| isspace((unsigned char)**h
))
3084 while(tmp
-otmp
<ntmp
-1 && **h
&& **h
!= ':' && !isspace((unsigned char)**h
))
3092 static char *_last_embedded_fg_color
, *_last_embedded_bg_color
;
3096 embed_color(COLOR_PAIR
*cp
, gf_io_t pc
)
3099 if(_last_embedded_fg_color
)
3100 fs_give((void **)&_last_embedded_fg_color
);
3102 _last_embedded_fg_color
= cpystr(cp
->fg
);
3104 if(!(pc
&& (*pc
)(TAG_EMBED
) && (*pc
)(TAG_FGCOLOR
) &&
3105 gf_puts(color_to_asciirgb(cp
->fg
), pc
)))
3110 if(_last_embedded_bg_color
)
3111 fs_give((void **)&_last_embedded_bg_color
);
3113 _last_embedded_bg_color
= cpystr(cp
->bg
);
3115 if(!(pc
&& (*pc
)(TAG_EMBED
) && (*pc
)(TAG_BGCOLOR
) &&
3116 gf_puts(color_to_asciirgb(cp
->bg
), pc
)))
3125 get_cur_embedded_color(void)
3129 if(_last_embedded_fg_color
&& _last_embedded_bg_color
)
3130 ret
= new_color_pair(_last_embedded_fg_color
, _last_embedded_bg_color
);
3132 ret
= pico_get_cur_color();
3139 clear_cur_embedded_color(void)
3141 if(_last_embedded_fg_color
)
3142 fs_give((void **)&_last_embedded_fg_color
);
3144 if(_last_embedded_bg_color
)
3145 fs_give((void **)&_last_embedded_bg_color
);
3150 scroll_handle_start_color(char *colorstring
, size_t buflen
, int *len
)
3154 if(pico_usingcolor()){
3155 char *fg
= NULL
, *bg
= NULL
, *s
;
3157 if(ps_global
->VAR_SLCTBL_FORE_COLOR
3158 && colorcmp(ps_global
->VAR_SLCTBL_FORE_COLOR
,
3159 ps_global
->VAR_NORM_FORE_COLOR
))
3160 fg
= ps_global
->VAR_SLCTBL_FORE_COLOR
;
3162 if(ps_global
->VAR_SLCTBL_BACK_COLOR
3163 && colorcmp(ps_global
->VAR_SLCTBL_BACK_COLOR
,
3164 ps_global
->VAR_NORM_BACK_COLOR
))
3165 bg
= ps_global
->VAR_SLCTBL_BACK_COLOR
;
3171 * The blacks are just known good colors for
3172 * testing whether the other color is good.
3174 if((tmp
= new_color_pair(fg
? fg
: colorx(COL_BLACK
),
3175 bg
? bg
: colorx(COL_BLACK
))) != NULL
){
3176 if(pico_is_good_colorpair(tmp
))
3177 for(s
= color_embed(fg
, bg
);
3178 (*len
) < buflen
&& (colorstring
[*len
] = *s
);
3182 free_color_pair(&tmp
);
3186 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
3187 strncpy(colorstring
+ (*len
), url_embed(TAG_BOLDON
), MIN(3,buflen
-(*len
)));
3192 colorstring
[buflen
-1] = '\0';
3199 scroll_handle_end_color(char *colorstring
, size_t buflen
, int *len
, int use_hdr_color
)
3202 if(pico_usingcolor()){
3203 char *fg
= NULL
, *bg
= NULL
, *s
;
3204 char *basefg
= NULL
, *basebg
= NULL
;
3206 basefg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
3207 : ps_global
->VAR_NORM_FORE_COLOR
;
3208 basebg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
3209 : ps_global
->VAR_NORM_BACK_COLOR
;
3212 * We need to change the fg and bg colors back even if they
3213 * are the same as the Normal Colors so that color_a_quote
3214 * will have a chance to pick up those colors as the ones to
3215 * switch to. We don't do this before the handle above so that
3216 * the quote color will flow into the selectable item when
3217 * the selectable item color is partly the same as the
3218 * normal color. That is, suppose the normal color was black on
3219 * cyan and the selectable color was blue on cyan, only a fg color
3220 * change. We preserve the only-a-fg-color-change in a quote by
3221 * letting the quote background color flow into the selectable text.
3223 if(ps_global
->VAR_SLCTBL_FORE_COLOR
)
3226 if(ps_global
->VAR_SLCTBL_BACK_COLOR
)
3229 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
3230 strncpy(colorstring
, url_embed(TAG_BOLDOFF
), MIN(3,buflen
));
3235 for(s
= color_embed(fg
, bg
); (*len
) < buflen
&& (colorstring
[*len
] = *s
); s
++, (*len
)++)
3239 colorstring
[buflen
-1] = '\0';
3246 * Helper routine that is of limited use.
3247 * We need to tally up the screen width of
3248 * a UTF-8 string as we go through the string.
3249 * We just want the width of the character starting
3250 * at str (and no longer than remaining_octets).
3251 * If we're plopped into the middle of a UTF-8
3252 * character we just want to return width zero.
3255 width_at_this_position(unsigned char *str
, unsigned long n
)
3257 unsigned char *inputp
= str
;
3258 unsigned long remaining_octets
= n
;
3262 ucs
= (UCS
) utf8_get(&inputp
, &remaining_octets
);
3263 if(!(ucs
& U8G_ERROR
|| ucs
== UBOGON
)){
3264 width
= wcellwidth(ucs
);
3265 /* Writechar will print a '?' */