* Update configure script to remove the search for a linking tcl
[alpine.git] / pith / mailview.c
bloba0aead2441f34f87052a97eb066dc8b5050a29ec
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: mailview.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2009 University of Washington
8 * Copyright 2013-2018 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 /*======================================================================
21 mailview.c
22 Implements message data gathering and formatting
24 ====*/
27 #include "headers.h"
28 #include "../pith/ical.h"
29 #include "../pith/body.h"
30 #include "../pith/mailpart.h"
31 #include "../pith/mailview.h"
32 #include "../pith/conf.h"
33 #include "../pith/msgno.h"
34 #include "../pith/editorial.h"
35 #include "../pith/mimedesc.h"
36 #include "../pith/margin.h"
37 #include "../pith/color.h"
38 #include "../pith/strlst.h"
39 #include "../pith/charset.h"
40 #include "../pith/status.h"
41 #include "../pith/maillist.h"
42 #include "../pith/mailcmd.h"
43 #include "../pith/mailindx.h"
44 #include "../pith/imap.h"
45 #include "../pith/detach.h"
46 #include "../pith/text.h"
47 #include "../pith/url.h"
48 #include "../pith/rfc2231.h"
49 #include "../pith/list.h"
50 #include "../pith/stream.h"
51 #include "../pith/send.h"
52 #include "../pith/filter.h"
53 #include "../pith/string.h"
54 #include "../pith/ablookup.h"
55 #include "../pith/escapes.h"
56 #include "../pith/keyword.h"
57 #include "../pith/smime.h"
60 #define FBUF_LEN (50)
62 #define ISRFCEOL(S) (*(S) == '\015' && *((S)+1) == '\012')
66 * This is a list of header fields that are represented canonically
67 * by the c-client's ENVELOPE structure. The list is used by the
68 * two functions below to decide if a given field is included in this
69 * set.
71 static struct envelope_s {
72 char *name;
73 long val;
74 } envelope_hdrs[] = {
75 {"from", FE_FROM},
76 {"sender", FE_SENDER},
77 {"date", FE_DATE},
78 {"to", FE_TO},
79 {"cc", FE_CC},
80 {"bcc", FE_BCC},
81 {"newsgroups", FE_NEWSGROUPS},
82 {"subject", FE_SUBJECT},
83 {"message-id", FE_MESSAGEID},
84 {"reply-to", FE_REPLYTO},
85 {"followup-to", FE_FOLLOWUPTO},
86 {"in-reply-to", FE_INREPLYTO},
87 /* {"return-path", FE_RETURNPATH}, not usually filled in */
88 {"references", FE_REFERENCES},
89 {NULL, 0}
94 * Hook for optional display of rfc2369 content
96 int (*pith_opt_rfc2369_editorial)(long, HANDLE_S **, int, int, gf_io_t);
102 * Internal prototypes
104 int format_blip_seen(long);
105 int is_an_env_hdr(char *);
106 int is_an_addr_hdr(char *);
107 void format_env_hdr(MAILSTREAM *, long, char *, ENVELOPE *,
108 fmt_env_t, gf_io_t, char *, char *, int);
109 int delineate_this_header(char *, char *, char **, char **);
110 char *url_embed(int);
111 int color_headers(long, char *, LT_INS_S **, void *);
112 int url_hilite_hdr(long, char *, LT_INS_S **, void *);
113 int pad_to_right_edge(long, char *, LT_INS_S **, void *);
114 int url_bogus_imap(char **, char *, char *);
115 int format_raw_header(MAILSTREAM *, long, char *, gf_io_t);
116 void format_envelope(MAILSTREAM *, long, char *, ENVELOPE *,
117 gf_io_t, long, char *, int);
118 int any_hdr_color(char *);
119 void format_addr_string(MAILSTREAM *, long, char *, char *,
120 ADDRESS *, int, char *, gf_io_t);
121 void pine_rfc822_write_address_noquote(ADDRESS *, gf_io_t, int *);
122 void format_newsgroup_string(char *, char *, int, gf_io_t);
123 int format_raw_hdr_string(char *, char *, gf_io_t, char *, int);
124 int format_env_puts(char *, gf_io_t);
125 int find_field(char **, char *, size_t);
126 int embed_color(COLOR_PAIR *, gf_io_t);
127 COLOR_PAIR *get_cur_embedded_color(void);
128 void clear_cur_embedded_color(void);
129 void format_calendar_vevent(VCALENDAR_S *, ATTACH_S *, HANDLE_S **, int, int, gf_io_t, int);
133 /*----------------------------------------------------------------------
134 Format a message message for viewing
136 Args: msgno -- The number of the message to view
137 env -- pointer to the message's envelope
138 body -- pointer to the message's body
139 handlesp -- address of pointer to the message's handles
140 flgs -- possible flags listed in pith/mailview.h with
141 prefix FM_
142 pc -- write to this function
144 Result: Returns true if no problems encountered, else false.
146 First the envelope is formatted; next a list of all attachments is
147 formatted if there is more than one. Then all the body parts are
148 formatted, fetching them as needed. This includes headers of included
149 message. Richtext is also formatted. An entry is made in the text for
150 parts that are not displayed or can't be displayed.
152 ----*/
154 format_message(long int msgno, ENVELOPE *env, struct mail_bodystruct *body,
155 HANDLE_S **handlesp, int flgs, gf_io_t pc)
157 char *decode_err = NULL;
158 HEADER_S h;
159 int width, rv;
161 clear_cur_embedded_color();
163 if(!(flgs & FM_DISPLAY))
164 flgs |= FM_NOINDENT;
166 width = (flgs & FM_DISPLAY) ? ps_global->ttyo->screen_cols : 80;
168 /*---- format and copy envelope ----*/
169 if(!(flgs & FM_NOEDITORIAL)){
170 if(ps_global->full_header == 1)
171 /* TRANSLATORS: User is viewing a message and all the quoted text is
172 being shown. */
173 q_status_message(SM_INFO, 0, 3, _("All quoted text being included"));
174 else if(ps_global->full_header == 2)
175 q_status_message(SM_INFO, 0, 3,
176 /* TRANSLATORS: User is viewing a message and all of
177 the header text is being shown. */
178 _("Full header mode ON. All header text being included"));
181 HD_INIT(&h, ps_global->VAR_VIEW_HEADERS, ps_global->view_all_except, FE_DEFAULT);
182 switch(format_header(ps_global->mail_stream, msgno, NULL,
183 env, &h, NULL, handlesp, flgs, NULL, pc)){
185 case -1 : /* write error */
186 goto write_error;
188 case 1 : /* fetch error */
189 if(!(gf_puts("[ Error fetching header ]", pc)
190 && !gf_puts(NEWLINE, pc)))
191 goto write_error;
193 break;
196 if(!(body == NULL
197 || (ps_global->full_header == 2
198 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global)))){
199 format_attachment_list(msgno, body, handlesp, flgs, width, pc);
200 format_calendar(msgno, body, handlesp, flgs, width, pc);
203 /* write delimiter and body */
204 if(gf_puts(NEWLINE, pc)){
205 if((decode_err = format_body(msgno, body, handlesp, &h, flgs, width, pc)) == NULL)
206 rv = 1;
207 else
208 rv = 0;
210 clear_cur_embedded_color();
211 return(rv);
214 write_error:
216 if(!(flgs & FM_DISPLAY))
217 q_status_message1(SM_ORDER, 3, 4, _("Error writing message: %s"),
218 decode_err ? decode_err : error_description(errno));
219 clear_cur_embedded_color();
220 return(0);
223 void
224 format_calendar_vevent(VCALENDAR_S *vcal, ATTACH_S *a, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc, int cflags)
226 int avail, m1, m2, hwid, i, partwid, padwid;
227 int s1, s2, dwid, minkey;
228 int *margin;
229 char padding[1024];
230 VEVENT_SUMMARY_S *vesy, *vesummary; /* vevent summary */
232 vesummary = vesy = ical_vevent_summary(vcal);
234 if(vesy == NULL) return;
236 if((cflags & FC_SUMMARY) && (cflags & FC_FULL))
237 cflags |= ~FC_FULL;
239 minkey = -1; /* initialize to something negative */
241 for(; vesy != NULL ; vesy = vesy->next){
242 avail = width;
243 margin = (cflags & FC_FULL) ? NULL
244 : (flgs & FM_NOINDENT) ? NULL : format_view_margin();
246 m1 = MAX(MIN(margin ? margin[0] : 0, avail), 0);
247 avail -= m1;
249 m2 = MAX(MIN(margin ? margin[1] : 0, avail), 0);
250 avail -= m2;
252 hwid = MAX(avail, 0);
253 padwid = 0;
255 if(ps_global->atmts[1].description == NULL){
256 avail = width - m1 -2;
258 dwid = MAX(MIN(40, avail), 0);
260 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s%s", m1, m1, "",
261 repeat_char(dwid, '-'));
263 if(!gf_puts(tmp_20k_buf, pc) || !gf_puts(NEWLINE, pc))
264 return;
268 if(cflags & FC_SUMMARY){
269 i = utf8_width(_("Calendar Entry:"));
270 partwid = MIN(i, hwid);
271 if(m1 > 0){
272 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", m1, m1, "");
273 if(!gf_puts(tmp_20k_buf, pc))
274 return;
277 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%-*.*w%*.*s",
278 partwid, partwid, _("Calendar Entry:"),
279 padwid, padwid, "");
281 if(!gf_puts(tmp_20k_buf, pc) || !gf_puts(NEWLINE, pc))
282 return;
284 else
285 partwid = 0;
287 if(m1 > 0){
288 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", m1, m1, "");
289 if(!gf_puts(tmp_20k_buf, pc))
290 return;
293 avail = width - m1 - m2;
295 s1 = MAX(MIN(1, avail), 0);
296 avail -= s1;
298 dwid = MAX(MIN(1, avail), 0);
299 avail -= dwid;
301 s2 = MAX(MIN(1, avail), 0);
302 avail -= s2;
304 if(cflags & FC_SUMMARY)
305 utf8_snprintf(padding, sizeof(padding), "%*.*s%*.*w%*.*s",
306 s1, s1, "", dwid, dwid, "", s2, s2, "");
307 else
308 padding[0] = '\0';
310 if(vesy->cancel){
311 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
312 padding, _("This event was cancelled!"));
313 if((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON)){
314 gf_puts(tmp_20k_buf, pc);
315 gf_puts(NEWLINE, pc);
316 (*pc)(TAG_EMBED);
317 (*pc)(TAG_BOLDOFF);
321 if(vesy->organizer){
322 if(vesy->sender != NULL){
323 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
324 padding, _("Sent-by: "), vesy->sender);
325 gf_puts(tmp_20k_buf, pc);
326 gf_puts(NEWLINE, pc);
329 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
330 padding, _("Organizer: "), vesy->organizer);
331 gf_puts(tmp_20k_buf, pc);
332 gf_puts(NEWLINE, pc);
333 } /* end of if(organizer) */
335 if(vesy->location){
336 ical_remove_escapes(&vesy->location);
337 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
338 padding, _("Location: "), vesy->location);
339 gf_puts(tmp_20k_buf, pc);
340 gf_puts(NEWLINE, pc);
341 } /* end of if location */
343 if(vesy->evstart){
344 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
345 padding, _("Start Date: "), vesy->evstart);
346 gf_puts(tmp_20k_buf, pc);
347 gf_puts(NEWLINE, pc);
348 } /* end of if dtstart */
350 if(vesy->duration){
351 int i;
353 for(i = 0; vesy->duration[i] != NULL; i++){
354 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
355 padding, _("Duration: "), vesy->duration[i]);
356 gf_puts(tmp_20k_buf, pc);
357 gf_puts(NEWLINE, pc);
359 } /* end of DURATION */
360 else if(vesy->evend){
361 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
362 padding, _("End Date: "), vesy->evend);
363 gf_puts(tmp_20k_buf, pc);
364 gf_puts(NEWLINE, pc);
365 } else { /* end of if dtend */
366 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
367 padding, _("No duration nor end time found for this event"));
368 gf_puts(tmp_20k_buf, pc);
369 gf_puts(NEWLINE, pc);
370 } /* end of else for if (duration) and if (dtend) */
372 if(vesy->attendee){
373 #define MAX_DISPLAYED 3
374 int i;
376 for(i = 0; vesy->attendee[i] != NULL; i++){
377 if((cflags & FC_SUMMARY) && i >= MAX_DISPLAYED)
378 break;
380 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
381 padding,
382 _("Attendee: "), vesy->attendee[i]);
383 gf_puts(tmp_20k_buf, pc);
384 gf_puts(NEWLINE, pc);
386 } /* end of ATTENDEES */
389 if(cflags & FC_SUMMARY){
390 COLOR_PAIR *lastc = NULL;
391 char numbuf[50];
392 int thisdescwid;
393 COLOR_PAIR *hdrcolor = NULL;
395 if((flgs & FM_DISPLAY)
396 && !(flgs & FM_NOCOLOR)
397 && pico_usingcolor()
398 && ps_global->VAR_HEADER_GENERAL_FORE_COLOR
399 && ps_global->VAR_HEADER_GENERAL_BACK_COLOR
400 && ps_global->VAR_NORM_FORE_COLOR
401 && ps_global->VAR_NORM_BACK_COLOR
402 && (colorcmp(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
403 ps_global->VAR_NORM_FORE_COLOR)
404 || colorcmp(ps_global->VAR_HEADER_GENERAL_BACK_COLOR,
405 ps_global->VAR_NORM_BACK_COLOR))){
407 if((hdrcolor = new_color_pair(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
408 ps_global->VAR_HEADER_GENERAL_BACK_COLOR)) != NULL){
409 if(!pico_is_good_colorpair(hdrcolor))
410 free_color_pair(&hdrcolor);
414 if(!(!hdrcolor || embed_color(hdrcolor, pc)))
415 return;
417 gf_puts(padding, pc);
418 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "[%s]", _("More Details"));
420 if(handlesp){
421 char buf[16], color[64];
422 int l;
423 HANDLE_S *h;
425 h = new_handle(handlesp);
426 if(minkey < 0) minkey = h->key;
427 h->type = iCal;
428 h->h.ical.attach = a;
429 h->h.ical.depth = h->key - minkey;
431 snprintf(buf, sizeof(buf), "%d", h->key);
432 buf[sizeof(buf)-1] = '\0';
434 if(!(flgs & FM_NOCOLOR)
435 && handle_start_color(color, sizeof(color), &l, 1)){
436 lastc = get_cur_embedded_color();
437 if(!gf_nputs(color, (long) l, pc))
438 return;
440 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)
441 && (!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON))))
442 return;
444 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE)
445 && (*pc)(strlen(buf)) && gf_puts(buf, pc)))
446 return;
447 } else
448 tmp_20k_buf[0] = '\0';
450 if(!format_env_puts(tmp_20k_buf, pc))
451 return;
453 if(handlesp){
454 if(lastc){
455 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
456 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
457 return;
460 if(!embed_color(lastc, pc))
461 return;
463 free_color_pair(&lastc);
465 else if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
466 return;
468 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)))
469 return;
472 if(padwid > 0){
473 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", padwid, padwid, "");
474 if(!gf_puts(tmp_20k_buf, pc))
475 return;
478 if(!gf_puts(NEWLINE, pc))
479 return;
481 avail = width - m1 -2;
483 dwid = MAX(MIN(40, avail), 0);
484 avail -= dwid;
486 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s%s", m1, m1, "",
487 repeat_char(dwid, '-'));
489 gf_puts(tmp_20k_buf, pc);
490 if(vesy->next)
491 gf_puts(NEWLINE, pc);
493 free_vevent_summary(&vesummary);
497 format_calendar(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc)
499 char *rawtext, *caltext;
500 unsigned long callen;
501 VCALENDAR_S *vcal = NULL;
502 ATTACH_S *a;
503 BODY *b;
505 if(flgs & FM_NEW_MESS) {
506 zero_atmts(ps_global->atmts);
507 describe_mime(body, "", 1, 1, 0, flgs);
510 for(a = ps_global->atmts; a->description != NULL; a++){
511 if(MIME_VCALENDAR(a->body->type, a->body->subtype)){
512 b = mail_body (ps_global->mail_stream, msgno, a->number);
513 if(b == NULL){
514 gf_puts(_("Error fetching calendar body part"), pc);
515 gf_puts(NEWLINE, pc);
516 continue;
518 if(b->sparep == NULL){
519 rawtext = mail_fetch_body(ps_global->mail_stream, msgno, a->number, &callen, 0);
520 if(rawtext == NULL || *rawtext == '\0'){
521 gf_puts(_("Error fetching calendar text"), pc);
522 gf_puts(NEWLINE, pc);
523 continue;
525 rawtext[callen] = '\0'; /* chop off cookie */
526 switch(b->encoding){
527 case ENCBASE64:
528 caltext = rfc822_base64(rawtext, strlen(rawtext), &callen);
529 if(caltext == NULL){
530 gf_puts(_("Error in calendar base64 encoding"), pc);
531 gf_puts(NEWLINE, pc);
532 continue;
534 caltext[callen] = '\0';
535 break;
537 case ENCQUOTEDPRINTABLE:
538 caltext = rfc822_qprint ((unsigned char *) rawtext,strlen(rawtext),&callen);
539 if(caltext == NULL){
540 gf_puts(_("Error in calendar quoted printable encoding"), pc);
541 gf_puts(NEWLINE, pc);
542 continue;
544 caltext[callen] = '\0';
545 break;
547 default: caltext = cpystr(rawtext);
549 if(caltext != NULL){
550 vcal = ical_parse_text(caltext);
551 if(vcal != NULL) vcal->encoding = b->encoding;
552 b->sparep = create_body_sparep(iCalType, (void *) vcal);
553 fs_give((void **) &caltext);
556 else if(get_body_sparep_type(b->sparep) == iCalType)
557 vcal = (VCALENDAR_S *) get_body_sparep_data(b->sparep);
558 if(vcal != NULL && vcal->comp != NULL){
559 if(vcal->comp[VEvent] != NULL){
560 format_calendar_vevent(vcal, a, handlesp, flgs, width, pc, FC_SUMMARY);
561 } /* else another type of entry in the calendar */
563 gf_puts(NEWLINE, pc);
566 return 0;
570 char *
571 format_body(long int msgno, BODY *body, HANDLE_S **handlesp, HEADER_S *hp, int flgs, int width, gf_io_t pc)
573 int filt_only_c0 = 0, wrapflags, error_found = 0;
574 int is_in_sig = OUT_SIG_BLOCK;
575 char *charset, *decode_err = NULL, *tmp1, *description;
576 ATTACH_S *a;
577 URL_HILITE_S uh;
578 gf_io_t gc;
580 if(body == NULL
581 || (ps_global->full_header == 2
582 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))) {
584 /*--- Server is not an IMAP2bis, It can't parse MIME
585 so we just show the text here. Hopefully the
586 message isn't a MIME message
587 ---*/
588 void *text2;
590 if((text2 = (void *)pine_mail_fetch_text(ps_global->mail_stream,
591 msgno, NULL, NULL, NIL)) != NULL){
593 if(!gf_puts(NEWLINE, pc)) /* write delimiter */
594 return("Write Error");
596 gf_set_readc(&gc, text2, (unsigned long)strlen(text2), CharStar, 0);
597 gf_filter_init();
600 * We need to translate the message
601 * into UTF-8, but that's trouble in the full header case
602 * because we don't know what to translate from. We'll just
603 * take a guess by looking for the first text part and
604 * using its charset.
606 if(body && body->type == TYPETEXT)
607 charset = parameter_val(body->parameter, "charset");
608 else if(body && body->type == TYPEMULTIPART && body->nested.part
609 && body->nested.part->body.type == TYPETEXT)
610 charset = parameter_val(body->nested.part->body.parameter, "charset");
611 else
612 charset = cpystr(ps_global->display_charmap);
614 if(strucmp(charset, "us-ascii") && strucmp(charset, "utf-8")){
615 /* transliterate message text to UTF-8 */
616 gf_link_filter(gf_utf8, gf_utf8_opt(charset));
618 if (charset) fs_give((void **) &charset);
620 /* link in filters, similar to what is done in decode_text() */
621 if(!ps_global->pass_ctrl_chars){
622 gf_link_filter(gf_escape_filter, NULL);
623 filt_only_c0 = 1;
624 gf_link_filter(gf_control_filter,
625 gf_control_filter_opt(&filt_only_c0));
628 gf_link_filter(gf_tag_filter, NULL);
630 if((F_ON(F_VIEW_SEL_URL, ps_global)
631 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
632 || F_ON(F_SCAN_ADDR, ps_global))
633 && handlesp){
634 gf_link_filter(gf_line_test,
635 gf_line_test_opt(url_hilite,
636 gf_url_hilite_opt(&uh,handlesp,0)));
639 if((flgs & FM_DISPLAY)
640 && !(flgs & FM_NOCOLOR)
641 && pico_usingcolor()
642 && ps_global->VAR_SIGNATURE_FORE_COLOR
643 && ps_global->VAR_SIGNATURE_BACK_COLOR){
644 gf_link_filter(gf_line_test, gf_line_test_opt(color_signature, &is_in_sig));
647 if((flgs & FM_DISPLAY)
648 && !(flgs & FM_NOCOLOR)
649 && pico_usingcolor()
650 && ps_global->VAR_QUOTE1_FORE_COLOR
651 && ps_global->VAR_QUOTE1_BACK_COLOR){
652 gf_link_filter(gf_line_test, gf_line_test_opt(color_a_quote, NULL));
655 if(!(flgs & FM_NOWRAP)){
656 wrapflags = (flgs & FM_DISPLAY) ? (GFW_HANDLES|GFW_SOFTHYPHEN) : GFW_NONE;
657 if(flgs & FM_DISPLAY
658 && !(flgs & FM_NOCOLOR)
659 && pico_usingcolor())
660 wrapflags |= GFW_USECOLOR;
661 gf_link_filter(gf_wrap, gf_wrap_filter_opt(width, width,
662 (flgs & FM_NOINDENT)
663 ? NULL : format_view_margin(),
665 wrapflags));
668 gf_link_filter(gf_nvtnl_local, NULL);
669 if((decode_err = gf_pipe(gc, pc)) != NULL){
670 /* TRANSLATORS: There was an error putting together a message for
671 viewing. The arg is the description of the error. */
672 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Formatting error: %s"), decode_err);
673 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
674 if(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
675 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
676 && gf_puts(NEWLINE, pc))
677 decode_err = NULL;
678 else
679 return(decode_err);
683 if(!text2){
684 if(!gf_puts(NEWLINE, pc)
685 || !gf_puts(_(" [ERROR fetching text of message]"), pc)
686 || !gf_puts(NEWLINE, pc)
687 || !gf_puts(NEWLINE, pc))
688 return("Write Error");
691 else{
692 int show_parts = 0;
694 /*======== Now loop through formatting all the parts =======*/
695 for(a = ps_global->atmts; a->description != NULL; a++) {
696 if(MIME_VCALENDAR(a->body->type, a->body->subtype))
697 continue;
699 if(a->body->type == TYPEMULTIPART){
700 #ifdef SMIME
701 if(strucmp(a->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0){
702 if(a->description){
703 if(!(!format_editorial(a->description, width, flgs, handlesp, pc)
704 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
705 return("Write Error");
708 #endif /* SMIME */
709 continue;
712 if(!a->shown) {
713 if(a->suppress_editorial)
714 continue;
716 if(!(flgs & FM_NOEDITORIAL)
717 && (!gf_puts(NEWLINE, pc)
718 || (decode_err = part_desc(a->number, a->body,
719 (flgs & FM_DISPLAY)
720 ? (a->can_display != MCD_NONE)
721 ? 1 : 2
722 : 3, width, flgs, pc))))
723 return("Write Error");
725 continue;
728 switch(a->body->type){
730 case TYPETEXT:
732 * If a message is multipart *and* the first part of it
733 * is text *and that text is empty, there is a good chance that
734 * there was actually something there that c-client was
735 * unable to parse. Here we report the empty message body
736 * and insert the raw RFC822.TEXT (if full-headers are
737 * on).
739 if(body->type == TYPEMULTIPART
740 && a == ps_global->atmts
741 && a->body->size.bytes == 0
742 && F_ON(F_ENABLE_FULL_HDR, ps_global)){
743 char *err = NULL;
745 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
746 "Empty or malformed message%s.",
747 ps_global->full_header == 2
748 ? ". Displaying raw text"
749 : ". Use \"H\" to see raw text");
750 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
752 if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
753 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
754 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
755 return("Write Error");
757 if(ps_global->full_header == 2
758 && (err = detach_raw(ps_global->mail_stream, msgno,
759 a->number, pc, flgs))){
760 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
761 "%s%s [ Formatting error: %s ]%s%s",
762 NEWLINE, NEWLINE, err, NEWLINE, NEWLINE);
763 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
764 if(!gf_puts(tmp_20k_buf, pc))
765 return("Write Error");
768 break;
772 * Don't write our delimiter if this text part is
773 * the first part of a message/rfc822 segment...
775 if(show_parts && a != ps_global->atmts
776 && !((a[-1].body && a[-1].body->type == TYPEMESSAGE)
777 #ifdef SMIME
778 || (a[-1].body->type == TYPEMULTIPART
779 && a[-1].body->subtype
780 && (strucmp(a[-1].body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0)
781 && &a[-1] != ps_global->atmts
782 && a[-2].body && a[-2].body->type == TYPEMESSAGE)
783 #endif /* SMIME */
785 && !(flgs & FM_NOEDITORIAL)){
786 if(MIME_VCALENDAR(a->body->type, a->body->subtype))
787 tmp1 = "Calendar entry";
788 else
789 tmp1 = a->body->description ? a->body->description
790 : "Attached Text";
791 description = iutf8ncpy((char *)(tmp_20k_buf+10000),
792 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+15000), 5000, tmp1), 5000);
794 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Part %s: \"%.1024s\"", a->number,
795 description);
796 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
797 if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
798 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
799 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
800 return("Write Error");
802 /* skip calendar */
803 if(!MIME_VCALENDAR(a->body->type, a->body->subtype))
804 error_found += decode_text(a, msgno, pc, handlesp,
805 (flgs & FM_DISPLAY) ? InLine : QStatus,
806 flgs);
807 break;
809 case TYPEMESSAGE:
810 tmp1 = a->body->description ? a->body->description
811 : (strucmp(a->body->subtype, "delivery-status") == 0)
812 ? "Delivery Status"
813 : "Included Message";
814 description = iutf8ncpy((char *)(tmp_20k_buf+10000),
815 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+15000), 5000, tmp1), 5000);
817 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Part %s: \"%.1024s\"", a->number,
818 description);
819 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
821 if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
822 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
823 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
824 return("Write Error");
826 if(a->body->subtype && strucmp(a->body->subtype, "rfc822") == 0){
827 /* imapenvonly, we may not have all the headers we need */
828 if(a->body->nested.msg->env->imapenvonly)
829 mail_fetch_header(ps_global->mail_stream, msgno,
830 a->number, NULL, NULL, FT_PEEK);
831 switch(format_header(ps_global->mail_stream, msgno, a->number,
832 a->body->nested.msg->env, hp,
833 NULL, handlesp, flgs, NULL, pc)){
834 case -1 : /* write error */
835 return("Write Error");
837 case 1 : /* fetch error */
838 if(!(gf_puts("[ Error fetching header ]", pc)
839 && !gf_puts(NEWLINE, pc)))
840 return("Write Error");
842 break;
845 else if(a->body->subtype && strucmp(a->body->subtype, "external-body") == 0){
846 int *margin, avail, m1, m2;
848 avail = width;
849 margin = (flgs & FM_NOINDENT) ? NULL : format_view_margin();
851 m1 = MAX(MIN(margin ? margin[0] : 0, avail), 0);
852 avail -= m1;
854 m2 = MAX(MIN(margin ? margin[1] : 0, avail), 0);
855 avail -= m2;
857 if(format_editorial("This part is not included and can be fetched as follows:", avail, flgs, handlesp, pc)
858 || !gf_puts(NEWLINE, pc)
859 || format_editorial(display_parameters(a->body->parameter), avail, flgs, handlesp, pc))
860 return("Write Error");
862 else
863 error_found += decode_text(a, msgno, pc, handlesp,
864 (flgs&FM_DISPLAY) ? InLine : QStatus,
865 flgs);
867 if(!gf_puts(NEWLINE, pc))
868 return("Write Error");
870 break;
872 default:
873 if((decode_err = part_desc(a->number, a->body,
874 (flgs & FM_DISPLAY) ? 1 : 3,
875 width, flgs, pc)) != NULL)
876 return("Write Error");
879 show_parts++;
882 if(!(!error_found
883 && (pith_opt_rfc2369_editorial ? (*pith_opt_rfc2369_editorial)(msgno, handlesp, flgs, width, pc) : 1)
884 && format_blip_seen(msgno)))
885 return("Cannot format body.");
888 return(NULL);
893 format_attachment_list(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc)
895 ATTACH_S *a;
897 if(flgs & FM_NEW_MESS) {
898 zero_atmts(ps_global->atmts);
899 describe_mime(body, "", 1, 1, 0, flgs);
902 /*----- First do the list of parts/attachments if needed ----*/
903 if((flgs & FM_DISPLAY)
904 && (ps_global->atmts[1].description
905 || (ps_global->atmts[0].body
906 && ps_global->atmts[0].body->type != TYPETEXT))){
907 char tmp[6*MAX_SCREEN_COLS + 1], *tmpp;
908 int i, n, maxnumwid = 0, maxsizewid = 0, *margin;
909 int avail, m1, m2, hwid, s1, s2, s3, s4, s5, dwid, shownwid;
910 int sizewid, descwid, dashwid, partwid, padwid;
911 COLOR_PAIR *hdrcolor = NULL;
913 if((flgs & FM_DISPLAY)
914 && !(flgs & FM_NOCOLOR)
915 && pico_usingcolor()
916 && ps_global->VAR_HEADER_GENERAL_FORE_COLOR
917 && ps_global->VAR_HEADER_GENERAL_BACK_COLOR
918 && ps_global->VAR_NORM_FORE_COLOR
919 && ps_global->VAR_NORM_BACK_COLOR
920 && (colorcmp(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
921 ps_global->VAR_NORM_FORE_COLOR)
922 || colorcmp(ps_global->VAR_HEADER_GENERAL_BACK_COLOR,
923 ps_global->VAR_NORM_BACK_COLOR))){
925 if((hdrcolor = new_color_pair(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
926 ps_global->VAR_HEADER_GENERAL_BACK_COLOR)) != NULL){
927 if(!pico_is_good_colorpair(hdrcolor))
928 free_color_pair(&hdrcolor);
932 margin = (flgs & FM_NOINDENT) ? NULL : format_view_margin();
935 * Attachment list header
938 avail = width;
940 m1 = MAX(MIN(margin ? margin[0] : 0, avail), 0);
941 avail -= m1;
943 m2 = MAX(MIN(margin ? margin[1] : 0, avail), 0);
944 avail -= m2;
946 hwid = MAX(avail, 0);
948 i = utf8_width(_("Parts/Attachments:"));
949 partwid = MIN(i, hwid);
950 padwid = hdrcolor ? (hwid-partwid) : 0;
952 if(m1 > 0){
953 snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
954 if(!gf_puts(tmp, pc))
955 return(0);
958 utf8_snprintf(tmp, sizeof(tmp),
959 "%-*.*w%*.*s",
960 /* TRANSLATORS: A label */
961 partwid, partwid, _("Parts/Attachments:"),
962 padwid, padwid, "");
964 if(!((!hdrcolor || embed_color(hdrcolor, pc)) && gf_puts(tmp, pc) && gf_puts(NEWLINE, pc)))
965 return(0);
968 /*----- Figure max display widths -----*/
969 for(a = ps_global->atmts; a->description != NULL; a++){
970 if(MIME_VCALENDAR(a->body->type, a->body->subtype))
971 continue;
973 if((n = utf8_width(a->number)) > maxnumwid)
974 maxnumwid = n;
976 if((n = utf8_width(a->size)) > maxsizewid)
977 maxsizewid = n;
981 * ----- adjust max lengths for nice display -----
983 * marg _ D _ number _ Shown _ _ _ size _ _ description marg
987 avail = width - m1 - m2;
989 s1 = MAX(MIN(1, avail), 0);
990 avail -= s1;
992 dwid = MAX(MIN(1, avail), 0);
993 avail -= dwid;
995 s2 = MAX(MIN(1, avail), 0);
996 avail -= s2;
998 maxnumwid = MIN(maxnumwid, width/3);
999 maxnumwid = MAX(MIN(maxnumwid, avail), 0);
1000 avail -= maxnumwid;
1002 s3 = MAX(MIN(1, avail), 0);
1003 avail -= s3;
1005 shownwid = MAX(MIN(5, avail), 0);
1006 avail -= shownwid;
1008 s4 = MAX(MIN(3, avail), 0);
1009 avail -= s4;
1011 sizewid = MAX(MIN(maxsizewid, avail), 0);
1012 avail -= sizewid;
1014 s5 = MAX(MIN(2, avail), 0);
1015 avail -= s5;
1017 descwid = MAX(0, avail);
1019 /*----- Format the list of attachments -----*/
1020 for(a = ps_global->atmts; a->description != NULL; a++){
1021 COLOR_PAIR *lastc = NULL;
1022 char numbuf[50];
1023 int thisdescwid, padwid;
1025 if(MIME_VCALENDAR(a->body->type, a->body->subtype))
1026 continue;
1027 #ifdef SMIME
1028 if(a->body->type == TYPEMULTIPART
1029 && (strucmp(a->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0))
1030 continue;
1031 #endif /* SMIME */
1033 i = utf8_width((descwid > 2 && a->description) ? a->description : "");
1034 thisdescwid = MIN(i, descwid);
1035 padwid = hdrcolor ? (descwid-thisdescwid) : 0;
1037 if(m1 > 0){
1038 snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
1039 if(!gf_puts(tmp, pc))
1040 return(0);
1043 utf8_snprintf(tmp, sizeof(tmp),
1044 "%*.*s%*.*w%*.*s%-*.*w%*.*s%*.*w%*.*s%*.*w%*.*s%-*.*w",
1045 s1, s1, "",
1046 dwid, dwid,
1047 msgno_part_deleted(ps_global->mail_stream, msgno, a->number) ? "D" : "",
1048 s2, s2, "",
1049 maxnumwid, maxnumwid,
1050 a->number
1051 ? short_str(a->number, numbuf, sizeof(numbuf), maxnumwid, FrontDots)
1052 : "",
1053 s3, s3, "",
1054 shownwid, shownwid,
1055 a->shown ? "Shown" :
1056 (a->can_display != MCD_NONE && !(a->can_display & MCD_EXT_PROMPT))
1057 ? "OK " : "",
1058 s4, s4, "",
1059 sizewid, sizewid,
1060 a->size ? a->size : "",
1061 s5, s5, "",
1062 thisdescwid, thisdescwid,
1063 (descwid > 2 && a->description) ? a->description : "");
1065 if(!(!hdrcolor || embed_color(hdrcolor, pc)))
1066 return(0);
1068 if(F_ON(F_VIEW_SEL_ATTACH, ps_global) && handlesp){
1069 char buf[16], color[64];
1070 int l;
1071 HANDLE_S *h;
1073 for(tmpp = tmp; *tmpp && *tmpp == ' '; tmpp++)
1074 if(!(*pc)(' '))
1075 return(0);
1077 h = new_handle(handlesp);
1078 h->type = Attach;
1079 h->h.attach = a;
1081 snprintf(buf, sizeof(buf), "%d", h->key);
1082 buf[sizeof(buf)-1] = '\0';
1084 if(!(flgs & FM_NOCOLOR)
1085 && handle_start_color(color, sizeof(color), &l, 1)){
1086 lastc = get_cur_embedded_color();
1087 if(!gf_nputs(color, (long) l, pc))
1088 return(0);
1090 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)
1091 && (!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON))))
1092 return(0);
1094 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE)
1095 && (*pc)(strlen(buf)) && gf_puts(buf, pc)))
1096 return(0);
1098 else
1099 tmpp = tmp;
1101 if(!format_env_puts(tmpp, pc))
1102 return(0);
1104 if(F_ON(F_VIEW_SEL_ATTACH, ps_global) && handlesp){
1105 if(lastc){
1106 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
1107 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
1108 return(0);
1111 if(!embed_color(lastc, pc))
1112 return(0);
1114 free_color_pair(&lastc);
1116 else if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
1117 return(0);
1119 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)))
1120 return(0);
1123 if(padwid > 0){
1124 snprintf(tmp, sizeof(tmp), "%*.*s", padwid, padwid, "");
1125 if(!gf_puts(tmp, pc))
1126 return(0);
1129 if(!gf_puts(NEWLINE, pc))
1130 return(0);
1134 * Dashed line after list
1137 if(hdrcolor){
1138 avail = width - m1 - m2;
1139 hwid = MAX(avail, 0);
1141 dashwid = MAX(MIN(40, hwid-2), 0);
1142 padwid = hwid - dashwid;
1143 if(m1 > 0){
1144 snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
1145 if(!gf_puts(tmp, pc))
1146 return(0);
1149 snprintf(tmp, sizeof(tmp),
1150 "%s%*.*s",
1151 repeat_char(dashwid, '-'),
1152 padwid, padwid, "");
1154 else{
1155 avail = width - m1 -2;
1157 dashwid = MAX(MIN(40, avail), 0);
1158 avail -= dashwid;
1160 snprintf(tmp, sizeof(tmp),
1161 "%*.*s%s",
1162 m1, m1, "",
1163 repeat_char(dashwid, '-'));
1166 if(!((!hdrcolor || embed_color(hdrcolor, pc)) && gf_puts(tmp, pc) && gf_puts(NEWLINE, pc)))
1167 return(0);
1169 if(hdrcolor)
1170 free_color_pair(&hdrcolor);
1173 return(1);
1179 * format_blip_seen - if seen bit (which is usually cleared as a side-effect
1180 * of body part fetches as we're formatting) for the
1181 * given message isn't set (likely because there
1182 * weren't any parts suitable for display), then make
1183 * sure to set it here.
1186 format_blip_seen(long int msgno)
1188 MESSAGECACHE *mc;
1190 if(msgno > 0L && ps_global->mail_stream
1191 && msgno <= ps_global->mail_stream->nmsgs
1192 && (mc = mail_elt(ps_global->mail_stream, msgno))
1193 && !mc->seen
1194 && !ps_global->mail_stream->rdonly)
1195 mail_flag(ps_global->mail_stream, long2string(msgno), "\\SEEN", ST_SET);
1197 return(1);
1202 * is_an_env_hdr - is this name a header in the envelope structure?
1204 * name - the header name to check
1207 is_an_env_hdr(char *name)
1209 register int i;
1211 for(i = 0; envelope_hdrs[i].name; i++)
1212 if(!strucmp(name, envelope_hdrs[i].name))
1213 return(1);
1215 return(0);
1222 * is_an_addr_hdr - is this an address header?
1224 * name - the header name to check
1227 is_an_addr_hdr(char *fieldname)
1229 char fbuf[FBUF_LEN+1];
1230 char *colon, *fname;
1231 static char *addr_headers[] = {
1232 "from",
1233 "reply-to",
1234 "to",
1235 "cc",
1236 "bcc",
1237 "return-path",
1238 "sender",
1239 "x-sender",
1240 "x-x-sender",
1241 "resent-from",
1242 "resent-to",
1243 "resent-cc",
1244 NULL
1247 /* so it is pointing to NULL */
1248 char **p = addr_headers + sizeof(addr_headers)/sizeof(*addr_headers) - 1;
1250 if((colon = strindex(fieldname, ':')) != NULL){
1251 strncpy(fbuf, fieldname, MIN(colon-fieldname,sizeof(fbuf)));
1252 fbuf[MIN(colon-fieldname,sizeof(fbuf)-1)] = '\0';
1253 fname = fbuf;
1255 else
1256 fname = fieldname;
1258 if(fname && *fname){
1259 for(p = addr_headers; *p; p++)
1260 if(!strucmp(fname, *p))
1261 break;
1264 return((*p) ? 1 : 0);
1269 * Format a single field from the envelope
1271 void
1272 format_env_hdr(MAILSTREAM *stream, long int msgno, char *section, ENVELOPE *env,
1273 fmt_env_t fmt_env, gf_io_t pc, char *field, char *oacs, int flags)
1275 register int i;
1277 if(!fmt_env)
1278 fmt_env = format_envelope;
1280 for(i = 0; envelope_hdrs[i].name; i++)
1281 if(!strucmp(field, envelope_hdrs[i].name)){
1282 (*fmt_env)(stream, msgno, section, env, pc, envelope_hdrs[i].val, oacs, flags);
1283 return;
1289 * Look through header string beginning with "begin", for the next
1290 * occurrence of header "field". Set "start" to that. Set "end" to point one
1291 * position past all of the continuation lines that go with "field".
1292 * That is, if "end" is converted to a null
1293 * character then the string "start" will be the next occurence of header
1294 * "field" including all of its continuation lines. Assume we
1295 * have CRLF's as end of lines.
1297 * If "field" is NULL, then we just leave "start" pointing to "begin" and
1298 * make "end" the end of that header.
1300 * Returns 1 if found, 0 if not.
1303 delineate_this_header(char *field, char *begin, char **start, char **end)
1305 char tmpfield[MAILTMPLEN+2]; /* copy of field with colon appended */
1306 char *p;
1307 char *begin_srch;
1309 if(field == NULL){
1310 if(!begin || !*begin || isspace((unsigned char)*begin))
1311 return 0;
1312 else
1313 *start = begin;
1315 else{
1316 strncpy(tmpfield, field, sizeof(tmpfield)-2);
1317 tmpfield[sizeof(tmpfield)-2] = '\0';
1318 strncat(tmpfield, ":", sizeof(tmpfield)-strlen(tmpfield)-1);
1319 tmpfield[sizeof(tmpfield)-1] = '\0';
1322 * We require that start is at the beginning of a line, so
1323 * either it equals begin (which we assume is the beginning of a
1324 * line) or it is preceded by a CRLF.
1326 begin_srch = begin;
1327 *start = srchstr(begin_srch, tmpfield);
1328 while(*start && *start != begin
1329 && !(*start - 2 >= begin && ISRFCEOL(*start - 2))){
1330 begin_srch = *start + 1;
1331 *start = srchstr(begin_srch, tmpfield);
1334 if(!*start)
1335 return 0;
1338 for(p = *start; *p; p++){
1339 if(ISRFCEOL(p)
1340 && (!isspace((unsigned char)*(p+2)) || *(p+2) == '\015')){
1342 * The final 015 in the test above is to test for the end
1343 * of the headers.
1345 *end = p+2;
1346 break;
1350 if(!*p)
1351 *end = p;
1353 return 1;
1359 handle_start_color(char *colorstring, size_t buflen, int *len, int use_hdr_color)
1361 *len = 0;
1363 if(pico_usingcolor()){
1364 char *fg = NULL, *bg = NULL, *s;
1365 char *basefg = NULL, *basebg = NULL;
1367 basefg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_FORE_COLOR
1368 : ps_global->VAR_NORM_FORE_COLOR;
1369 basebg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_BACK_COLOR
1370 : ps_global->VAR_NORM_BACK_COLOR;
1372 if(ps_global->VAR_SLCTBL_FORE_COLOR
1373 && colorcmp(ps_global->VAR_SLCTBL_FORE_COLOR, basefg))
1374 fg = ps_global->VAR_SLCTBL_FORE_COLOR;
1376 if(ps_global->VAR_SLCTBL_BACK_COLOR
1377 && colorcmp(ps_global->VAR_SLCTBL_BACK_COLOR, basebg))
1378 bg = ps_global->VAR_SLCTBL_BACK_COLOR;
1380 if(bg || fg){
1381 COLOR_PAIR *tmp;
1384 * The blacks are just known good colors for
1385 * testing whether the other color is good.
1387 if((tmp = new_color_pair(fg ? fg : colorx(COL_BLACK),
1388 bg ? bg : colorx(COL_BLACK))) != NULL){
1389 if(pico_is_good_colorpair(tmp))
1390 for(s = color_embed(fg, bg);
1391 (*len) < buflen && (colorstring[*len] = *s);
1392 s++, (*len)++)
1395 free_color_pair(&tmp);
1399 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
1400 strncpy(colorstring + (*len), url_embed(TAG_BOLDON), MIN(3,buflen-(*len)));
1401 *len += 2;
1405 colorstring[buflen-1] = '\0';
1407 return(*len != 0);
1412 handle_end_color(char *colorstring, size_t buflen, int *len)
1414 *len = 0;
1415 if(pico_usingcolor()){
1416 char *fg = NULL, *bg = NULL, *s;
1419 * We need to change the fg and bg colors back even if they
1420 * are the same as the Normal Colors so that color_a_quote
1421 * will have a chance to pick up those colors as the ones to
1422 * switch to. We don't do this before the handle above so that
1423 * the quote color will flow into the selectable item when
1424 * the selectable item color is partly the same as the
1425 * normal color. That is, suppose the normal color was black on
1426 * cyan and the selectable color was blue on cyan, only a fg color
1427 * change. We preserve the only-a-fg-color-change in a quote by
1428 * letting the quote background color flow into the selectable text.
1430 if(ps_global->VAR_SLCTBL_FORE_COLOR)
1431 fg = ps_global->VAR_NORM_FORE_COLOR;
1433 if(ps_global->VAR_SLCTBL_BACK_COLOR)
1434 bg = ps_global->VAR_NORM_BACK_COLOR;
1436 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
1437 strncpy(colorstring, url_embed(TAG_BOLDOFF), MIN(3,buflen));
1438 *len = 2;
1441 if(fg || bg)
1442 for(s = color_embed(fg, bg); (*len) < buflen && (colorstring[*len] = *s); s++, (*len)++)
1446 colorstring[buflen-1] = '\0';
1448 return(*len != 0);
1452 char *
1453 url_embed(int embed)
1455 static char buf[3] = {TAG_EMBED};
1456 buf[1] = embed;
1457 buf[2] = '\0';
1458 return(buf);
1463 * Paint the signature.
1466 color_signature(long int linenum, char *line, LT_INS_S **ins, void *is_in_sig)
1468 struct variable *vars = ps_global->vars;
1469 int *in_sig_block;
1470 COLOR_PAIR *col = NULL;
1472 if(is_in_sig == NULL)
1473 return 0;
1475 in_sig_block = (int *) is_in_sig;
1477 if(!strcmp(line, SIGDASHES))
1478 *in_sig_block = START_SIG_BLOCK;
1479 else if(*line == '\0')
1481 * Suggested by Eduardo: allow for a blank line right after
1482 * the sigdashes.
1484 *in_sig_block = (*in_sig_block == START_SIG_BLOCK)
1485 ? IN_SIG_BLOCK : OUT_SIG_BLOCK;
1486 else
1487 *in_sig_block = (*in_sig_block != OUT_SIG_BLOCK)
1488 ? IN_SIG_BLOCK : OUT_SIG_BLOCK;
1490 if(*in_sig_block != OUT_SIG_BLOCK
1491 && VAR_SIGNATURE_FORE_COLOR && VAR_SIGNATURE_BACK_COLOR
1492 && (col = new_color_pair(VAR_SIGNATURE_FORE_COLOR,
1493 VAR_SIGNATURE_BACK_COLOR))){
1494 if(!pico_is_good_colorpair(col))
1495 free_color_pair(&col);
1498 if(col){
1499 char *p, fg[RGBLEN + 1], bg[RGBLEN + 1], rgbbuf[RGBLEN + 1];
1501 ins = gf_line_test_new_ins(ins, line,
1502 color_embed(col->fg, col->bg),
1503 (2 * RGBLEN) + 4);
1505 strncpy(fg, color_to_asciirgb(VAR_NORM_FORE_COLOR), sizeof(fg));
1506 fg[sizeof(fg)-1] = '\0';
1507 strncpy(bg, color_to_asciirgb(VAR_NORM_BACK_COLOR), sizeof(bg));
1508 bg[sizeof(bg)-1] = '\0';
1511 * Loop watching colors, and override with
1512 * signature color whenever the normal foreground and background
1513 * colors are in force.
1516 for(p = line; *p; )
1517 if(*p++ == TAG_EMBED){
1519 switch(*p++){
1520 case TAG_HANDLE :
1521 p += *p + 1; /* skip handle key */
1522 break;
1524 case TAG_FGCOLOR :
1525 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1526 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1527 p += RGBLEN; /* advance past color value */
1529 if(!colorcmp(rgbbuf, VAR_NORM_FORE_COLOR)
1530 && !colorcmp(bg, VAR_NORM_BACK_COLOR))
1531 ins = gf_line_test_new_ins(ins, p,
1532 color_embed(col->fg,NULL),
1533 RGBLEN + 2);
1534 break;
1536 case TAG_BGCOLOR :
1537 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1538 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1539 p += RGBLEN; /* advance past color value */
1541 if(!colorcmp(rgbbuf, VAR_NORM_BACK_COLOR)
1542 && !colorcmp(fg, VAR_NORM_FORE_COLOR))
1543 ins = gf_line_test_new_ins(ins, p,
1544 color_embed(NULL,col->bg),
1545 RGBLEN + 2);
1547 break;
1549 default :
1550 break;
1554 ins = gf_line_test_new_ins(ins, line + strlen(line),
1555 color_embed(VAR_NORM_FORE_COLOR,
1556 VAR_NORM_BACK_COLOR),
1557 (2 * RGBLEN) + 4);
1558 free_color_pair(&col);
1561 return 0;
1566 * Line filter to add color to displayed headers.
1569 color_headers(long int linenum, char *line, LT_INS_S **ins, void *local)
1571 static char field[FBUF_LEN + 1];
1572 char fg[RGBLEN + 1], bg[RGBLEN + 1], rgbbuf[RGBLEN + 1];
1573 char *p, *q, *value, *beg;
1574 COLOR_PAIR *color;
1575 int in_quote = 0, in_comment = 0, did_color = 0;
1576 struct variable *vars = ps_global->vars;
1578 field[FBUF_LEN] = '\0';
1580 if(isspace((unsigned char)*line)) /* continuation line */
1581 value = line;
1582 else{
1583 if(!(value = strindex(line, ':')))
1584 return(0);
1586 memset(field, 0, sizeof(field));
1587 strncpy(field, line, MIN(value-line, sizeof(field)-1));
1590 for(value++; isspace((unsigned char)*value); value++)
1593 strncpy(fg, color_to_asciirgb(VAR_HEADER_GENERAL_FORE_COLOR), sizeof(fg));
1594 fg[sizeof(fg)-1] = '\0';
1595 strncpy(bg, color_to_asciirgb(VAR_HEADER_GENERAL_BACK_COLOR), sizeof(bg));
1596 bg[sizeof(bg)-1] = '\0';
1599 * Split into two cases depending on whether this is a header which
1600 * contains addresses or not. We may color addresses separately.
1602 if(is_an_addr_hdr(field)){
1605 * If none of the patterns are for this header, don't bother parsing
1606 * and checking each address.
1608 if(!any_hdr_color(field))
1609 return(0);
1612 * First check for patternless patterns which color whole line.
1614 if((color = hdr_color(field, NULL, ps_global->hdr_colors)) != NULL){
1615 if(pico_is_good_colorpair(color)){
1616 ins = gf_line_test_new_ins(ins, value,
1617 color_embed(color->fg, color->bg),
1618 (2 * RGBLEN) + 4);
1619 strncpy(fg, color_to_asciirgb(color->fg), sizeof(fg));
1620 fg[sizeof(fg)-1] = '\0';
1621 strncpy(bg, color_to_asciirgb(color->bg), sizeof(bg));
1622 bg[sizeof(bg)-1] = '\0';
1623 did_color++;
1625 else
1626 free_color_pair(&color);
1630 * Then go through checking address by address.
1631 * Keep track of quotes and watch for color changes, and override
1632 * with most recent header color whenever the normal foreground
1633 * and background colors are in force.
1635 beg = p = value;
1636 while(*p){
1637 switch(*p){
1638 case '\\':
1639 /* skip next character */
1640 if(*(p+1) && (in_comment || in_quote))
1641 p += 2;
1642 else
1643 p++;
1645 break;
1647 case '"':
1648 if(!in_comment)
1649 in_quote = 1 - in_quote;
1651 p++;
1652 break;
1654 case '(':
1655 in_comment++;
1656 p++;
1657 break;
1659 case ')':
1660 if(in_comment > 0)
1661 in_comment--;
1663 p++;
1664 break;
1666 case ',':
1667 if(!(in_quote || in_comment)){
1668 /* we reached the end of this address */
1669 *p = '\0';
1670 if(color)
1671 free_color_pair(&color);
1673 if((color = hdr_color(field, beg,
1674 ps_global->hdr_colors)) != NULL){
1675 if(pico_is_good_colorpair(color)){
1676 did_color++;
1677 ins = gf_line_test_new_ins(ins, beg,
1678 color_embed(color->fg,
1679 color->bg),
1680 (2 * RGBLEN) + 4);
1681 *p = ',';
1682 for(q = p; q > beg &&
1683 isspace((unsigned char)*(q-1)); q--)
1686 ins = gf_line_test_new_ins(ins, q,
1687 color_embed(fg, bg),
1688 (2 * RGBLEN) + 4);
1690 else
1691 free_color_pair(&color);
1693 else
1694 *p = ',';
1696 for(p++; isspace((unsigned char)*p); p++)
1699 beg = p;
1701 else
1702 p++;
1704 break;
1706 case TAG_EMBED:
1707 switch(*(++p)){
1708 case TAG_HANDLE:
1709 p++;
1710 p += *p + 1; /* skip handle key */
1711 break;
1713 case TAG_FGCOLOR:
1714 p++;
1715 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1716 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1717 p += RGBLEN; /* advance past color value */
1719 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_FORE_COLOR))
1720 ins = gf_line_test_new_ins(ins, p,
1721 color_embed(color->fg,NULL),
1722 RGBLEN + 2);
1723 break;
1725 case TAG_BGCOLOR:
1726 p++;
1727 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1728 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1729 p += RGBLEN; /* advance past color value */
1731 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_BACK_COLOR))
1732 ins = gf_line_test_new_ins(ins, p,
1733 color_embed(NULL,color->bg),
1734 RGBLEN + 2);
1736 break;
1738 default:
1739 break;
1742 break;
1744 default:
1745 p++;
1746 break;
1750 for(q = beg; *q && isspace((unsigned char)*q); q++)
1753 if(*q && !(in_quote || in_comment)){
1754 /* we reached the end of this address */
1755 if(color)
1756 free_color_pair(&color);
1758 if((color = hdr_color(field, beg, ps_global->hdr_colors)) != NULL){
1759 if(pico_is_good_colorpair(color)){
1760 did_color++;
1761 ins = gf_line_test_new_ins(ins, beg,
1762 color_embed(color->fg,
1763 color->bg),
1764 (2 * RGBLEN) + 4);
1765 for(q = p; q > beg && isspace((unsigned char)*(q-1)); q--)
1768 ins = gf_line_test_new_ins(ins, q,
1769 color_embed(fg, bg),
1770 (2 * RGBLEN) + 4);
1772 else
1773 free_color_pair(&color);
1777 if(color)
1778 free_color_pair(&color);
1780 if(did_color)
1781 ins = gf_line_test_new_ins(ins, line + strlen(line),
1782 color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
1783 VAR_HEADER_GENERAL_BACK_COLOR),
1784 (2 * RGBLEN) + 4);
1786 else{
1788 color = hdr_color(field, value, ps_global->hdr_colors);
1790 if(color){
1791 if(pico_is_good_colorpair(color)){
1792 ins = gf_line_test_new_ins(ins, value,
1793 color_embed(color->fg, color->bg),
1794 (2 * RGBLEN) + 4);
1797 * Loop watching colors, and override with header
1798 * color whenever the normal foreground and background
1799 * colors are in force.
1801 p = value;
1802 while(*p)
1803 if(*p++ == TAG_EMBED){
1805 switch(*p++){
1806 case TAG_HANDLE:
1807 p += *p + 1; /* skip handle key */
1808 break;
1810 case TAG_FGCOLOR:
1811 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1812 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1813 p += RGBLEN; /* advance past color value */
1815 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_FORE_COLOR)
1816 && !colorcmp(bg, VAR_HEADER_GENERAL_BACK_COLOR))
1817 ins = gf_line_test_new_ins(ins, p,
1818 color_embed(color->fg,NULL),
1819 RGBLEN + 2);
1820 break;
1822 case TAG_BGCOLOR:
1823 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1824 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1825 p += RGBLEN; /* advance past color value */
1827 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_BACK_COLOR)
1828 && !colorcmp(fg, VAR_HEADER_GENERAL_FORE_COLOR))
1829 ins = gf_line_test_new_ins(ins, p,
1830 color_embed(NULL,color->bg),
1831 RGBLEN + 2);
1833 break;
1835 default:
1836 break;
1840 ins = gf_line_test_new_ins(ins, line + strlen(line),
1841 color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
1842 VAR_HEADER_GENERAL_BACK_COLOR),
1843 (2 * RGBLEN) + 4);
1846 free_color_pair(&color);
1850 return(0);
1855 url_hilite(long int linenum, char *line, LT_INS_S **ins, void *local)
1857 register char *lp, *up = NULL, *urlp = NULL,
1858 *weburlp = NULL, *mailurlp = NULL;
1859 int n, n1, n2, n3, l;
1860 char buf[256], color[256];
1861 HANDLE_S *h;
1862 URL_HILITE_S *uh;
1864 for(lp = line; ; lp = up + n){
1865 /* scan for all of them so we can choose the first */
1866 if(F_ON(F_VIEW_SEL_URL,ps_global))
1867 urlp = rfc1738_scan(lp, &n1);
1868 if(F_ON(F_VIEW_SEL_URL_HOST,ps_global))
1869 weburlp = web_host_scan(lp, &n2);
1870 if(F_ON(F_SCAN_ADDR,ps_global))
1871 mailurlp = mail_addr_scan(lp, &n3);
1873 if(urlp || weburlp || mailurlp){
1874 up = urlp ? urlp :
1875 weburlp ? weburlp : mailurlp;
1876 if(up == urlp && weburlp && weburlp < up)
1877 up = weburlp;
1878 if(mailurlp && mailurlp < up)
1879 up = mailurlp;
1881 if(up == urlp){
1882 n = n1;
1883 weburlp = mailurlp = NULL;
1885 else if(up == weburlp){
1886 n = n2;
1887 mailurlp = NULL;
1889 else{
1890 n = n3;
1891 weburlp = NULL;
1894 else
1895 break;
1897 uh = (URL_HILITE_S *) local;
1899 h = new_handle(uh->handlesp);
1900 h->type = URL;
1901 h->h.url.path = (char *) fs_get((n + 10) * sizeof(char));
1902 snprintf(h->h.url.path, n+10, "%s%.*s",
1903 weburlp ? "http://" : (mailurlp ? "mailto:" : ""), n, up);
1904 h->h.url.path[n+10-1] = '\0';
1906 if(handle_start_color(color, sizeof(color), &l, uh->hdr_color))
1907 ins = gf_line_test_new_ins(ins, up, color, l);
1908 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global))
1909 ins = gf_line_test_new_ins(ins, up, url_embed(TAG_BOLDON), 2);
1911 buf[0] = TAG_EMBED;
1912 buf[1] = TAG_HANDLE;
1913 snprintf(&buf[3], sizeof(buf)-3, "%d", h->key);
1914 buf[sizeof(buf)-1] = '\0';
1915 buf[2] = strlen(&buf[3]);
1916 ins = gf_line_test_new_ins(ins, up, buf, (int) buf[2] + 3);
1918 /* in case it was the current selection */
1919 ins = gf_line_test_new_ins(ins, up + n, url_embed(TAG_INVOFF), 2);
1921 if(scroll_handle_end_color(color, sizeof(color), &l, uh->hdr_color))
1922 ins = gf_line_test_new_ins(ins, up + n, color, l);
1923 else
1924 ins = gf_line_test_new_ins(ins, up + n, url_embed(TAG_BOLDOFF), 2);
1926 urlp = weburlp = mailurlp = NULL;
1929 return(0);
1934 url_hilite_hdr(long int linenum, char *line, LT_INS_S **ins, void *local)
1936 static int check_for_urls = 0;
1937 register char *lp;
1939 if(isspace((unsigned char)*line)) /* continuation, check or not
1940 depending on last line */
1941 lp = line;
1942 else{
1943 check_for_urls = 0;
1944 if((lp = strchr(line, ':')) != NULL){ /* there ought to always be a colon */
1945 FieldType ft;
1947 *lp = '\0';
1949 if(((ft = pine_header_standard(line)) == FreeText
1950 || ft == Subject
1951 || ft == TypeUnknown)
1952 && strucmp(line, "message-id")
1953 && strucmp(line, "newsgroups")
1954 && strucmp(line, "references")
1955 && strucmp(line, "in-reply-to")
1956 && strucmp(line, "received")
1957 && strucmp(line, "date")){
1958 check_for_urls = 1;
1961 *lp = ':';
1965 if(check_for_urls)
1966 (void) url_hilite(linenum, lp + 1, ins, local);
1968 return(0);
1973 pad_to_right_edge(long int linenum, char *line, LT_INS_S **ins, void *local)
1975 char *p;
1976 int wid = 0;
1977 int total_wid;
1978 struct variable *vars = ps_global->vars;
1980 if(!line[0])
1981 return 0;
1983 total_wid = *((int *) local);
1985 /* calculate width of line */
1986 p = line;
1987 while(*p){
1989 switch(*p){
1990 case TAG_EMBED:
1991 p++;
1992 switch(*p){
1993 case TAG_HANDLE:
1994 p++;
1995 p += *p + 1; /* skip handle key */
1996 break;
1998 case TAG_FGCOLOR :
1999 case TAG_BGCOLOR :
2000 p += (RGBLEN + 1); /* 1 for TAG, RGBLEN for color */
2001 break;
2003 case TAG_INVON:
2004 case TAG_INVOFF:
2005 case TAG_BOLDON:
2006 case TAG_BOLDOFF:
2007 case TAG_ULINEON:
2008 case TAG_ULINEOFF:
2009 p++;
2010 break;
2012 default: /* literal embed char */
2013 break;
2016 break;
2018 case TAB:
2019 p++;
2020 while(((++wid) & 0x07) != 0) /* add tab's spaces */
2023 break;
2025 default:
2026 wid += width_at_this_position((unsigned char *) p, strlen(p));
2027 p++;
2028 break;
2032 if(total_wid > wid){
2033 ins = gf_line_test_new_ins(ins, line + strlen(line),
2034 color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
2035 VAR_HEADER_GENERAL_BACK_COLOR),
2036 (2 * RGBLEN) + 4);
2037 ins = gf_line_test_new_ins(ins, line+strlen(line),
2038 repeat_char(total_wid-wid, ' '), total_wid-wid);
2039 ins = gf_line_test_new_ins(ins, line + strlen(line),
2040 color_embed(VAR_NORM_FORE_COLOR,
2041 VAR_NORM_BACK_COLOR),
2042 (2 * RGBLEN) + 4);
2045 return(0);
2050 #define UES_LEN 12
2051 #define UES_MAX 32
2053 url_external_specific_handler(char *url, int len)
2055 static char list[UES_LEN * UES_MAX];
2057 if(url){
2058 char *p;
2059 int i;
2061 for(i = 0; i < UES_MAX && *(p = &list[i * UES_LEN]); i++)
2062 if(strlen(p) == len && !struncmp(p, url, len))
2063 return(1);
2065 else{ /* initialize! */
2066 char **l, *test, *cmd, *p, *p2;
2067 int i = 0, n;
2069 memset(list, 0, sizeof(list));
2070 for(l = ps_global->VAR_BROWSER ; l && *l; l++){
2071 get_pair(*l, &test, &cmd, 1, 1);
2073 if((p = srchstr(test, "_scheme(")) && (p2 = strstr(p+8, ")_"))){
2074 *p2 = '\0';
2076 for(p += 8; *p && i < UES_MAX; p += n)
2077 if((p2 = strchr(p, ',')) != NULL){
2078 if((n = p2 - p) < UES_LEN){
2079 strncpy(&list[i * UES_LEN], p, MIN(n, sizeof(list)-(i * UES_LEN)));
2080 i++;
2082 else
2083 dprint((1,
2084 "* * * HANLDER TOO LONG: %.*s\n", n,
2085 p ? p : "?"));
2087 n++;
2089 else{
2090 if(strlen(p) <= UES_LEN){
2091 strncpy(&list[i * UES_LEN], p, sizeof(list)-(i * UES_LEN));
2092 i++;
2095 break;
2099 if(test)
2100 fs_give((void **) &test);
2102 if(cmd)
2103 fs_give((void **) &cmd);
2107 return(0);
2112 url_imap_folder(char *true_url, char **folder, imapuid_t *uid_val,
2113 imapuid_t *uid, char **search, int silent)
2115 char *url, *scheme, *p, *cmd, *server = NULL,
2116 *user = NULL, *auth = NULL, *mailbox = NULL,
2117 *section = NULL;
2118 size_t l;
2119 int rv = URL_IMAP_ERROR;
2122 * Since we're planting nulls, operate on a temporary copy...
2124 scheme = silent ? NULL : "IMAP";
2125 url = cpystr(true_url + 7);
2127 /* Try to pick apart the "iserver" portion */
2128 if((cmd = strchr(url, '/')) != NULL){ /* iserver "/" [mailbox] ? */
2129 *cmd++ = '\0';
2131 else{
2132 dprint((2, "-- URL IMAP FOLDER: missing: %s\n",
2133 url ? url : "?"));
2134 cmd = &url[strlen(url)-1]; /* assume only iserver */
2137 if((p = strchr(url, '@')) != NULL){ /* user | auth | pass? */
2138 *p++ = '\0';
2139 server = rfc1738_str(p);
2141 /* only ";auth=*" supported (and also ";auth=anonymous") */
2142 if((p = srchstr(url, ";auth=")) != NULL){
2143 *p = '\0';
2144 auth = rfc1738_str(p + 6);
2147 if(*url)
2148 user = rfc1738_str(url);
2150 else
2151 server = rfc1738_str(url);
2153 if(!*server)
2154 return(url_bogus_imap(&url, scheme, "No server specified"));
2157 * "iserver" in hand, pick apart the "icommand"...
2159 p = NULL;
2160 if(!*cmd || (p = srchstr(cmd, ";type="))){
2161 char *criteria;
2164 * No "icommand" (all top-level folders) or "imailboxlist"...
2166 if(p){
2167 *p = '\0'; /* tie off criteria */
2168 criteria = rfc1738_str(cmd); /* get "enc_list_mailbox" */
2169 if(!strucmp(p = rfc1738_str(p+6), "lsub"))
2170 rv |= URL_IMAP_IMBXLSTLSUB;
2171 else if(strucmp(p, "list"))
2172 return(url_bogus_imap(&url, scheme,
2173 "Invalid list type specified"));
2175 else{
2176 rv |= URL_IMAP_ISERVERONLY;
2177 criteria = "";
2180 /* build folder list from specified server/criteria/list-method */
2181 l = strlen(server) + strlen(criteria) + 10 + (user ? (strlen(user)+2) : 9);
2182 *folder = (char *) fs_get((l+1) * sizeof(char));
2183 snprintf(*folder, l+1, "{%s/%s%s%s}%s%s%s", server,
2184 user ? "user=\"" : "Anonymous",
2185 user ? user : "",
2186 user ? "\"" : "",
2187 *criteria ? "[" : "", criteria, *criteria ? "[" : "");
2188 (*folder)[l] = '\0';
2189 rv |= URL_IMAP_IMAILBOXLIST;
2191 else{
2192 if((p = srchstr(cmd, "/;uid=")) != NULL){ /* "imessagepart" */
2193 *p = '\0'; /* tie off mailbox [uidvalidity] */
2194 if((section = srchstr(p += 6, "/;section=")) != NULL){
2195 *section = '\0'; /* tie off UID */
2196 section = rfc1738_str(section + 10);
2197 /* BUG: verify valid section spec ala rfc 2060 */
2198 dprint((2,
2199 "-- URL IMAP FOLDER: section not used: %s\n",
2200 section ? section : "?"));
2203 if(!(*uid = rfc1738_num(&p)) || *p) /* decode UID */
2204 return(url_bogus_imap(&url, scheme, "Invalid data in UID"));
2206 /* optional "uidvalidity"? */
2207 if((p = srchstr(cmd, ";uidvalidity=")) != NULL){
2208 *p = '\0';
2209 p += 13;
2210 if(!(*uid_val = rfc1738_num(&p)) || *p)
2211 return(url_bogus_imap(&url, scheme,
2212 "Invalid UIDVALIDITY"));
2215 mailbox = rfc1738_str(cmd);
2216 rv = URL_IMAP_IMESSAGEPART;
2218 else{ /* "imessagelist" */
2219 /* optional "uidvalidity"? */
2220 if((p = srchstr(cmd, ";uidvalidity=")) != NULL){
2221 *p = '\0';
2222 p += 13;
2223 if(!(*uid_val = rfc1738_num(&p)) || *p)
2224 return(url_bogus_imap(&url, scheme,
2225 "Invalid UIDVALIDITY"));
2228 /* optional "enc_search"? */
2229 if((p = strchr(cmd, '?')) != NULL){
2230 *p = '\0';
2231 if(search)
2232 *search = cpystr(rfc1738_str(p + 1));
2233 /* BUG: verify valid search spec ala rfc 2060 */
2236 mailbox = rfc1738_str(cmd);
2237 rv = URL_IMAP_IMESSAGELIST;
2240 if(auth && *auth != '*' && strucmp(auth, "anonymous"))
2241 q_status_message(SM_ORDER, 3, 3,
2242 "Unsupported authentication method. Using standard login.");
2245 * At this point our structure should contain the
2246 * digested url. Now put it together for c-client...
2248 l = strlen(server) + 8 + (mailbox ? strlen(mailbox) : 0)
2249 + (user ? (strlen(user)+2) : 9);
2250 *folder = (char *) fs_get((l+1) * sizeof(char));
2251 snprintf(*folder, l+1, "{%s%s%s%s%s}%s", server,
2252 (user || !(auth && strucmp(auth, "anonymous"))) ? "/" : "",
2253 user ? "user=\"" : ((auth && strucmp(auth, "anonymous")) ? "" : "Anonymous"),
2254 user ? user : "",
2255 user ? "\"" : "",
2256 mailbox);
2257 (*folder)[l] = '\0';
2260 fs_give((void **) &url);
2261 return(rv);
2266 url_bogus_imap(char **freeme, char *url, char *problem)
2268 fs_give((void **) freeme);
2269 (void) url_bogus(url, problem);
2270 return(URL_IMAP_ERROR);
2275 * url_bogus - report url syntax errors and such
2278 url_bogus(char *url, char *reason)
2280 dprint((2, "-- bogus url \"%s\": %s\n",
2281 url ? url : "<NULL URL>", reason ? reason : "?"));
2282 if(url)
2283 q_status_message3(SM_ORDER|SM_DING, 2, 3,
2284 "Malformed \"%.*s\" URL: %.200s",
2285 (void *) (strchr(url, ':') - url), url, reason);
2287 return(0);
2292 /*----------------------------------------------------------------------
2293 Format header text suitable for display
2295 Args: stream -- mail stream for various header text fetches
2296 msgno -- sequence number in stream of message we're interested in
2297 section -- which section of message
2298 env -- pointer to msg's envelope
2299 hdrs -- struct containing what's to get formatted
2300 prefix -- prefix to append to each output line
2301 handlesp -- address of pointer to the message's handles
2302 flags -- FM_ flags, see pith/mailview.h
2303 final_pc -- function to write header text with
2305 Result: 0 if all's well, -1 if write error, 1 if fetch error
2307 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2308 in the local convention.
2310 ----*/
2311 #define FHT_OK 0
2312 #define FHT_WRTERR -1
2313 #define FHT_FTCHERR 1
2315 format_header(MAILSTREAM *stream, long int msgno, char *section, ENVELOPE *env,
2316 HEADER_S *hdrs, char *prefix, HANDLE_S **handlesp, int flags,
2317 fmt_env_t fmt_env, gf_io_t final_pc)
2319 int rv = FHT_OK;
2320 int nfields, i;
2321 char *h = NULL, **fields = NULL, **v, *q, *start,
2322 *finish, *current;
2323 STORE_S *tmp_store;
2324 URL_HILITE_S uh;
2325 gf_io_t tmp_pc, tmp_gc;
2326 struct variable *vars = ps_global->vars;
2328 if((tmp_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL)
2329 gf_set_so_writec(&tmp_pc, tmp_store);
2330 else
2331 return(FHT_WRTERR);
2333 if(!fmt_env)
2334 fmt_env = format_envelope;
2336 if(ps_global->full_header == 2){
2337 rv = format_raw_header(stream, msgno, section, tmp_pc);
2339 else{
2341 * First, calculate how big a fields array we need.
2344 /* Custom header viewing list specified */
2345 if(hdrs->type == HD_LIST){
2346 /* view all these headers */
2347 if(!hdrs->except){
2348 for(nfields = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
2349 if(!is_an_env_hdr(q))
2350 nfields++;
2352 /* view all except these headers */
2353 else{
2354 for(nfields = 0, v = hdrs->h.l; *v != NULL; v++)
2355 nfields++;
2357 if(nfields > 1)
2358 nfields--; /* subtract one for ALL_EXCEPT field */
2361 else
2362 nfields = 6; /* default view */
2364 /* allocate pointer space */
2365 if(nfields){
2366 fields = (char **)fs_get((size_t)(nfields+1) * sizeof(char *));
2367 memset(fields, 0, (size_t)(nfields+1) * sizeof(char *));
2370 if(hdrs->type == HD_LIST){
2371 /* view all these headers */
2372 if(!hdrs->except){
2373 /* put the non-envelope headers in fields */
2374 if(nfields)
2375 for(i = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
2376 if(!is_an_env_hdr(q))
2377 fields[i++] = q;
2379 /* view all except these headers */
2380 else{
2381 /* put the list of headers not to view in fields */
2382 if(nfields)
2383 for(i = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
2384 if(strucmp(ALL_EXCEPT, q))
2385 fields[i++] = q;
2388 v = hdrs->h.l;
2390 else{
2391 if(nfields){
2392 fields[i = 0] = "Resent-Date";
2393 fields[++i] = "Resent-From";
2394 fields[++i] = "Resent-To";
2395 fields[++i] = "Resent-cc";
2396 fields[++i] = "Resent-Subject";
2399 v = fields;
2402 /* custom view with exception list */
2403 if(hdrs->type == HD_LIST && hdrs->except){
2405 * Go through each header in h and print it.
2406 * First we check to see if it is an envelope header so we
2407 * can print our envelope version of it instead of the raw version.
2410 /* fetch all the other headers */
2411 if(nfields)
2412 h = pine_fetchheader_lines_not(stream, msgno, section, fields);
2414 for(current = h;
2415 h && delineate_this_header(NULL, current, &start, &finish);
2416 current = finish){
2417 char tmp[MAILTMPLEN+1];
2418 char *colon_loc;
2420 colon_loc = strindex(start, ':');
2421 if(colon_loc && colon_loc < finish){
2422 strncpy(tmp, start, MIN(colon_loc-start, sizeof(tmp)-1));
2423 tmp[MIN(colon_loc-start, sizeof(tmp)-1)] = '\0';
2425 else
2426 colon_loc = NULL;
2428 if(colon_loc && is_an_env_hdr(tmp)){
2429 char *dummystart, *dummyfinish;
2432 * Pretty format for env hdrs.
2433 * If the same header appears more than once, only
2434 * print the last to avoid duplicates.
2435 * They should have been combined in the env when parsed.
2437 if(!delineate_this_header(tmp, current+1, &dummystart,
2438 &dummyfinish))
2439 format_env_hdr(stream, msgno, section, env,
2440 fmt_env, tmp_pc, tmp, hdrs->charset, flags);
2442 else{
2443 if((rv = format_raw_hdr_string(start, finish, tmp_pc,
2444 hdrs->charset, flags)) != 0)
2445 goto write_error;
2446 else
2447 start = finish;
2451 /* custom view or default */
2452 else{
2453 /* fetch the non-envelope headers */
2454 if(nfields)
2455 h = pine_fetchheader_lines(stream, msgno, section, fields);
2457 /* default envelope for default view */
2458 if(hdrs->type == HD_BFIELD)
2459 (*fmt_env)(stream, msgno, section, env, tmp_pc, hdrs->h.b, hdrs->charset, flags);
2461 /* go through each header in list, v initialized above */
2462 for(; (q = *v) != NULL; v++){
2463 if(is_an_env_hdr(q)){
2464 /* pretty format for env hdrs */
2465 format_env_hdr(stream, msgno, section, env,
2466 fmt_env, tmp_pc, q, hdrs->charset, flags);
2468 else{
2470 * Go through h finding all occurences of this header
2471 * and all continuation lines, and output.
2473 for(current = h;
2474 h && delineate_this_header(q,current,&start,&finish);
2475 current = finish){
2476 if((rv = format_raw_hdr_string(start, finish, tmp_pc,
2477 hdrs->charset, flags)) != 0)
2478 goto write_error;
2479 else
2480 start = finish;
2488 write_error:
2490 gf_clear_so_writec(tmp_store);
2492 if(!rv){ /* valid data? Do wrapping and filtering... */
2493 int column;
2494 char *errstr, *display_filter = NULL, trigger[MAILTMPLEN];
2495 STORE_S *df_store = NULL;
2497 so_seek(tmp_store, 0L, 0);
2499 column = (flags & FM_DISPLAY) ? ps_global->ttyo->screen_cols : 80;
2502 * Test for and act on any display filter
2503 * This barely makes sense. The display filter is going
2504 * to be getting UTF-8'ized headers here. In pre-alpine
2505 * pine the display filter was being fed already decoded
2506 * headers in whatever character set they were in.
2507 * The good news is that that didn't make much
2508 * sense either, so this shouldn't break anything.
2509 * It seems unlikely that anybody is doing anything useful
2510 * with the header part of display filters.
2512 if(ps_global->tools.display_filter
2513 && ps_global->tools.display_filter_trigger
2514 && (display_filter = (*ps_global->tools.display_filter_trigger)(NULL, trigger, sizeof(trigger)))){
2515 if((df_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
2517 gf_set_so_writec(&tmp_pc, df_store);
2518 gf_set_so_readc(&tmp_gc, df_store);
2519 if((errstr = (*ps_global->tools.display_filter)(display_filter, tmp_store, tmp_pc, NULL)) != NULL){
2520 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2521 _("Formatting error: %s"), errstr);
2522 rv = FHT_WRTERR;
2524 else
2525 so_seek(df_store, 0L, 0);
2527 gf_clear_so_writec(df_store);
2529 else{
2530 q_status_message(SM_ORDER | SM_DING, 3, 3,
2531 "No space for filtered text.");
2532 rv = FHT_WRTERR;
2535 else{
2536 so_seek(tmp_store, 0L, 0);
2537 gf_set_so_readc(&tmp_gc, tmp_store);
2540 if(!rv){
2541 int *margin, wrapflags = GFW_ONCOMMA;
2543 gf_filter_init();
2544 gf_link_filter(gf_local_nvtnl, NULL);
2546 if((F_ON(F_VIEW_SEL_URL, ps_global)
2547 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
2548 || F_ON(F_SCAN_ADDR, ps_global))
2549 && handlesp){
2550 gf_link_filter(gf_line_test,
2551 gf_line_test_opt(url_hilite_hdr,
2552 gf_url_hilite_opt(&uh,handlesp,1)));
2553 wrapflags |= GFW_HANDLES;
2556 if((flags & FM_DISPLAY)
2557 && !(flags & FM_NOCOLOR)
2558 && pico_usingcolor()
2559 && ((VAR_NORM_FORE_COLOR
2560 && VAR_HEADER_GENERAL_FORE_COLOR
2561 && colorcmp(VAR_NORM_FORE_COLOR, VAR_HEADER_GENERAL_FORE_COLOR))
2563 (VAR_NORM_BACK_COLOR
2564 && VAR_HEADER_GENERAL_BACK_COLOR
2565 && colorcmp(VAR_NORM_BACK_COLOR, VAR_HEADER_GENERAL_BACK_COLOR))))
2566 wrapflags |= GFW_HDRCOLOR;
2568 if((flags & FM_DISPLAY)
2569 && !(flags & FM_NOCOLOR)
2570 && pico_usingcolor()
2571 && ps_global->hdr_colors){
2572 gf_link_filter(gf_line_test,
2573 gf_line_test_opt(color_headers, NULL));
2574 wrapflags |= (GFW_HANDLES | GFW_HDRCOLOR);
2577 if(prefix && *prefix)
2578 column = MAX(column-strlen(prefix), 50);
2580 margin = format_view_margin();
2582 if(!(flags & FM_NOWRAP))
2583 gf_link_filter(gf_wrap,
2584 gf_wrap_filter_opt(column, column,
2585 (flags & FM_NOINDENT) ? NULL : margin,
2586 4, wrapflags));
2588 if(prefix && *prefix)
2589 gf_link_filter(gf_prefix, gf_prefix_opt(prefix));
2591 if((flags & FM_DISPLAY)
2592 && !(flags & FM_NOCOLOR)
2593 && pico_usingcolor()
2594 && ((VAR_NORM_FORE_COLOR
2595 && VAR_HEADER_GENERAL_FORE_COLOR
2596 && colorcmp(VAR_NORM_FORE_COLOR, VAR_HEADER_GENERAL_FORE_COLOR))
2598 (VAR_NORM_BACK_COLOR
2599 && VAR_HEADER_GENERAL_BACK_COLOR
2600 && colorcmp(VAR_NORM_BACK_COLOR, VAR_HEADER_GENERAL_BACK_COLOR)))){
2601 int right_margin;
2602 int total_wid;
2604 right_margin = margin ? margin[1] : 0;
2605 total_wid = column - right_margin;
2607 gf_link_filter(gf_line_test,
2608 gf_line_test_opt(pad_to_right_edge, (void *) &total_wid));
2609 wrapflags |= GFW_HANDLES;
2612 gf_link_filter(gf_nvtnl_local, NULL);
2614 if((errstr = gf_pipe(tmp_gc, final_pc)) != NULL){
2615 rv = FHT_WRTERR;
2616 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2617 "Can't build header : %.200s", errstr);
2621 if(df_store){
2622 gf_clear_so_readc(df_store);
2623 so_give(&df_store);
2625 else
2626 gf_clear_so_readc(tmp_store);
2629 so_give(&tmp_store);
2631 if(h)
2632 fs_give((void **)&h);
2634 if(fields)
2635 fs_give((void **)&fields);
2637 return(rv);
2641 /*----------------------------------------------------------------------
2642 Format RAW header text for display
2644 Args: stream -- mail stream for various header text fetches
2645 rawno -- sequence number in stream of message we're interested in
2646 section -- which section of message
2647 pc -- function to write header text with
2649 Result: 0 if all's well, -1 if write error, 1 if fetch error
2651 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2652 in the local convention.
2654 ----*/
2656 format_raw_header(MAILSTREAM *stream, long int msgno, char *section, gf_io_t pc)
2658 char *h = mail_fetch_header(stream, msgno, section, NULL, NULL, FT_PEEK);
2659 unsigned char c;
2661 if(h){
2662 while(*h){
2663 if(ISRFCEOL(h)){
2664 h += 2;
2665 if(!gf_puts(NEWLINE, pc))
2666 return(FHT_WRTERR);
2668 if(ISRFCEOL(h)) /* all done! */
2669 return(FHT_OK);
2671 else if((unsigned char)(*h) < 0x80 && FILTER_THIS(*h) &&
2672 !(*(h+1) && *h == ESCAPE && match_escapes(h+1))){
2673 c = (unsigned char) *h++;
2674 if(!((*pc)(c >= 0x80 ? '~' : '^')
2675 && (*pc)((c == 0x7f) ? '?' : (c & 0x1f) + '@')))
2676 return(FHT_WRTERR);
2678 else if(!(*pc)(*h++))
2679 return(FHT_WRTERR);
2682 else
2683 return(FHT_FTCHERR);
2685 return(FHT_OK);
2690 /*----------------------------------------------------------------------
2691 Format c-client envelope data suitable for display
2693 Args: s -- mail stream for various header text fetches
2694 n -- raw sequence number in stream of message we're interested in
2695 sect -- which section of message
2696 e -- pointer to msg's envelope
2697 pc -- function to write header text with
2698 which -- which header lines to write
2699 oacs --
2700 flags -- FM_ flags, see pith/mailview.h
2702 Result: 0 if all's well, -1 if write error, 1 if fetch error
2704 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2705 in the local convention.
2707 ----*/
2708 void
2709 format_envelope(MAILSTREAM *s, long int n, char *sect, ENVELOPE *e, gf_io_t pc,
2710 long int which, char *oacs, int flags)
2712 char *q, *p2, buftmp[MAILTMPLEN];
2714 if(!e)
2715 return;
2717 if((which & FE_DATE) && e->date) {
2718 q = "Date: ";
2719 snprintf(buftmp, sizeof(buftmp), "%s",
2720 F_ON(F_DATES_TO_LOCAL,ps_global)
2721 ? convert_date_to_local((char *) e->date) : (char *) e->date);
2722 buftmp[sizeof(buftmp)-1] = '\0';
2723 p2 = (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
2724 SIZEOF_20KBUF, buftmp);
2725 gf_puts(q, pc);
2726 format_env_puts(p2, pc);
2727 gf_puts(NEWLINE, pc);
2730 if((which & FE_FROM) && e->from)
2731 format_addr_string(s, n, sect, "From: ", e->from, flags, oacs, pc);
2733 if((which & FE_REPLYTO) && e->reply_to
2734 && (!e->from || !address_is_same(e->reply_to, e->from)))
2735 format_addr_string(s, n, sect, "Reply-To: ", e->reply_to, flags, oacs, pc);
2737 if((which & FE_TO) && e->to)
2738 format_addr_string(s, n, sect, "To: ", e->to, flags, oacs, pc);
2740 if((which & FE_CC) && e->cc)
2741 format_addr_string(s, n, sect, "Cc: ", e->cc, flags, oacs, pc);
2743 if((which & FE_BCC) && e->bcc)
2744 format_addr_string(s, n, sect, "Bcc: ", e->bcc, flags, oacs, pc);
2746 if((which & FE_RETURNPATH) && e->return_path)
2747 format_addr_string(s, n, sect, "Return-Path: ", e->return_path,
2748 flags, oacs, pc);
2750 if((which & FE_NEWSGROUPS) && e->newsgroups){
2751 int bogus = NIL;
2752 format_newsgroup_string("Newsgroups: ", e->newsgroups, flags, pc);
2753 if (!e->ngpathexists && e->message_id &&
2754 strncmp (e->message_id,"<alpine.",8) &&
2755 strncmp (e->message_id,"<Pine.",6) &&
2756 strncmp (e->message_id,"<MS-C.",6) &&
2757 strncmp (e->message_id,"<MailManager.",13) &&
2758 strncmp (e->message_id,"<EasyMail.",11) &&
2759 strncmp (e->message_id,"<ML-",4)) bogus = T;
2761 if(bogus)
2762 q_status_message(SM_ORDER, 0, 3,
2763 "Unverified Newsgroup header -- Message MAY or MAY NOT have been posted");
2766 if((which & FE_FOLLOWUPTO) && e->followup_to)
2767 format_newsgroup_string("Followup-To: ", e->followup_to, flags, pc);
2769 if((which & FE_SUBJECT) && e->subject && e->subject[0]){
2770 char *freeme = NULL;
2772 q = "Subject: ";
2773 gf_puts(q, pc);
2775 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->subject), SIZEOF_20KBUF-10000);
2777 if(flags & FM_DISPLAY
2778 && (ps_global->display_keywords_in_subject
2779 || ps_global->display_keywordinits_in_subject)){
2781 /* don't bother if no keywords are defined */
2782 if(some_user_flags_defined(s))
2783 p2 = freeme = prepend_keyword_subject(s, n, p2,
2784 ps_global->display_keywords_in_subject ? KW : KWInit,
2785 NULL, ps_global->VAR_KW_BRACES);
2788 format_env_puts(p2, pc);
2790 if(freeme)
2791 fs_give((void **) &freeme);
2793 gf_puts(NEWLINE, pc);
2796 if((which & FE_SENDER) && e->sender
2797 && (!e->from || !address_is_same(e->sender, e->from)))
2798 format_addr_string(s, n, sect, "Sender: ", e->sender, flags, oacs, pc);
2800 if((which & FE_MESSAGEID) && e->message_id){
2801 q = "Message-ID: ";
2802 gf_puts(q, pc);
2803 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->message_id), SIZEOF_20KBUF-10000);
2804 format_env_puts(p2, pc);
2805 gf_puts(NEWLINE, pc);
2808 if((which & FE_INREPLYTO) && e->in_reply_to){
2809 q = "In-Reply-To: ";
2810 gf_puts(q, pc);
2811 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);
2812 format_env_puts(p2, pc);
2813 gf_puts(NEWLINE, pc);
2816 if((which & FE_REFERENCES) && e->references) {
2817 q = "References: ";
2818 gf_puts(q, pc);
2819 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->references), SIZEOF_20KBUF-10000);
2820 format_env_puts(p2, pc);
2821 gf_puts(NEWLINE, pc);
2829 * The argument fieldname is something like "Subject:..." or "Subject".
2830 * Look through the specs in speccolor for a match of the fieldname,
2831 * and return the color that goes with any match, or NULL.
2832 * Caller should free the color.
2834 COLOR_PAIR *
2835 hdr_color(char *fieldname, char *value, SPEC_COLOR_S *speccolor)
2837 SPEC_COLOR_S *hc = NULL;
2838 COLOR_PAIR *color_pair = NULL;
2839 char *colon, *fname;
2840 char fbuf[FBUF_LEN+1];
2841 int gotit;
2842 PATTERN_S *pat;
2844 colon = strindex(fieldname, ':');
2845 if(colon){
2846 strncpy(fbuf, fieldname, MIN(colon-fieldname,FBUF_LEN));
2847 fbuf[MIN(colon-fieldname,FBUF_LEN)] = '\0';
2848 fname = fbuf;
2850 else
2851 fname = fieldname;
2853 if(fname && *fname)
2854 for(hc = speccolor; hc; hc = hc->next)
2855 if(hc->spec && !strucmp(fname, hc->spec)){
2856 if(!hc->val)
2857 break;
2859 gotit = 0;
2860 for(pat = hc->val; !gotit && pat; pat = pat->next)
2861 if(srchstr(value, pat->substring))
2862 gotit++;
2864 if(gotit)
2865 break;
2868 if(hc && hc->fg && hc->fg[0] && hc->bg && hc->bg[0])
2869 color_pair = new_color_pair(hc->fg, hc->bg);
2871 return(color_pair);
2876 * The argument fieldname is something like "Subject:..." or "Subject".
2877 * Look through the specs in hdr_colors for a match of the fieldname,
2878 * and return 1 if that fieldname is in one of the patterns, 0 otherwise.
2881 any_hdr_color(char *fieldname)
2883 SPEC_COLOR_S *hc = NULL;
2884 char *colon, *fname;
2885 char fbuf[FBUF_LEN+1];
2887 colon = strindex(fieldname, ':');
2888 if(colon){
2889 strncpy(fbuf, fieldname, MIN(colon-fieldname,FBUF_LEN));
2890 fbuf[MIN(colon-fieldname,FBUF_LEN)] = '\0';
2891 fname = fbuf;
2893 else
2894 fname = fieldname;
2896 if(fname && *fname)
2897 for(hc = ps_global->hdr_colors; hc; hc = hc->next)
2898 if(hc->spec && !strucmp(fname, hc->spec))
2899 break;
2901 return(hc ? 1 : 0);
2905 /*----------------------------------------------------------------------
2906 Format an address field, wrapping lines nicely at commas
2908 Args: field_name -- The name of the field we're formatting ("TO: ", ...)
2909 addr -- ADDRESS structure to format
2911 Result: A formatted, malloced string is returned.
2912 ----------------------------------------------------------------------*/
2913 void
2914 format_addr_string(MAILSTREAM *stream, long int msgno, char *section, char *field_name,
2915 struct mail_address *addr, int flags, char *oacs, gf_io_t pc)
2917 char *ptmp, *mtmp;
2918 int trailing = 0, group = 0;
2919 ADDRESS *atmp;
2921 if(!addr)
2922 return;
2925 * quickly run down address list to make sure none are patently bogus.
2926 * If so, just blat raw field out.
2928 for(atmp = addr; stream && atmp; atmp = atmp->next)
2929 if(atmp->host && atmp->host[0] == '.'){
2930 char *field, *fields[2];
2932 fields[1] = NULL;
2933 fields[0] = cpystr(field_name);
2934 if((ptmp = strchr(fields[0], ':')) != NULL)
2935 *ptmp = '\0';
2937 if((field = pine_fetchheader_lines(stream, msgno, section, fields)) != NULL){
2938 char *h, *t;
2940 for(t = h = field; *h ; t++)
2941 if(*t == '\015' && *(t+1) == '\012'){
2942 *t = '\0'; /* tie off line */
2943 format_env_puts(h, pc);
2944 if(*(h = (++t) + 1)) /* set new h and skip CRLF */
2945 gf_puts(NEWLINE, pc); /* more to write */
2946 else
2947 break;
2949 else if(!*t){ /* shouldn't happen much */
2950 if(h != t)
2951 format_env_puts(h, pc);
2953 break;
2956 fs_give((void **)&field);
2959 fs_give((void **)&fields[0]);
2960 gf_puts(NEWLINE, pc);
2961 dprint((2, "Error in \"%s\" field address\n",
2962 field_name ? field_name : "?"));
2963 return;
2966 gf_puts(field_name, pc);
2968 while(addr){
2969 atmp = addr->next; /* remember what's next */
2970 addr->next = NULL;
2971 if(!addr->host && addr->mailbox){
2972 mtmp = addr->mailbox;
2973 addr->mailbox = cpystr((char *)rfc1522_decode_to_utf8(
2974 (unsigned char *)tmp_20k_buf,
2975 SIZEOF_20KBUF, addr->mailbox));
2978 ptmp = addr->personal; /* RFC 1522 personal name? */
2979 addr->personal = iutf8ncpy((char *)tmp_20k_buf, (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+10000), SIZEOF_20KBUF-10000, addr->personal), 10000);
2980 tmp_20k_buf[10000-1] = '\0';
2982 if(!trailing) /* 1st pass, just address */
2983 trailing++;
2984 else{ /* else comma, unless */
2985 if(!((group == 1 && addr->host) /* 1st addr in group, */
2986 || (!addr->host && !addr->mailbox))){ /* or end of group */
2987 gf_puts(",", pc);
2988 #if 0
2989 gf_puts(NEWLINE, pc); /* ONE address/line please */
2990 gf_puts(" ", pc);
2991 #endif
2994 gf_puts(" ", pc);
2997 pine_rfc822_write_address_noquote(addr, pc, &group);
2999 addr->personal = ptmp; /* restore old personal ptr */
3000 if(!addr->host && addr->mailbox){
3001 fs_give((void **)&addr->mailbox);
3002 addr->mailbox = mtmp;
3005 addr->next = atmp;
3006 addr = atmp;
3009 gf_puts(NEWLINE, pc);
3015 const char *rspecials_minus_quote_and_dot = "()<>@,;:\\[]";
3016 /* RFC822 continuation, must start with CRLF */
3017 #define RFC822CONT "\015\012 "
3019 /* Write RFC822 address with some quoting turned off.
3020 * Accepts:
3021 * address to interpret
3023 * (This is a copy of c-client's rfc822_write_address except
3024 * we don't quote double quote and dot in personal names. It writes
3025 * to a gf_io_t instead of to a buffer so that we don't have to worry
3026 * about fixed sized buffer overflowing. It's also special cased to deal
3027 * with only a single address.)
3029 * The idea is that there are some places where we'd just like to display
3030 * the personal name as is before applying confusing quoting. However,
3031 * we do want to be careful not to break things that should be quoted so
3032 * we'll only use this where we are sure. Quoting may look ugly but it
3033 * doesn't usually break anything.
3035 void
3036 pine_rfc822_write_address_noquote(struct mail_address *adr, gf_io_t pc, int *group)
3038 extern const char *rspecials;
3040 if (adr->host) { /* ordinary address? */
3041 if (!(adr->personal || adr->adl)) pine_rfc822_address (adr, pc);
3042 else { /* no, must use phrase <route-addr> form */
3043 if (adr->personal)
3044 pine_rfc822_cat (adr->personal, rspecials_minus_quote_and_dot, pc);
3046 gf_puts(" <", pc); /* write address delimiter */
3047 pine_rfc822_address(adr, pc);
3048 gf_puts (">", pc); /* closing delimiter */
3051 if(*group)
3052 (*group)++;
3054 else if (adr->mailbox) { /* start of group? */
3055 /* yes, write group name */
3056 pine_rfc822_cat (adr->mailbox, rspecials, pc);
3058 gf_puts (": ", pc); /* write group identifier */
3059 *group = 1; /* in a group */
3061 else if (*group) { /* must be end of group (but be paranoid) */
3062 gf_puts (";", pc);
3063 *group = 0; /* no longer in that group */
3068 /* Write RFC822 route-address to string
3069 * Accepts:
3070 * address to interpret
3073 void
3074 pine_rfc822_address(struct mail_address *adr, gf_io_t pc)
3076 extern char *wspecials;
3078 if (adr && adr->host) { /* no-op if no address */
3079 if (adr->adl) { /* have an A-D-L? */
3080 gf_puts (adr->adl, pc);
3081 gf_puts (":", pc);
3083 /* write mailbox name */
3084 pine_rfc822_cat (adr->mailbox, wspecials, pc);
3085 if (*adr->host != '@') { /* unless null host (HIGHLY discouraged!) */
3086 gf_puts ("@", pc); /* host delimiter */
3087 gf_puts (adr->host, pc); /* write host name */
3093 /* Concatenate RFC822 string
3094 * Accepts:
3095 * pointer to string to concatenate
3096 * list of special characters
3099 void
3100 pine_rfc822_cat(char *src, const char *specials, gf_io_t pc)
3102 char *s;
3104 if (strpbrk (src,specials)) { /* any specials present? */
3105 gf_puts ("\"", pc); /* opening quote */
3106 /* truly bizarre characters in there? */
3107 while ((s = strpbrk (src,"\\\"")) != NULL) {
3108 char save[2];
3110 /* turn it into a null-terminated piece */
3111 save[0] = *s;
3112 save[1] = '\0';
3113 *s = '\0';
3114 gf_puts (src, pc); /* yes, output leader */
3115 *s = save[0];
3116 gf_puts ("\\", pc); /* quoting */
3117 gf_puts (save, pc); /* output the bizarre character */
3118 src = ++s; /* continue after the bizarre character */
3120 if (*src) gf_puts (src, pc);/* output non-bizarre string */
3121 gf_puts ("\"", pc); /* closing quote */
3123 else gf_puts (src, pc); /* otherwise it's the easy case */
3127 /*----------------------------------------------------------------------
3128 Format an address field, wrapping lines nicely at commas
3130 Args: field_name -- The name of the field we're formatting ("TO:", Cc:...)
3131 newsgrps -- ADDRESS structure to format
3133 Result: A formatted, malloced string is returned.
3135 The resuling lines formatted are 80 columns wide.
3136 ----------------------------------------------------------------------*/
3137 void
3138 format_newsgroup_string(char *field_name, char *newsgrps, int flags, gf_io_t pc)
3140 char buf[MAILTMPLEN];
3141 int trailing = 0, llen, alen;
3142 char *next_ng;
3144 if(!newsgrps || !*newsgrps)
3145 return;
3147 gf_puts(field_name, pc);
3149 llen = strlen(field_name);
3150 while(*newsgrps){
3151 for(next_ng = newsgrps; *next_ng && *next_ng != ','; next_ng++);
3152 strncpy(buf, newsgrps, MIN(next_ng - newsgrps, sizeof(buf)-1));
3153 buf[MIN(next_ng - newsgrps, sizeof(buf)-1)] = '\0';
3154 newsgrps = next_ng;
3155 if(*newsgrps)
3156 newsgrps++;
3157 alen = strlen(buf);
3158 if(!trailing){ /* first time thru, just address */
3159 llen += alen;
3160 trailing++;
3162 else{ /* else preceding comma */
3163 gf_puts(",", pc);
3164 llen++;
3166 if(alen + llen + 1 > 76){
3167 gf_puts(NEWLINE, pc);
3168 gf_puts(" ", pc);
3169 llen = alen + 5;
3171 else{
3172 gf_puts(" ", pc);
3173 llen += alen + 1;
3177 if(alen && llen > 76){ /* handle long addresses */
3178 register char *q, *p = &buf[alen-1];
3180 while(p > buf){
3181 if(isspace((unsigned char)*p)
3182 && (llen - (alen - (int)(p - buf))) < 76){
3183 for(q = buf; q < p; q++)
3184 (*pc)(*q); /* write character */
3186 gf_puts(NEWLINE, pc);
3187 gf_puts(" ", pc);
3188 gf_puts(p, pc);
3189 break;
3191 else
3192 p--;
3195 if(p == buf) /* no reasonable break point */
3196 gf_puts(buf, pc);
3198 else
3199 gf_puts(buf, pc);
3202 gf_puts(NEWLINE, pc);
3207 /*----------------------------------------------------------------------
3208 Format a text field that's part of some raw (non-envelope) message header
3210 Args: start --
3211 finish --
3212 pc --
3214 Result: Semi-digested text (RFC 1522 decoded, anyway) written with "pc"
3216 ----------------------------------------------------------------------*/
3218 format_raw_hdr_string(char *start, char *finish, gf_io_t pc, char *oacs, int flags)
3220 register char *current;
3221 unsigned char *p, *tmp = NULL, c;
3222 size_t n, len;
3223 char ch;
3224 int rv = FHT_OK;
3226 ch = *finish;
3227 *finish = '\0';
3229 if((n = 4*(finish-start)) > SIZEOF_20KBUF-1){
3230 len = n+1;
3231 p = tmp = (unsigned char *) fs_get(len * sizeof(unsigned char));
3233 else{
3234 len = SIZEOF_20KBUF;
3235 p = (unsigned char *) tmp_20k_buf;
3238 if(islower((unsigned char)(*start)))
3239 *start = toupper((unsigned char)(*start));
3241 current = (char *) rfc1522_decode_to_utf8(p, len, start);
3243 /* output from start to finish */
3244 while(*current && rv == FHT_OK)
3245 if(ISRFCEOL(current)){
3246 if(!gf_puts(NEWLINE, pc))
3247 rv = FHT_WRTERR;
3249 current += 2;
3251 else if((unsigned char)(*current) < 0x80 && FILTER_THIS(*current) &&
3252 !(*(current+1) && *current == ESCAPE && match_escapes(current+1))){
3253 c = (unsigned char) *current++;
3254 if(!((*pc)(c >= 0x80 ? '~' : '^')
3255 && (*pc)((c == 0x7f) ? '?' : (c & 0x1f) + '@')))
3256 rv = FHT_WRTERR;
3258 else if(!(*pc)(*current++))
3259 rv = FHT_WRTERR;
3261 if(tmp)
3262 fs_give((void **) &tmp);
3264 *finish = ch;
3266 return(rv);
3272 /*----------------------------------------------------------------------
3273 Format a text field that's part of some raw (non-envelope) message header
3275 Args: s --
3276 pc --
3278 Result: Output
3280 ----------------------------------------------------------------------*/
3282 format_env_puts(char *s, gf_io_t pc)
3284 if(ps_global->pass_ctrl_chars)
3285 return(gf_puts(s, pc));
3287 for(; *s; s++)
3288 if((unsigned char)(*s) < 0x80 && FILTER_THIS(*s) && !(*(s+1) && *s == ESCAPE && match_escapes(s+1))){
3289 if(!((*pc)((unsigned char) (*s) >= 0x80 ? '~' : '^')
3290 && (*pc)((*s == 0x7f) ? '?' : (*s & 0x1f) + '@')))
3291 return(0);
3293 else if(!(*pc)(*s))
3294 return(0);
3296 return(1);
3300 char *
3301 display_parameters(PARAMETER *params)
3303 int n, longest = 0;
3304 char *d, *printme;
3305 PARAMETER *p;
3306 PARMLIST_S *parmlist;
3308 for(p = params; p; p = p->next) /* ok if we include *'s */
3309 if(p->attribute && (n = strlen(p->attribute)) > longest)
3310 longest = MIN(32, n); /* shouldn't be any bigger than 32 */
3312 d = tmp_20k_buf;
3313 tmp_20k_buf[0] = '\0';
3314 if((parmlist = rfc2231_newparmlist(params)) != NULL){
3315 n = 0; /* n overloaded */
3316 while(rfc2231_list_params(parmlist) && d < tmp_20k_buf + 10000){
3317 if(n++){
3318 snprintf(d, 10000-(d-tmp_20k_buf), "\n");
3319 tmp_20k_buf[10000-1] = '\0';
3320 d += strlen(d);
3323 if(parmlist->value){
3324 if(parmlist->attrib && strucmp(parmlist->attrib, "url") == 0){
3325 snprintf(printme = tmp_20k_buf + 11000, 1000, "%s", parmlist->value);
3326 sqzspaces(printme);
3328 else
3329 printme = strsquish(tmp_20k_buf + 11000, 1000, parmlist->value, 100);
3331 else
3332 printme = "";
3334 snprintf(d, 10000-(d-tmp_20k_buf), "%-*s: %s", longest,
3335 parmlist->attrib ? parmlist->attrib : "", printme);
3337 tmp_20k_buf[10000-1] = '\0';
3338 d += strlen(d);
3341 rfc2231_free_parmlist(&parmlist);
3344 return(tmp_20k_buf);
3348 /*----------------------------------------------------------------------
3349 Fetch the requested header fields from the msgno specified
3351 Args: stream -- mail stream of open folder
3352 msgno -- number of message to get header lines from
3353 fields -- array of pointers to desired fields
3355 Returns: allocated string containing matched header lines,
3356 NULL on error.
3357 ----*/
3358 char *
3359 pine_fetch_header(MAILSTREAM *stream, long int msgno, char *section, char **fields, long int flags)
3361 STRINGLIST *sl;
3362 char *p, *m, *h = NULL, *match = NULL, *free_this, tmp[MAILTMPLEN];
3363 char **pflds = NULL, **pp = NULL, **qq;
3366 * If the user misconfigures it is possible to have one of the fields
3367 * set to the empty string instead of a header name. We want to catch
3368 * that here instead of asking the server the nonsensical question.
3370 for(pp = fields ? &fields[0] : NULL; pp && *pp; pp++)
3371 if(!**pp)
3372 break;
3374 if(pp && *pp){ /* found an empty header field, fix it */
3375 pflds = copy_list_array(fields);
3376 for(pp = pflds; pp && *pp; pp++){
3377 if(!**pp){ /* scoot rest of the lines up */
3378 free_this = *pp;
3379 for(qq = pp; *qq; qq++)
3380 *qq = *(qq+1);
3382 if(free_this)
3383 fs_give((void **) &free_this);
3387 /* no headers to look for, return NULL */
3388 if(pflds && !*pflds && !(flags & FT_NOT)){
3389 free_list_array(&pflds);
3390 return(NULL);
3393 else
3394 pflds = fields;
3396 sl = (pflds && *pflds) ? new_strlst(pflds) : NULL; /* package up fields */
3397 h = mail_fetch_header(stream, msgno, section, sl, NULL, flags | FT_PEEK);
3398 if (sl)
3399 free_strlst(&sl);
3401 if(!h){
3402 if(pflds && pflds != fields)
3403 free_list_array(&pflds);
3405 return(NULL);
3408 while(find_field(&h, tmp, sizeof(tmp))){
3409 for(pp = &pflds[0]; *pp && strucmp(tmp, *pp); pp++)
3412 /* interesting field? */
3413 if((p = (flags & FT_NOT) ? ((*pp) ? NULL : tmp) : *pp) != NULL){
3415 * Hold off allocating space for matching fields until
3416 * we at least find one to copy...
3418 if(!match)
3419 match = m = fs_get(strlen(h) + strlen(p) + 1);
3421 while(*p) /* copy field name */
3422 *m++ = *p++;
3424 while(*h && (*m++ = *h++)) /* header includes colon */
3425 if(*(m-1) == '\n' && (*h == '\r' || !isspace((unsigned char)*h)))
3426 break;
3428 *m = '\0'; /* tie off match string */
3430 else{ /* no match, pass this field */
3431 while(*h && !(*h++ == '\n'
3432 && (*h == '\r' || !isspace((unsigned char)*h))))
3437 if(pflds && pflds != fields)
3438 free_list_array(&pflds);
3440 return(match ? match : cpystr(""));
3445 find_field(char **h, char *tmp, size_t ntmp)
3447 char *otmp = tmp;
3449 if(!h || !*h || !**h || isspace((unsigned char)**h))
3450 return(0);
3452 while(tmp-otmp<ntmp-1 && **h && **h != ':' && !isspace((unsigned char)**h))
3453 *tmp++ = *(*h)++;
3455 *tmp = '\0';
3456 return(1);
3460 static char *_last_embedded_fg_color, *_last_embedded_bg_color;
3464 embed_color(COLOR_PAIR *cp, gf_io_t pc)
3466 if(cp && cp->fg){
3467 if(_last_embedded_fg_color)
3468 fs_give((void **)&_last_embedded_fg_color);
3470 _last_embedded_fg_color = cpystr(cp->fg);
3472 if(!(pc && (*pc)(TAG_EMBED) && (*pc)(TAG_FGCOLOR) &&
3473 gf_puts(color_to_asciirgb(cp->fg), pc)))
3474 return 0;
3477 if(cp && cp->bg){
3478 if(_last_embedded_bg_color)
3479 fs_give((void **)&_last_embedded_bg_color);
3481 _last_embedded_bg_color = cpystr(cp->bg);
3483 if(!(pc && (*pc)(TAG_EMBED) && (*pc)(TAG_BGCOLOR) &&
3484 gf_puts(color_to_asciirgb(cp->bg), pc)))
3485 return 0;
3488 return 1;
3492 COLOR_PAIR *
3493 get_cur_embedded_color(void)
3495 COLOR_PAIR *ret;
3497 if(_last_embedded_fg_color && _last_embedded_bg_color)
3498 ret = new_color_pair(_last_embedded_fg_color, _last_embedded_bg_color);
3499 else
3500 ret = pico_get_cur_color();
3502 return(ret);
3506 void
3507 clear_cur_embedded_color(void)
3509 if(_last_embedded_fg_color)
3510 fs_give((void **)&_last_embedded_fg_color);
3512 if(_last_embedded_bg_color)
3513 fs_give((void **)&_last_embedded_bg_color);
3518 scroll_handle_start_color(char *colorstring, size_t buflen, int *len)
3520 *len = 0;
3522 if(pico_usingcolor()){
3523 char *fg = NULL, *bg = NULL, *s;
3525 if(ps_global->VAR_SLCTBL_FORE_COLOR
3526 && colorcmp(ps_global->VAR_SLCTBL_FORE_COLOR,
3527 ps_global->VAR_NORM_FORE_COLOR))
3528 fg = ps_global->VAR_SLCTBL_FORE_COLOR;
3530 if(ps_global->VAR_SLCTBL_BACK_COLOR
3531 && colorcmp(ps_global->VAR_SLCTBL_BACK_COLOR,
3532 ps_global->VAR_NORM_BACK_COLOR))
3533 bg = ps_global->VAR_SLCTBL_BACK_COLOR;
3535 if(bg || fg){
3536 COLOR_PAIR *tmp;
3539 * The blacks are just known good colors for
3540 * testing whether the other color is good.
3542 if((tmp = new_color_pair(fg ? fg : colorx(COL_BLACK),
3543 bg ? bg : colorx(COL_BLACK))) != NULL){
3544 if(pico_is_good_colorpair(tmp))
3545 for(s = color_embed(fg, bg);
3546 (*len) < buflen && (colorstring[*len] = *s);
3547 s++, (*len)++)
3550 free_color_pair(&tmp);
3554 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
3555 strncpy(colorstring + (*len), url_embed(TAG_BOLDON), MIN(3,buflen-(*len)));
3556 *len += 2;
3560 colorstring[buflen-1] = '\0';
3562 return(*len != 0);
3567 scroll_handle_end_color(char *colorstring, size_t buflen, int *len, int use_hdr_color)
3569 *len = 0;
3570 if(pico_usingcolor()){
3571 char *fg = NULL, *bg = NULL, *s;
3572 char *basefg = NULL, *basebg = NULL;
3574 basefg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_FORE_COLOR
3575 : ps_global->VAR_NORM_FORE_COLOR;
3576 basebg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_BACK_COLOR
3577 : ps_global->VAR_NORM_BACK_COLOR;
3580 * We need to change the fg and bg colors back even if they
3581 * are the same as the Normal Colors so that color_a_quote
3582 * will have a chance to pick up those colors as the ones to
3583 * switch to. We don't do this before the handle above so that
3584 * the quote color will flow into the selectable item when
3585 * the selectable item color is partly the same as the
3586 * normal color. That is, suppose the normal color was black on
3587 * cyan and the selectable color was blue on cyan, only a fg color
3588 * change. We preserve the only-a-fg-color-change in a quote by
3589 * letting the quote background color flow into the selectable text.
3591 if(ps_global->VAR_SLCTBL_FORE_COLOR)
3592 fg = basefg;
3594 if(ps_global->VAR_SLCTBL_BACK_COLOR)
3595 bg = basebg;
3597 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
3598 strncpy(colorstring, url_embed(TAG_BOLDOFF), MIN(3,buflen));
3599 *len = 2;
3602 if(fg || bg)
3603 for(s = color_embed(fg, bg); (*len) < buflen && (colorstring[*len] = *s); s++, (*len)++)
3607 colorstring[buflen-1] = '\0';
3609 return(*len != 0);
3614 * Helper routine that is of limited use.
3615 * We need to tally up the screen width of
3616 * a UTF-8 string as we go through the string.
3617 * We just want the width of the character starting
3618 * at str (and no longer than remaining_octets).
3619 * If we're plopped into the middle of a UTF-8
3620 * character we just want to return width zero.
3623 width_at_this_position(unsigned char *str, unsigned long n)
3625 unsigned char *inputp = str;
3626 unsigned long remaining_octets = n;
3627 UCS ucs;
3628 int width = 0;
3630 ucs = (UCS) utf8_get(&inputp, &remaining_octets);
3631 if(!(ucs & U8G_ERROR || ucs == UBOGON)){
3632 width = wcellwidth(ucs);
3633 /* Writechar will print a '?' */
3634 if(width < 0)
3635 width = 1;
3638 return(width);