* For mailing lists, Alpine adds a description of the type of link
[alpine.git] / pith / mailview.c
bloba37633294b72c286b30359ee47b026f12dfeae46
1 /*
2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2009 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 /*======================================================================
17 mailview.c
18 Implements message data gathering and formatting
20 ====*/
23 #include "headers.h"
24 #include "../pith/ical.h"
25 #include "../pith/body.h"
26 #include "../pith/mailpart.h"
27 #include "../pith/mailview.h"
28 #include "../pith/conf.h"
29 #include "../pith/msgno.h"
30 #include "../pith/editorial.h"
31 #include "../pith/mimedesc.h"
32 #include "../pith/margin.h"
33 #include "../pith/color.h"
34 #include "../pith/strlst.h"
35 #include "../pith/charset.h"
36 #include "../pith/status.h"
37 #include "../pith/maillist.h"
38 #include "../pith/mailcmd.h"
39 #include "../pith/mailindx.h"
40 #include "../pith/imap.h"
41 #include "../pith/detach.h"
42 #include "../pith/text.h"
43 #include "../pith/url.h"
44 #include "../pith/rfc2231.h"
45 #include "../pith/list.h"
46 #include "../pith/stream.h"
47 #include "../pith/send.h"
48 #include "../pith/filter.h"
49 #include "../pith/string.h"
50 #include "../pith/ablookup.h"
51 #include "../pith/escapes.h"
52 #include "../pith/keyword.h"
53 #include "../pith/smime.h"
56 #define FBUF_LEN (50)
58 #define ISRFCEOL(S) (*(S) == '\015' && *((S)+1) == '\012')
62 * This is a list of header fields that are represented canonically
63 * by the c-client's ENVELOPE structure. The list is used by the
64 * two functions below to decide if a given field is included in this
65 * set.
67 static struct envelope_s {
68 char *name;
69 long val;
70 } envelope_hdrs[] = {
71 {"from", FE_FROM},
72 {"sender", FE_SENDER},
73 {"date", FE_DATE},
74 {"to", FE_TO},
75 {"cc", FE_CC},
76 {"bcc", FE_BCC},
77 {"newsgroups", FE_NEWSGROUPS},
78 {"subject", FE_SUBJECT},
79 {"message-id", FE_MESSAGEID},
80 {"reply-to", FE_REPLYTO},
81 {"followup-to", FE_FOLLOWUPTO},
82 {"in-reply-to", FE_INREPLYTO},
83 /* {"return-path", FE_RETURNPATH}, not usually filled in */
84 {"references", FE_REFERENCES},
85 {NULL, 0}
90 * Hook for optional display of rfc2369 content
92 int (*pith_opt_rfc2369_editorial)(long, HANDLE_S **, int, int, gf_io_t);
98 * Internal prototypes
100 int format_blip_seen(long);
101 int is_an_env_hdr(char *);
102 int is_an_addr_hdr(char *);
103 void format_env_hdr(MAILSTREAM *, long, char *, ENVELOPE *,
104 fmt_env_t, gf_io_t, char *, char *, int);
105 int delineate_this_header(char *, char *, char **, char **);
106 char *url_embed(int);
107 int color_headers(long, char *, LT_INS_S **, void *);
108 int url_hilite_hdr(long, char *, LT_INS_S **, void *);
109 int pad_to_right_edge(long, char *, LT_INS_S **, void *);
110 int url_bogus_imap(char **, char *, char *);
111 int format_raw_header(MAILSTREAM *, long, char *, gf_io_t);
112 void format_envelope(MAILSTREAM *, long, char *, ENVELOPE *,
113 gf_io_t, long, char *, int);
114 int any_hdr_color(char *);
115 void format_addr_string(MAILSTREAM *, long, char *, char *,
116 ADDRESS *, int, char *, gf_io_t);
117 void pine_rfc822_write_address_noquote(ADDRESS *, gf_io_t, int *);
118 void format_newsgroup_string(char *, char *, int, gf_io_t);
119 int format_raw_hdr_string(char *, char *, gf_io_t, char *, int);
120 int format_env_puts(char *, gf_io_t);
121 int find_field(char **, char *, size_t);
122 int embed_color(COLOR_PAIR *, gf_io_t);
123 COLOR_PAIR *get_cur_embedded_color(void);
124 void clear_cur_embedded_color(void);
125 void format_calendar_vevent(VCALENDAR_S *, ATTACH_S *, HANDLE_S **, int, int, gf_io_t, int);
129 /*----------------------------------------------------------------------
130 Format a message message for viewing
132 Args: msgno -- The number of the message to view
133 env -- pointer to the message's envelope
134 body -- pointer to the message's body
135 handlesp -- address of pointer to the message's handles
136 flgs -- possible flags listed in pith/mailview.h with
137 prefix FM_
138 pc -- write to this function
140 Result: Returns true if no problems encountered, else false.
142 First the envelope is formatted; next a list of all attachments is
143 formatted if there is more than one. Then all the body parts are
144 formatted, fetching them as needed. This includes headers of included
145 message. Richtext is also formatted. An entry is made in the text for
146 parts that are not displayed or can't be displayed.
148 ----*/
150 format_message(long int msgno, ENVELOPE *env, struct mail_bodystruct *body,
151 HANDLE_S **handlesp, int flgs, gf_io_t pc)
153 char *decode_err = NULL;
154 HEADER_S h;
155 int width, rv = 0;
157 clear_cur_embedded_color();
159 if(!(flgs & FM_DISPLAY))
160 flgs |= FM_NOINDENT;
162 width = (flgs & FM_DISPLAY) ? ps_global->ttyo->screen_cols : 80;
164 /*---- format and copy envelope ----*/
165 if(!(flgs & FM_NOEDITORIAL)){
166 if(ps_global->full_header == 1)
167 /* TRANSLATORS: User is viewing a message and all the quoted text is
168 being shown. */
169 q_status_message(SM_INFO, 0, 3, _("All quoted text being included"));
170 else if(ps_global->full_header == 2)
171 q_status_message(SM_INFO, 0, 3,
172 /* TRANSLATORS: User is viewing a message and all of
173 the header text is being shown. */
174 _("Full header mode ON. All header text being included"));
177 HD_INIT(&h, ps_global->VAR_VIEW_HEADERS, ps_global->view_all_except, FE_DEFAULT);
178 switch(format_header(ps_global->mail_stream, msgno, NULL,
179 env, &h, NULL, handlesp, flgs, NULL, pc)){
181 case -1 : /* write error */
182 goto write_error;
184 case 1 : /* fetch error */
185 if(!(gf_puts("[ Error fetching header ]", pc)
186 && !gf_puts(NEWLINE, pc)))
187 goto write_error;
189 break;
192 if(!(body == NULL
193 || (ps_global->full_header == 2
194 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global)))){
195 format_attachment_list(msgno, body, handlesp, flgs, width, pc);
196 format_calendar(msgno, body, handlesp, flgs, width, pc);
199 /* write delimiter and body */
200 if(gf_puts(NEWLINE, pc)){
201 if((decode_err = format_body(msgno, body, handlesp, &h, flgs, width, pc)) == NULL)
202 rv = 1;
203 else
204 rv = 0;
206 clear_cur_embedded_color();
207 return(rv);
210 write_error:
212 if(!(flgs & FM_DISPLAY))
213 q_status_message1(SM_ORDER, 3, 4, _("Error writing message: %s"),
214 decode_err ? decode_err : error_description(errno));
215 clear_cur_embedded_color();
216 return(0);
219 void
220 format_calendar_vevent(VCALENDAR_S *vcal, ATTACH_S *a, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc, int cflags)
222 int avail, m1, m2, hwid, i, partwid, padwid;
223 int s1, s2, dwid, minkey;
224 int *margin;
225 char padding[1024];
226 VEVENT_SUMMARY_S *vesy, *vesummary; /* vevent summary */
228 vesummary = vesy = ical_vevent_summary(vcal);
230 if(vesy == NULL) return;
232 if((cflags & FC_SUMMARY) && (cflags & FC_FULL))
233 cflags |= ~FC_FULL;
235 minkey = -1; /* initialize to something negative */
237 for(; vesy != NULL ; vesy = vesy->next){
238 avail = width;
239 margin = (cflags & FC_FULL) ? NULL
240 : (flgs & FM_NOINDENT) ? NULL : format_view_margin();
242 m1 = MAX(MIN(margin ? margin[0] : 0, avail), 0);
243 avail -= m1;
245 m2 = MAX(MIN(margin ? margin[1] : 0, avail), 0);
246 avail -= m2;
248 hwid = MAX(avail, 0);
249 padwid = 0;
251 if(ps_global->atmts[1].description == NULL){
252 avail = width - m1 -2;
254 dwid = MAX(MIN(40, avail), 0);
256 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s%s", m1, m1, "",
257 repeat_char(dwid, '-'));
259 if(!gf_puts(tmp_20k_buf, pc) || !gf_puts(NEWLINE, pc))
260 return;
264 if(cflags & FC_SUMMARY){
265 i = utf8_width(_("Calendar Entry:"));
266 partwid = MIN(i, hwid);
267 if(m1 > 0){
268 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", m1, m1, "");
269 if(!gf_puts(tmp_20k_buf, pc))
270 return;
273 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%-*.*w%*.*s",
274 partwid, partwid, _("Calendar Entry:"),
275 padwid, padwid, "");
277 if(!gf_puts(tmp_20k_buf, pc) || !gf_puts(NEWLINE, pc))
278 return;
280 else
281 partwid = 0;
283 if(m1 > 0){
284 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", m1, m1, "");
285 if(!gf_puts(tmp_20k_buf, pc))
286 return;
289 avail = width - m1 - m2;
291 s1 = MAX(MIN(1, avail), 0);
292 avail -= s1;
294 dwid = MAX(MIN(1, avail), 0);
295 avail -= dwid;
297 s2 = MAX(MIN(1, avail), 0);
298 avail -= s2;
300 if(cflags & FC_SUMMARY)
301 utf8_snprintf(padding, sizeof(padding), "%*.*s%*.*w%*.*s",
302 s1, s1, "", dwid, dwid, "", s2, s2, "");
303 else
304 padding[0] = '\0';
306 if(vesy->cancel){
307 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
308 padding, _("This event was cancelled!"));
309 if((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON)){
310 gf_puts(tmp_20k_buf, pc);
311 gf_puts(NEWLINE, pc);
312 (*pc)(TAG_EMBED);
313 (*pc)(TAG_BOLDOFF);
317 if(vesy->organizer){
318 if(vesy->sender != NULL){
319 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
320 padding, _("Sent-by: "), vesy->sender);
321 gf_puts(tmp_20k_buf, pc);
322 gf_puts(NEWLINE, pc);
325 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
326 padding, _("Organizer: "), vesy->organizer);
327 gf_puts(tmp_20k_buf, pc);
328 gf_puts(NEWLINE, pc);
329 } /* end of if(organizer) */
331 if(vesy->location){
332 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
333 padding, _("Location: "), vesy->location);
334 gf_puts(tmp_20k_buf, pc);
335 gf_puts(NEWLINE, pc);
336 } /* end of if location */
338 if(vesy->evstart){
339 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
340 padding, _("Start Date: "), vesy->evstart);
341 gf_puts(tmp_20k_buf, pc);
342 gf_puts(NEWLINE, pc);
343 } /* end of if dtstart */
345 if(vesy->duration){
346 int i;
348 for(i = 0; vesy->duration[i] != NULL; i++){
349 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
350 padding, _("Duration: "), vesy->duration[i]);
351 gf_puts(tmp_20k_buf, pc);
352 gf_puts(NEWLINE, pc);
354 } /* end of DURATION */
355 else if(vesy->evend){
356 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
357 padding, _("End Date: "), vesy->evend);
358 gf_puts(tmp_20k_buf, pc);
359 gf_puts(NEWLINE, pc);
360 } else { /* end of if dtend */
361 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
362 padding, _("No duration nor end time found for this event"));
363 gf_puts(tmp_20k_buf, pc);
364 gf_puts(NEWLINE, pc);
365 } /* end of else for if (duration) and if (dtend) */
367 if(vesy->attendee){
368 #define MAX_DISPLAYED 3
369 int i;
371 for(i = 0; vesy->attendee[i] != NULL; i++){
372 if((cflags & FC_SUMMARY) && i >= MAX_DISPLAYED)
373 break;
375 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
376 padding,
377 _("Attendee: "), vesy->attendee[i]);
378 gf_puts(tmp_20k_buf, pc);
379 gf_puts(NEWLINE, pc);
381 } /* end of ATTENDEES */
384 if(cflags & FC_SUMMARY){
385 COLOR_PAIR *lastc = NULL;
386 COLOR_PAIR *hdrcolor = NULL;
388 if((flgs & FM_DISPLAY)
389 && !(flgs & FM_NOCOLOR)
390 && pico_usingcolor()
391 && ps_global->VAR_HEADER_GENERAL_FORE_COLOR
392 && ps_global->VAR_HEADER_GENERAL_BACK_COLOR
393 && ps_global->VAR_NORM_FORE_COLOR
394 && ps_global->VAR_NORM_BACK_COLOR
395 && (colorcmp(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
396 ps_global->VAR_NORM_FORE_COLOR)
397 || colorcmp(ps_global->VAR_HEADER_GENERAL_BACK_COLOR,
398 ps_global->VAR_NORM_BACK_COLOR))){
400 if((hdrcolor = new_color_pair(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
401 ps_global->VAR_HEADER_GENERAL_BACK_COLOR)) != NULL){
402 if(!pico_is_good_colorpair(hdrcolor))
403 free_color_pair(&hdrcolor);
407 if(!(!hdrcolor || embed_color(hdrcolor, pc)))
408 return;
410 gf_puts(padding, pc);
411 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "[%s]", _("More Details"));
413 if(handlesp){
414 char buf[16], color[64];
415 int l;
416 HANDLE_S *h;
418 h = new_handle(handlesp);
419 if(minkey < 0) minkey = h->key;
420 h->type = iCal;
421 h->h.ical.attach = a;
422 h->h.ical.depth = h->key - minkey;
424 snprintf(buf, sizeof(buf), "%d", h->key);
425 buf[sizeof(buf)-1] = '\0';
427 if(!(flgs & FM_NOCOLOR)
428 && handle_start_color(color, sizeof(color), &l, 1)){
429 lastc = get_cur_embedded_color();
430 if(!gf_nputs(color, (long) l, pc))
431 return;
433 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)
434 && (!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON))))
435 return;
437 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE)
438 && (*pc)(strlen(buf)) && gf_puts(buf, pc)))
439 return;
440 } else
441 tmp_20k_buf[0] = '\0';
443 if(!format_env_puts(tmp_20k_buf, pc))
444 return;
446 if(handlesp){
447 if(lastc){
448 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
449 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
450 return;
453 if(!embed_color(lastc, pc))
454 return;
456 free_color_pair(&lastc);
458 else if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
459 return;
461 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)))
462 return;
465 if(padwid > 0){
466 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", padwid, padwid, "");
467 if(!gf_puts(tmp_20k_buf, pc))
468 return;
471 if(!gf_puts(NEWLINE, pc))
472 return;
474 avail = width - m1 -2;
476 dwid = MAX(MIN(40, avail), 0);
477 avail -= dwid;
479 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s%s", m1, m1, "",
480 repeat_char(dwid, '-'));
482 gf_puts(tmp_20k_buf, pc);
483 if(vesy->next)
484 gf_puts(NEWLINE, pc);
486 free_vevent_summary(&vesummary);
490 format_calendar(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc)
492 char *rawtext, *caltext;
493 unsigned long callen;
494 VCALENDAR_S *vcal = NULL;
495 ATTACH_S *a;
496 BODY *b;
498 if(flgs & FM_NEW_MESS) {
499 zero_atmts(ps_global->atmts);
500 describe_mime(body, "", 1, 1, 0, flgs);
503 for(a = ps_global->atmts; a->description != NULL; a++){
504 if(MIME_VCALENDAR(a->body->type, a->body->subtype)){
505 b = mail_body (ps_global->mail_stream, msgno, (unsigned char *) a->number);
506 if(b == NULL){
507 gf_puts(_("Error fetching calendar body part"), pc);
508 gf_puts(NEWLINE, pc);
509 continue;
511 if(b->sparep == NULL){
512 rawtext = mail_fetch_body(ps_global->mail_stream, msgno, a->number, &callen, 0);
513 if(rawtext == NULL || *rawtext == '\0'){
514 gf_puts(_("Error fetching calendar text"), pc);
515 gf_puts(NEWLINE, pc);
516 continue;
518 rawtext[callen] = '\0'; /* chop off cookie */
519 switch(b->encoding){
520 case ENCBASE64:
521 caltext = rfc822_base64((unsigned char *) rawtext, strlen(rawtext), &callen);
522 if(caltext == NULL){
523 gf_puts(_("Error in calendar base64 encoding"), pc);
524 gf_puts(NEWLINE, pc);
525 continue;
527 caltext[callen] = '\0';
528 break;
530 case ENCQUOTEDPRINTABLE:
531 caltext = rfc822_qprint ((unsigned char *) rawtext,strlen(rawtext),&callen);
532 if(caltext == NULL){
533 gf_puts(_("Error in calendar quoted printable encoding"), pc);
534 gf_puts(NEWLINE, pc);
535 continue;
537 caltext[callen] = '\0';
538 break;
540 default: caltext = cpystr(rawtext);
542 if(caltext != NULL){
543 vcal = ical_parse_text(caltext);
544 if(vcal != NULL) vcal->encoding = b->encoding;
545 b->sparep = create_body_sparep(iCalType, (void *) vcal);
546 fs_give((void **) &caltext);
549 else if(get_body_sparep_type(b->sparep) == iCalType)
550 vcal = (VCALENDAR_S *) get_body_sparep_data(b->sparep);
551 if(vcal != NULL && vcal->comp != NULL){
552 if(vcal->comp[VEvent] != NULL){
553 format_calendar_vevent(vcal, a, handlesp, flgs, width, pc, FC_SUMMARY);
554 } /* else another type of entry in the calendar */
556 gf_puts(NEWLINE, pc);
559 return 0;
563 char *
564 format_body(long int msgno, BODY *body, HANDLE_S **handlesp, HEADER_S *hp, int flgs, int width, gf_io_t pc)
566 int filt_only_c0 = 0, wrapflags, error_found = 0;
567 int is_in_sig = OUT_SIG_BLOCK;
568 char *charset, *decode_err = NULL, *tmp1, *description;
569 ATTACH_S *a;
570 URL_HILITE_S uh;
571 gf_io_t gc;
573 if(body == NULL
574 || (ps_global->full_header == 2
575 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))) {
577 /*--- Server is not an IMAP2bis, It can't parse MIME
578 so we just show the text here. Hopefully the
579 message isn't a MIME message
580 ---*/
581 void *text2;
583 if((text2 = (void *)pine_mail_fetch_text(ps_global->mail_stream,
584 msgno, NULL, NULL, NIL)) != NULL){
586 if(!gf_puts(NEWLINE, pc)) /* write delimiter */
587 return("Write Error");
589 gf_set_readc(&gc, text2, (unsigned long)strlen(text2), CharStar, 0);
590 gf_filter_init();
593 * We need to translate the message
594 * into UTF-8, but that's trouble in the full header case
595 * because we don't know what to translate from. We'll just
596 * take a guess by looking for the first text part and
597 * using its charset.
599 if(body && body->type == TYPETEXT)
600 charset = parameter_val(body->parameter, "charset");
601 else if(body && body->type == TYPEMULTIPART && body->nested.part
602 && body->nested.part->body.type == TYPETEXT)
603 charset = parameter_val(body->nested.part->body.parameter, "charset");
604 else
605 charset = cpystr(ps_global->display_charmap);
607 if(strucmp(charset, "us-ascii") && strucmp(charset, "utf-8")){
608 /* transliterate message text to UTF-8 */
609 gf_link_filter(gf_utf8, gf_utf8_opt(charset));
611 if (charset) fs_give((void **) &charset);
613 /* link in filters, similar to what is done in decode_text() */
614 if(!ps_global->pass_ctrl_chars){
615 gf_link_filter(gf_escape_filter, NULL);
616 filt_only_c0 = 1;
617 gf_link_filter(gf_control_filter,
618 gf_control_filter_opt(&filt_only_c0));
621 gf_link_filter(gf_tag_filter, NULL);
623 if((F_ON(F_VIEW_SEL_URL, ps_global)
624 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
625 || F_ON(F_SCAN_ADDR, ps_global))
626 && handlesp){
627 gf_link_filter(gf_line_test,
628 gf_line_test_opt(url_hilite,
629 gf_url_hilite_opt(&uh,handlesp,0)));
632 if((flgs & FM_DISPLAY)
633 && !(flgs & FM_NOCOLOR)
634 && pico_usingcolor()
635 && ps_global->VAR_SIGNATURE_FORE_COLOR
636 && ps_global->VAR_SIGNATURE_BACK_COLOR){
637 gf_link_filter(gf_line_test, gf_line_test_opt(color_signature, &is_in_sig));
640 if((flgs & FM_DISPLAY)
641 && !(flgs & FM_NOCOLOR)
642 && pico_usingcolor()
643 && ps_global->VAR_QUOTE1_FORE_COLOR
644 && ps_global->VAR_QUOTE1_BACK_COLOR){
645 gf_link_filter(gf_line_test, gf_line_test_opt(color_a_quote, NULL));
648 if(!(flgs & FM_NOWRAP)){
649 wrapflags = (flgs & FM_DISPLAY) ? (GFW_HANDLES|GFW_SOFTHYPHEN) : GFW_NONE;
650 if(flgs & FM_DISPLAY
651 && !(flgs & FM_NOCOLOR)
652 && pico_usingcolor())
653 wrapflags |= GFW_USECOLOR;
654 gf_link_filter(gf_wrap, gf_wrap_filter_opt(width, width,
655 (flgs & FM_NOINDENT)
656 ? NULL : format_view_margin(),
658 wrapflags));
661 gf_link_filter(gf_nvtnl_local, NULL);
662 if((decode_err = gf_pipe(gc, pc)) != NULL){
663 /* TRANSLATORS: There was an error putting together a message for
664 viewing. The arg is the description of the error. */
665 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Formatting error: %s"), decode_err);
666 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
667 if(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
668 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
669 && gf_puts(NEWLINE, pc))
670 decode_err = NULL;
671 else
672 return(decode_err);
676 if(!text2){
677 if(!gf_puts(NEWLINE, pc)
678 || !gf_puts(_(" [ERROR fetching text of message]"), pc)
679 || !gf_puts(NEWLINE, pc)
680 || !gf_puts(NEWLINE, pc))
681 return("Write Error");
684 else{
685 int show_parts = 0;
687 /*======== Now loop through formatting all the parts =======*/
688 for(a = ps_global->atmts; a->description != NULL; a++) {
689 if(MIME_VCALENDAR(a->body->type, a->body->subtype))
690 continue;
692 if(a->body->type == TYPEMULTIPART){
693 #ifdef SMIME
694 if(strucmp(a->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0){
695 if(a->description){
696 if(!(!format_editorial(a->description, width, flgs, handlesp, pc)
697 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
698 return("Write Error");
701 #endif /* SMIME */
702 continue;
705 if(!a->shown) {
706 if(a->suppress_editorial)
707 continue;
709 if(!(flgs & FM_NOEDITORIAL)
710 && (!gf_puts(NEWLINE, pc)
711 || (decode_err = part_desc(a->number, a->body,
712 (flgs & FM_DISPLAY)
713 ? (a->can_display != MCD_NONE)
714 ? 1 : 2
715 : 3, width, flgs, pc))))
716 return("Write Error");
718 continue;
721 switch(a->body->type){
723 case TYPETEXT:
725 * If a message is multipart *and* the first part of it
726 * is text *and that text is empty, there is a good chance that
727 * there was actually something there that c-client was
728 * unable to parse. Here we report the empty message body
729 * and insert the raw RFC822.TEXT (if full-headers are
730 * on).
732 if(body->type == TYPEMULTIPART
733 && a == ps_global->atmts
734 && a->body->size.bytes == 0
735 && F_ON(F_ENABLE_FULL_HDR, ps_global)){
736 char *err = NULL;
738 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
739 "Empty or malformed message%s.",
740 ps_global->full_header == 2
741 ? ". Displaying raw text"
742 : ". Use \"H\" to see raw text");
743 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
745 if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
746 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
747 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
748 return("Write Error");
750 if(ps_global->full_header == 2
751 && (err = detach_raw(ps_global->mail_stream, msgno,
752 a->number, pc, flgs))){
753 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
754 "%s%s [ Formatting error: %s ]%s%s",
755 NEWLINE, NEWLINE, err, NEWLINE, NEWLINE);
756 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
757 if(!gf_puts(tmp_20k_buf, pc))
758 return("Write Error");
761 break;
765 * Don't write our delimiter if this text part is
766 * the first part of a message/rfc822 segment...
768 if(show_parts && a != ps_global->atmts
769 && !((a[-1].body && a[-1].body->type == TYPEMESSAGE)
770 #ifdef SMIME
771 || (a[-1].body->type == TYPEMULTIPART
772 && a[-1].body->subtype
773 && (strucmp(a[-1].body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0)
774 && &a[-1] != ps_global->atmts
775 && a[-2].body && a[-2].body->type == TYPEMESSAGE)
776 #endif /* SMIME */
778 && !(flgs & FM_NOEDITORIAL)){
779 if(MIME_VCALENDAR(a->body->type, a->body->subtype))
780 tmp1 = "Calendar entry";
781 else
782 tmp1 = a->body->description ? a->body->description
783 : "Attached Text";
784 description = iutf8ncpy((char *)(tmp_20k_buf+10000),
785 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+15000), 5000, tmp1), 5000);
787 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Part %s: \"%.1024s\"", a->number,
788 description);
789 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
790 if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
791 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
792 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
793 return("Write Error");
795 /* skip calendar */
796 if(!MIME_VCALENDAR(a->body->type, a->body->subtype))
797 error_found += decode_text(a, msgno, pc, handlesp,
798 (flgs & FM_DISPLAY) ? InLine : QStatus,
799 flgs);
800 break;
802 case TYPEMESSAGE:
803 tmp1 = a->body->description ? a->body->description
804 : (strucmp(a->body->subtype, "delivery-status") == 0)
805 ? "Delivery Status"
806 : "Included Message";
807 description = iutf8ncpy((char *)(tmp_20k_buf+10000),
808 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+15000), 5000, tmp1), 5000);
810 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Part %s: \"%.1024s\"", a->number,
811 description);
812 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
814 if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
815 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
816 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
817 return("Write Error");
819 if(a->body->subtype && strucmp(a->body->subtype, "rfc822") == 0){
820 /* imapenvonly, we may not have all the headers we need */
821 if(a->body->nested.msg->env->imapenvonly)
822 mail_fetch_header(ps_global->mail_stream, msgno,
823 a->number, NULL, NULL, FT_PEEK);
824 switch(format_header(ps_global->mail_stream, msgno, a->number,
825 a->body->nested.msg->env, hp,
826 NULL, handlesp, flgs, NULL, pc)){
827 case -1 : /* write error */
828 return("Write Error");
830 case 1 : /* fetch error */
831 if(!(gf_puts("[ Error fetching header ]", pc)
832 && !gf_puts(NEWLINE, pc)))
833 return("Write Error");
835 break;
838 else if(a->body->subtype && strucmp(a->body->subtype, "external-body") == 0){
839 int *margin, avail, m1, m2;
841 avail = width;
842 margin = (flgs & FM_NOINDENT) ? NULL : format_view_margin();
844 m1 = MAX(MIN(margin ? margin[0] : 0, avail), 0);
845 avail -= m1;
847 m2 = MAX(MIN(margin ? margin[1] : 0, avail), 0);
848 avail -= m2;
850 if(format_editorial("This part is not included and can be fetched as follows:", avail, flgs, handlesp, pc)
851 || !gf_puts(NEWLINE, pc)
852 || format_editorial(display_parameters(a->body->parameter), avail, flgs, handlesp, pc))
853 return("Write Error");
855 else
856 error_found += decode_text(a, msgno, pc, handlesp,
857 (flgs&FM_DISPLAY) ? InLine : QStatus,
858 flgs);
860 if(!gf_puts(NEWLINE, pc))
861 return("Write Error");
863 break;
865 default:
866 if((decode_err = part_desc(a->number, a->body,
867 (flgs & FM_DISPLAY) ? 1 : 3,
868 width, flgs, pc)) != NULL)
869 return("Write Error");
872 show_parts++;
875 if(!(!error_found
876 && (pith_opt_rfc2369_editorial ? (*pith_opt_rfc2369_editorial)(msgno, handlesp, flgs, width, pc) : 1)
877 && format_blip_seen(msgno)))
878 return("Cannot format body.");
881 return(NULL);
886 format_attachment_list(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc)
888 ATTACH_S *a;
890 if(flgs & FM_NEW_MESS) {
891 zero_atmts(ps_global->atmts);
892 describe_mime(body, "", 1, 1, 0, flgs);
895 /*----- First do the list of parts/attachments if needed ----*/
896 if((flgs & FM_DISPLAY)
897 && (ps_global->atmts[1].description
898 || (ps_global->atmts[0].body
899 && ps_global->atmts[0].body->type != TYPETEXT))){
900 char tmp[6*MAX_SCREEN_COLS + 1], *tmpp;
901 int i, n, maxnumwid = 0, maxsizewid = 0, *margin;
902 int avail, m1, m2, hwid, s1, s2, s3, s4, s5, dwid, shownwid;
903 int sizewid, descwid, dashwid, partwid, padwid;
904 COLOR_PAIR *hdrcolor = NULL;
906 if((flgs & FM_DISPLAY)
907 && !(flgs & FM_NOCOLOR)
908 && pico_usingcolor()
909 && ps_global->VAR_HEADER_GENERAL_FORE_COLOR
910 && ps_global->VAR_HEADER_GENERAL_BACK_COLOR
911 && ps_global->VAR_NORM_FORE_COLOR
912 && ps_global->VAR_NORM_BACK_COLOR
913 && (colorcmp(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
914 ps_global->VAR_NORM_FORE_COLOR)
915 || colorcmp(ps_global->VAR_HEADER_GENERAL_BACK_COLOR,
916 ps_global->VAR_NORM_BACK_COLOR))){
918 if((hdrcolor = new_color_pair(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
919 ps_global->VAR_HEADER_GENERAL_BACK_COLOR)) != NULL){
920 if(!pico_is_good_colorpair(hdrcolor))
921 free_color_pair(&hdrcolor);
925 margin = (flgs & FM_NOINDENT) ? NULL : format_view_margin();
928 * Attachment list header
931 avail = width;
933 m1 = MAX(MIN(margin ? margin[0] : 0, avail), 0);
934 avail -= m1;
936 m2 = MAX(MIN(margin ? margin[1] : 0, avail), 0);
937 avail -= m2;
939 hwid = MAX(avail, 0);
941 i = utf8_width(_("Parts/Attachments:"));
942 partwid = MIN(i, hwid);
943 padwid = hdrcolor ? (hwid-partwid) : 0;
945 if(m1 > 0){
946 snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
947 if(!gf_puts(tmp, pc))
948 return(0);
951 utf8_snprintf(tmp, sizeof(tmp),
952 "%-*.*w%*.*s",
953 /* TRANSLATORS: A label */
954 partwid, partwid, _("Parts/Attachments:"),
955 padwid, padwid, "");
957 if(!((!hdrcolor || embed_color(hdrcolor, pc)) && gf_puts(tmp, pc) && gf_puts(NEWLINE, pc)))
958 return(0);
961 /*----- Figure max display widths -----*/
962 for(a = ps_global->atmts; a->description != NULL; a++){
963 if(MIME_VCALENDAR(a->body->type, a->body->subtype))
964 continue;
966 if((n = utf8_width(a->number)) > maxnumwid)
967 maxnumwid = n;
969 if((n = utf8_width(a->size)) > maxsizewid)
970 maxsizewid = n;
974 * ----- adjust max lengths for nice display -----
976 * marg _ D _ number _ Shown _ _ _ size _ _ description marg
980 avail = width - m1 - m2;
982 s1 = MAX(MIN(1, avail), 0);
983 avail -= s1;
985 dwid = MAX(MIN(1, avail), 0);
986 avail -= dwid;
988 s2 = MAX(MIN(1, avail), 0);
989 avail -= s2;
991 maxnumwid = MIN(maxnumwid, width/3);
992 maxnumwid = MAX(MIN(maxnumwid, avail), 0);
993 avail -= maxnumwid;
995 s3 = MAX(MIN(1, avail), 0);
996 avail -= s3;
998 shownwid = MAX(MIN(5, avail), 0);
999 avail -= shownwid;
1001 s4 = MAX(MIN(3, avail), 0);
1002 avail -= s4;
1004 sizewid = MAX(MIN(maxsizewid, avail), 0);
1005 avail -= sizewid;
1007 s5 = MAX(MIN(2, avail), 0);
1008 avail -= s5;
1010 descwid = MAX(0, avail);
1012 /*----- Format the list of attachments -----*/
1013 for(a = ps_global->atmts; a->description != NULL; a++){
1014 COLOR_PAIR *lastc = NULL;
1015 char numbuf[50];
1016 int thisdescwid, padwid;
1018 if(MIME_VCALENDAR(a->body->type, a->body->subtype))
1019 continue;
1020 #ifdef SMIME
1021 if(a->body->type == TYPEMULTIPART
1022 && (strucmp(a->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0))
1023 continue;
1024 #endif /* SMIME */
1026 i = utf8_width((descwid > 2 && a->description) ? a->description : "");
1027 thisdescwid = MIN(i, descwid);
1028 padwid = hdrcolor ? (descwid-thisdescwid) : 0;
1030 if(m1 > 0){
1031 snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
1032 if(!gf_puts(tmp, pc))
1033 return(0);
1036 utf8_snprintf(tmp, sizeof(tmp),
1037 "%*.*s%*.*w%*.*s%-*.*w%*.*s%*.*w%*.*s%*.*w%*.*s%-*.*w",
1038 s1, s1, "",
1039 dwid, dwid,
1040 msgno_part_deleted(ps_global->mail_stream, msgno, a->number) ? "D" : "",
1041 s2, s2, "",
1042 maxnumwid, maxnumwid,
1043 a->number
1044 ? short_str(a->number, numbuf, sizeof(numbuf), maxnumwid, FrontDots)
1045 : "",
1046 s3, s3, "",
1047 shownwid, shownwid,
1048 a->shown ? "Shown" :
1049 (a->can_display != MCD_NONE && !(a->can_display & MCD_EXT_PROMPT))
1050 ? "OK " : "",
1051 s4, s4, "",
1052 sizewid, sizewid,
1053 a->size ? a->size : "",
1054 s5, s5, "",
1055 thisdescwid, thisdescwid,
1056 (descwid > 2 && a->description) ? a->description : "");
1058 if(!(!hdrcolor || embed_color(hdrcolor, pc)))
1059 return(0);
1061 if(F_ON(F_VIEW_SEL_ATTACH, ps_global) && handlesp){
1062 char buf[16], color[64];
1063 int l;
1064 HANDLE_S *h;
1066 for(tmpp = tmp; *tmpp && *tmpp == ' '; tmpp++)
1067 if(!(*pc)(' '))
1068 return(0);
1070 h = new_handle(handlesp);
1071 h->type = Attach;
1072 h->h.attach = a;
1074 snprintf(buf, sizeof(buf), "%d", h->key);
1075 buf[sizeof(buf)-1] = '\0';
1077 if(!(flgs & FM_NOCOLOR)
1078 && handle_start_color(color, sizeof(color), &l, 1)){
1079 lastc = get_cur_embedded_color();
1080 if(!gf_nputs(color, (long) l, pc))
1081 return(0);
1083 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)
1084 && (!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON))))
1085 return(0);
1087 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE)
1088 && (*pc)(strlen(buf)) && gf_puts(buf, pc)))
1089 return(0);
1091 else
1092 tmpp = tmp;
1094 if(!format_env_puts(tmpp, pc))
1095 return(0);
1097 if(F_ON(F_VIEW_SEL_ATTACH, ps_global) && handlesp){
1098 if(lastc){
1099 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
1100 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
1101 return(0);
1104 if(!embed_color(lastc, pc))
1105 return(0);
1107 free_color_pair(&lastc);
1109 else if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
1110 return(0);
1112 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)))
1113 return(0);
1116 if(padwid > 0){
1117 snprintf(tmp, sizeof(tmp), "%*.*s", padwid, padwid, "");
1118 if(!gf_puts(tmp, pc))
1119 return(0);
1122 if(!gf_puts(NEWLINE, pc))
1123 return(0);
1127 * Dashed line after list
1130 if(hdrcolor){
1131 avail = width - m1 - m2;
1132 hwid = MAX(avail, 0);
1134 dashwid = MAX(MIN(40, hwid-2), 0);
1135 padwid = hwid - dashwid;
1136 if(m1 > 0){
1137 snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
1138 if(!gf_puts(tmp, pc))
1139 return(0);
1142 snprintf(tmp, sizeof(tmp),
1143 "%s%*.*s",
1144 repeat_char(dashwid, '-'),
1145 padwid, padwid, "");
1147 else{
1148 avail = width - m1 -2;
1150 dashwid = MAX(MIN(40, avail), 0);
1151 avail -= dashwid;
1153 snprintf(tmp, sizeof(tmp),
1154 "%*.*s%s",
1155 m1, m1, "",
1156 repeat_char(dashwid, '-'));
1159 if(!((!hdrcolor || embed_color(hdrcolor, pc)) && gf_puts(tmp, pc) && gf_puts(NEWLINE, pc)))
1160 return(0);
1162 if(hdrcolor)
1163 free_color_pair(&hdrcolor);
1166 return(1);
1172 * format_blip_seen - if seen bit (which is usually cleared as a side-effect
1173 * of body part fetches as we're formatting) for the
1174 * given message isn't set (likely because there
1175 * weren't any parts suitable for display), then make
1176 * sure to set it here.
1179 format_blip_seen(long int msgno)
1181 MESSAGECACHE *mc;
1183 if(msgno > 0L && ps_global->mail_stream
1184 && msgno <= ps_global->mail_stream->nmsgs
1185 && (mc = mail_elt(ps_global->mail_stream, msgno))
1186 && !mc->seen
1187 && !ps_global->mail_stream->rdonly)
1188 mail_flag(ps_global->mail_stream, long2string(msgno), "\\SEEN", ST_SET);
1190 return(1);
1195 * is_an_env_hdr - is this name a header in the envelope structure?
1197 * name - the header name to check
1200 is_an_env_hdr(char *name)
1202 register int i;
1204 for(i = 0; envelope_hdrs[i].name; i++)
1205 if(!strucmp(name, envelope_hdrs[i].name))
1206 return(1);
1208 return(0);
1215 * is_an_addr_hdr - is this an address header?
1217 * name - the header name to check
1220 is_an_addr_hdr(char *fieldname)
1222 char fbuf[FBUF_LEN+1];
1223 char *colon, *fname;
1224 static char *addr_headers[] = {
1225 "from",
1226 "reply-to",
1227 "to",
1228 "cc",
1229 "bcc",
1230 "return-path",
1231 "sender",
1232 "x-sender",
1233 "x-x-sender",
1234 "resent-from",
1235 "resent-to",
1236 "resent-cc",
1237 NULL
1240 /* so it is pointing to NULL */
1241 char **p = addr_headers + sizeof(addr_headers)/sizeof(*addr_headers) - 1;
1243 if((colon = strindex(fieldname, ':')) != NULL){
1244 strncpy(fbuf, fieldname, MIN(colon-fieldname,sizeof(fbuf)));
1245 fbuf[MIN(colon-fieldname,sizeof(fbuf)-1)] = '\0';
1246 fname = fbuf;
1248 else
1249 fname = fieldname;
1251 if(fname && *fname){
1252 for(p = addr_headers; *p; p++)
1253 if(!strucmp(fname, *p))
1254 break;
1257 return((*p) ? 1 : 0);
1262 * Format a single field from the envelope
1264 void
1265 format_env_hdr(MAILSTREAM *stream, long int msgno, char *section, ENVELOPE *env,
1266 fmt_env_t fmt_env, gf_io_t pc, char *field, char *oacs, int flags)
1268 register int i;
1270 if(!fmt_env)
1271 fmt_env = format_envelope;
1273 for(i = 0; envelope_hdrs[i].name; i++)
1274 if(!strucmp(field, envelope_hdrs[i].name)){
1275 (*fmt_env)(stream, msgno, section, env, pc, envelope_hdrs[i].val, oacs, flags);
1276 return;
1282 * Look through header string beginning with "begin", for the next
1283 * occurrence of header "field". Set "start" to that. Set "end" to point one
1284 * position past all of the continuation lines that go with "field".
1285 * That is, if "end" is converted to a null
1286 * character then the string "start" will be the next occurrence of header
1287 * "field" including all of its continuation lines. Assume we
1288 * have CRLF's as end of lines.
1290 * If "field" is NULL, then we just leave "start" pointing to "begin" and
1291 * make "end" the end of that header.
1293 * Returns 1 if found, 0 if not.
1296 delineate_this_header(char *field, char *begin, char **start, char **end)
1298 char tmpfield[MAILTMPLEN+2]; /* copy of field with colon appended */
1299 char *p;
1300 char *begin_srch;
1302 if(field == NULL){
1303 if(!begin || !*begin || isspace((unsigned char)*begin))
1304 return 0;
1305 else
1306 *start = begin;
1308 else{
1309 strncpy(tmpfield, field, sizeof(tmpfield)-2);
1310 tmpfield[sizeof(tmpfield)-2] = '\0';
1311 strncat(tmpfield, ":", sizeof(tmpfield)-strlen(tmpfield)-1);
1312 tmpfield[sizeof(tmpfield)-1] = '\0';
1315 * We require that start is at the beginning of a line, so
1316 * either it equals begin (which we assume is the beginning of a
1317 * line) or it is preceded by a CRLF.
1319 begin_srch = begin;
1320 *start = srchstr(begin_srch, tmpfield);
1321 while(*start && *start != begin
1322 && !(*start - 2 >= begin && ISRFCEOL(*start - 2))){
1323 begin_srch = *start + 1;
1324 *start = srchstr(begin_srch, tmpfield);
1327 if(!*start)
1328 return 0;
1331 for(p = *start; *p; p++){
1332 if(ISRFCEOL(p)
1333 && (!isspace((unsigned char)*(p+2)) || *(p+2) == '\015')){
1335 * The final 015 in the test above is to test for the end
1336 * of the headers.
1338 *end = p+2;
1339 break;
1343 if(!*p)
1344 *end = p;
1346 return 1;
1352 handle_start_color(char *colorstring, size_t buflen, int *len, int use_hdr_color)
1354 *len = 0;
1356 if(pico_usingcolor()){
1357 char *fg = NULL, *bg = NULL, *s;
1358 char *basefg = NULL, *basebg = NULL;
1360 basefg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_FORE_COLOR
1361 : ps_global->VAR_NORM_FORE_COLOR;
1362 basebg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_BACK_COLOR
1363 : ps_global->VAR_NORM_BACK_COLOR;
1365 if(ps_global->VAR_SLCTBL_FORE_COLOR
1366 && colorcmp(ps_global->VAR_SLCTBL_FORE_COLOR, basefg))
1367 fg = ps_global->VAR_SLCTBL_FORE_COLOR;
1369 if(ps_global->VAR_SLCTBL_BACK_COLOR
1370 && colorcmp(ps_global->VAR_SLCTBL_BACK_COLOR, basebg))
1371 bg = ps_global->VAR_SLCTBL_BACK_COLOR;
1373 if(bg || fg){
1374 COLOR_PAIR *tmp;
1377 * The blacks are just known good colors for
1378 * testing whether the other color is good.
1380 if((tmp = new_color_pair(fg ? fg : colorx(COL_BLACK),
1381 bg ? bg : colorx(COL_BLACK))) != NULL){
1382 if(pico_is_good_colorpair(tmp))
1383 for(s = color_embed(fg, bg);
1384 (*len) < buflen && (colorstring[*len] = *s);
1385 s++, (*len)++)
1388 free_color_pair(&tmp);
1392 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
1393 strncpy(colorstring + (*len), url_embed(TAG_BOLDON), MIN(3,buflen-(*len)));
1394 *len += 2;
1398 colorstring[buflen-1] = '\0';
1400 return(*len != 0);
1405 handle_end_color(char *colorstring, size_t buflen, int *len)
1407 *len = 0;
1408 if(pico_usingcolor()){
1409 char *fg = NULL, *bg = NULL, *s;
1412 * We need to change the fg and bg colors back even if they
1413 * are the same as the Normal Colors so that color_a_quote
1414 * will have a chance to pick up those colors as the ones to
1415 * switch to. We don't do this before the handle above so that
1416 * the quote color will flow into the selectable item when
1417 * the selectable item color is partly the same as the
1418 * normal color. That is, suppose the normal color was black on
1419 * cyan and the selectable color was blue on cyan, only a fg color
1420 * change. We preserve the only-a-fg-color-change in a quote by
1421 * letting the quote background color flow into the selectable text.
1423 if(ps_global->VAR_SLCTBL_FORE_COLOR)
1424 fg = ps_global->VAR_NORM_FORE_COLOR;
1426 if(ps_global->VAR_SLCTBL_BACK_COLOR)
1427 bg = ps_global->VAR_NORM_BACK_COLOR;
1429 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
1430 strncpy(colorstring, url_embed(TAG_BOLDOFF), MIN(3,buflen));
1431 *len = 2;
1434 if(fg || bg)
1435 for(s = color_embed(fg, bg); (*len) < buflen && (colorstring[*len] = *s); s++, (*len)++)
1439 colorstring[buflen-1] = '\0';
1441 return(*len != 0);
1445 char *
1446 url_embed(int embed)
1448 static char buf[3] = {TAG_EMBED};
1449 buf[1] = embed;
1450 buf[2] = '\0';
1451 return(buf);
1456 * Paint the signature.
1459 color_signature(long int linenum, char *line, LT_INS_S **ins, void *is_in_sig)
1461 struct variable *vars = ps_global->vars;
1462 int *in_sig_block;
1463 COLOR_PAIR *col = NULL;
1465 if(is_in_sig == NULL)
1466 return 0;
1468 in_sig_block = (int *) is_in_sig;
1470 if(!strcmp(line, SIGDASHES))
1471 *in_sig_block = START_SIG_BLOCK;
1472 else if(*line == '\0')
1474 * Suggested by Eduardo: allow for a blank line right after
1475 * the sigdashes.
1477 *in_sig_block = (*in_sig_block == START_SIG_BLOCK)
1478 ? IN_SIG_BLOCK : OUT_SIG_BLOCK;
1479 else
1480 *in_sig_block = (*in_sig_block != OUT_SIG_BLOCK)
1481 ? IN_SIG_BLOCK : OUT_SIG_BLOCK;
1483 if(*in_sig_block != OUT_SIG_BLOCK
1484 && VAR_SIGNATURE_FORE_COLOR && VAR_SIGNATURE_BACK_COLOR
1485 && (col = new_color_pair(VAR_SIGNATURE_FORE_COLOR,
1486 VAR_SIGNATURE_BACK_COLOR))){
1487 if(!pico_is_good_colorpair(col))
1488 free_color_pair(&col);
1491 if(col){
1492 char *p, fg[RGBLEN + 1], bg[RGBLEN + 1], rgbbuf[RGBLEN + 1];
1494 ins = gf_line_test_new_ins(ins, line,
1495 color_embed(col->fg, col->bg),
1496 (2 * RGBLEN) + 4);
1498 strncpy(fg, color_to_asciirgb(VAR_NORM_FORE_COLOR), sizeof(fg));
1499 fg[sizeof(fg)-1] = '\0';
1500 strncpy(bg, color_to_asciirgb(VAR_NORM_BACK_COLOR), sizeof(bg));
1501 bg[sizeof(bg)-1] = '\0';
1504 * Loop watching colors, and override with
1505 * signature color whenever the normal foreground and background
1506 * colors are in force.
1509 for(p = line; *p; )
1510 if(*p++ == TAG_EMBED){
1512 switch(*p++){
1513 case TAG_HANDLE :
1514 p += *p + 1; /* skip handle key */
1515 break;
1517 case TAG_FGCOLOR :
1518 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1519 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1520 p += RGBLEN; /* advance past color value */
1522 if(!colorcmp(rgbbuf, VAR_NORM_FORE_COLOR)
1523 && !colorcmp(bg, VAR_NORM_BACK_COLOR))
1524 ins = gf_line_test_new_ins(ins, p,
1525 color_embed(col->fg,NULL),
1526 RGBLEN + 2);
1527 break;
1529 case TAG_BGCOLOR :
1530 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1531 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1532 p += RGBLEN; /* advance past color value */
1534 if(!colorcmp(rgbbuf, VAR_NORM_BACK_COLOR)
1535 && !colorcmp(fg, VAR_NORM_FORE_COLOR))
1536 ins = gf_line_test_new_ins(ins, p,
1537 color_embed(NULL,col->bg),
1538 RGBLEN + 2);
1540 break;
1542 default :
1543 break;
1547 ins = gf_line_test_new_ins(ins, line + strlen(line),
1548 color_embed(VAR_NORM_FORE_COLOR,
1549 VAR_NORM_BACK_COLOR),
1550 (2 * RGBLEN) + 4);
1551 free_color_pair(&col);
1554 return 0;
1559 * Line filter to add color to displayed headers.
1562 color_headers(long int linenum, char *line, LT_INS_S **ins, void *local)
1564 static char field[FBUF_LEN + 1];
1565 char fg[RGBLEN + 1], bg[RGBLEN + 1], rgbbuf[RGBLEN + 1];
1566 char *p, *q, *value, *beg;
1567 COLOR_PAIR *color;
1568 int in_quote = 0, in_comment = 0, did_color = 0;
1569 struct variable *vars = ps_global->vars;
1571 field[FBUF_LEN] = '\0';
1573 if(isspace((unsigned char)*line)) /* continuation line */
1574 value = line;
1575 else{
1576 if(!(value = strindex(line, ':')))
1577 return(0);
1579 memset(field, 0, sizeof(field));
1580 strncpy(field, line, MIN(value-line, sizeof(field)-1));
1583 for(value++; isspace((unsigned char)*value); value++)
1586 strncpy(fg, color_to_asciirgb(VAR_HEADER_GENERAL_FORE_COLOR), sizeof(fg));
1587 fg[sizeof(fg)-1] = '\0';
1588 strncpy(bg, color_to_asciirgb(VAR_HEADER_GENERAL_BACK_COLOR), sizeof(bg));
1589 bg[sizeof(bg)-1] = '\0';
1592 * Split into two cases depending on whether this is a header which
1593 * contains addresses or not. We may color addresses separately.
1595 if(is_an_addr_hdr(field)){
1598 * If none of the patterns are for this header, don't bother parsing
1599 * and checking each address.
1601 if(!any_hdr_color(field))
1602 return(0);
1605 * First check for patternless patterns which color whole line.
1607 if((color = hdr_color(field, NULL, ps_global->hdr_colors)) != NULL){
1608 if(pico_is_good_colorpair(color)){
1609 ins = gf_line_test_new_ins(ins, value,
1610 color_embed(color->fg, color->bg),
1611 (2 * RGBLEN) + 4);
1612 strncpy(fg, color_to_asciirgb(color->fg), sizeof(fg));
1613 fg[sizeof(fg)-1] = '\0';
1614 strncpy(bg, color_to_asciirgb(color->bg), sizeof(bg));
1615 bg[sizeof(bg)-1] = '\0';
1616 did_color++;
1618 else
1619 free_color_pair(&color);
1623 * Then go through checking address by address.
1624 * Keep track of quotes and watch for color changes, and override
1625 * with most recent header color whenever the normal foreground
1626 * and background colors are in force.
1628 beg = p = value;
1629 while(*p){
1630 switch(*p){
1631 case '\\':
1632 /* skip next character */
1633 if(*(p+1) && (in_comment || in_quote))
1634 p += 2;
1635 else
1636 p++;
1638 break;
1640 case '"':
1641 if(!in_comment)
1642 in_quote = 1 - in_quote;
1644 p++;
1645 break;
1647 case '(':
1648 in_comment++;
1649 p++;
1650 break;
1652 case ')':
1653 if(in_comment > 0)
1654 in_comment--;
1656 p++;
1657 break;
1659 case ',':
1660 if(!(in_quote || in_comment)){
1661 /* we reached the end of this address */
1662 *p = '\0';
1663 if(color)
1664 free_color_pair(&color);
1666 if((color = hdr_color(field, beg,
1667 ps_global->hdr_colors)) != NULL){
1668 if(pico_is_good_colorpair(color)){
1669 did_color++;
1670 ins = gf_line_test_new_ins(ins, beg,
1671 color_embed(color->fg,
1672 color->bg),
1673 (2 * RGBLEN) + 4);
1674 *p = ',';
1675 for(q = p; q > beg &&
1676 isspace((unsigned char)*(q-1)); q--)
1679 ins = gf_line_test_new_ins(ins, q,
1680 color_embed(fg, bg),
1681 (2 * RGBLEN) + 4);
1683 else
1684 free_color_pair(&color);
1686 else
1687 *p = ',';
1689 for(p++; isspace((unsigned char)*p); p++)
1692 beg = p;
1694 else
1695 p++;
1697 break;
1699 case TAG_EMBED:
1700 switch(*(++p)){
1701 case TAG_HANDLE:
1702 p++;
1703 p += *p + 1; /* skip handle key */
1704 break;
1706 case TAG_FGCOLOR:
1707 p++;
1708 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1709 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1710 p += RGBLEN; /* advance past color value */
1712 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_FORE_COLOR))
1713 ins = gf_line_test_new_ins(ins, p,
1714 color_embed(color->fg,NULL),
1715 RGBLEN + 2);
1716 break;
1718 case TAG_BGCOLOR:
1719 p++;
1720 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1721 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1722 p += RGBLEN; /* advance past color value */
1724 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_BACK_COLOR))
1725 ins = gf_line_test_new_ins(ins, p,
1726 color_embed(NULL,color->bg),
1727 RGBLEN + 2);
1729 break;
1731 default:
1732 break;
1735 break;
1737 default:
1738 p++;
1739 break;
1743 for(q = beg; *q && isspace((unsigned char)*q); q++)
1746 if(*q && !(in_quote || in_comment)){
1747 /* we reached the end of this address */
1748 if(color)
1749 free_color_pair(&color);
1751 if((color = hdr_color(field, beg, ps_global->hdr_colors)) != NULL){
1752 if(pico_is_good_colorpair(color)){
1753 did_color++;
1754 ins = gf_line_test_new_ins(ins, beg,
1755 color_embed(color->fg,
1756 color->bg),
1757 (2 * RGBLEN) + 4);
1758 for(q = p; q > beg && isspace((unsigned char)*(q-1)); q--)
1761 ins = gf_line_test_new_ins(ins, q,
1762 color_embed(fg, bg),
1763 (2 * RGBLEN) + 4);
1765 else
1766 free_color_pair(&color);
1770 if(color)
1771 free_color_pair(&color);
1773 if(did_color)
1774 ins = gf_line_test_new_ins(ins, line + strlen(line),
1775 color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
1776 VAR_HEADER_GENERAL_BACK_COLOR),
1777 (2 * RGBLEN) + 4);
1779 else{
1781 color = hdr_color(field, value, ps_global->hdr_colors);
1783 if(color){
1784 if(pico_is_good_colorpair(color)){
1785 ins = gf_line_test_new_ins(ins, value,
1786 color_embed(color->fg, color->bg),
1787 (2 * RGBLEN) + 4);
1790 * Loop watching colors, and override with header
1791 * color whenever the normal foreground and background
1792 * colors are in force.
1794 p = value;
1795 while(*p)
1796 if(*p++ == TAG_EMBED){
1798 switch(*p++){
1799 case TAG_HANDLE:
1800 p += *p + 1; /* skip handle key */
1801 break;
1803 case TAG_FGCOLOR:
1804 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1805 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1806 p += RGBLEN; /* advance past color value */
1808 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_FORE_COLOR)
1809 && !colorcmp(bg, VAR_HEADER_GENERAL_BACK_COLOR))
1810 ins = gf_line_test_new_ins(ins, p,
1811 color_embed(color->fg,NULL),
1812 RGBLEN + 2);
1813 break;
1815 case TAG_BGCOLOR:
1816 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1817 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1818 p += RGBLEN; /* advance past color value */
1820 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_BACK_COLOR)
1821 && !colorcmp(fg, VAR_HEADER_GENERAL_FORE_COLOR))
1822 ins = gf_line_test_new_ins(ins, p,
1823 color_embed(NULL,color->bg),
1824 RGBLEN + 2);
1826 break;
1828 default:
1829 break;
1833 ins = gf_line_test_new_ins(ins, line + strlen(line),
1834 color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
1835 VAR_HEADER_GENERAL_BACK_COLOR),
1836 (2 * RGBLEN) + 4);
1839 free_color_pair(&color);
1843 return(0);
1848 url_hilite(long int linenum, char *line, LT_INS_S **ins, void *local)
1850 register char *lp, *up = NULL, *urlp = NULL,
1851 *weburlp = NULL, *mailurlp = NULL;
1852 int n, n1, n2, n3, l;
1853 char buf[256], color[256];
1854 HANDLE_S *h;
1855 URL_HILITE_S *uh;
1857 for(lp = line; ; lp = up + n){
1858 /* scan for all of them so we can choose the first */
1859 if(F_ON(F_VIEW_SEL_URL,ps_global))
1860 urlp = rfc1738_scan(lp, &n1);
1861 if(F_ON(F_VIEW_SEL_URL_HOST,ps_global))
1862 weburlp = web_host_scan(lp, &n2);
1863 if(F_ON(F_SCAN_ADDR,ps_global))
1864 mailurlp = mail_addr_scan(lp, &n3);
1866 if(urlp || weburlp || mailurlp){
1867 up = urlp ? urlp :
1868 weburlp ? weburlp : mailurlp;
1869 if(up == urlp && weburlp && weburlp < up)
1870 up = weburlp;
1871 if(mailurlp && mailurlp < up)
1872 up = mailurlp;
1874 if(up == urlp){
1875 n = n1;
1876 weburlp = mailurlp = NULL;
1878 else if(up == weburlp){
1879 n = n2;
1880 mailurlp = NULL;
1882 else{
1883 n = n3;
1884 weburlp = NULL;
1887 else
1888 break;
1890 uh = (URL_HILITE_S *) local;
1892 h = new_handle(uh->handlesp);
1893 h->type = URL;
1894 h->h.url.path = (char *) fs_get((n + 10) * sizeof(char));
1895 snprintf(h->h.url.path, n+10, "%s%.*s",
1896 weburlp ? "http://" : (mailurlp ? "mailto:" : ""), n, up);
1897 h->h.url.path[n+10-1] = '\0';
1899 if(handle_start_color(color, sizeof(color), &l, uh->hdr_color))
1900 ins = gf_line_test_new_ins(ins, up, color, l);
1901 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global))
1902 ins = gf_line_test_new_ins(ins, up, url_embed(TAG_BOLDON), 2);
1904 buf[0] = TAG_EMBED;
1905 buf[1] = TAG_HANDLE;
1906 snprintf(&buf[3], sizeof(buf)-3, "%d", h->key);
1907 buf[sizeof(buf)-1] = '\0';
1908 buf[2] = strlen(&buf[3]);
1909 ins = gf_line_test_new_ins(ins, up, buf, (int) buf[2] + 3);
1911 /* in case it was the current selection */
1912 ins = gf_line_test_new_ins(ins, up + n, url_embed(TAG_INVOFF), 2);
1914 if(scroll_handle_end_color(color, sizeof(color), &l, uh->hdr_color))
1915 ins = gf_line_test_new_ins(ins, up + n, color, l);
1916 else
1917 ins = gf_line_test_new_ins(ins, up + n, url_embed(TAG_BOLDOFF), 2);
1919 urlp = weburlp = mailurlp = NULL;
1922 return(0);
1927 url_hilite_hdr(long int linenum, char *line, LT_INS_S **ins, void *local)
1929 static int check_for_urls = 0;
1930 register char *lp;
1932 if(isspace((unsigned char)*line)) /* continuation, check or not
1933 depending on last line */
1934 lp = line;
1935 else{
1936 check_for_urls = 0;
1937 if((lp = strchr(line, ':')) != NULL){ /* there ought to always be a colon */
1938 FieldType ft;
1940 *lp = '\0';
1942 if(((ft = pine_header_standard(line)) == FreeText
1943 || ft == Subject
1944 || ft == TypeUnknown)
1945 && strucmp(line, "message-id")
1946 && strucmp(line, "newsgroups")
1947 && strucmp(line, "references")
1948 && strucmp(line, "in-reply-to")
1949 && strucmp(line, "received")
1950 && strucmp(line, "date")){
1951 check_for_urls = 1;
1954 *lp = ':';
1958 if(check_for_urls)
1959 (void) url_hilite(linenum, lp + 1, ins, local);
1961 return(0);
1966 pad_to_right_edge(long int linenum, char *line, LT_INS_S **ins, void *local)
1968 char *p;
1969 int wid = 0;
1970 int total_wid;
1971 struct variable *vars = ps_global->vars;
1973 if(!line[0])
1974 return 0;
1976 total_wid = *((int *) local);
1978 /* calculate width of line */
1979 p = line;
1980 while(*p){
1982 switch(*p){
1983 case TAG_EMBED:
1984 p++;
1985 switch(*p){
1986 case TAG_HANDLE:
1987 p++;
1988 p += *p + 1; /* skip handle key */
1989 break;
1991 case TAG_FGCOLOR :
1992 case TAG_BGCOLOR :
1993 p += (RGBLEN + 1); /* 1 for TAG, RGBLEN for color */
1994 break;
1996 case TAG_INVON:
1997 case TAG_INVOFF:
1998 case TAG_BOLDON:
1999 case TAG_BOLDOFF:
2000 case TAG_ULINEON:
2001 case TAG_ULINEOFF:
2002 p++;
2003 break;
2005 default: /* literal embed char */
2006 break;
2009 break;
2011 case TAB:
2012 p++;
2013 while(((++wid) & 0x07) != 0) /* add tab's spaces */
2016 break;
2018 default:
2019 wid += width_at_this_position((unsigned char *) p, strlen(p));
2020 p++;
2021 break;
2025 if(total_wid > wid){
2026 ins = gf_line_test_new_ins(ins, line + strlen(line),
2027 color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
2028 VAR_HEADER_GENERAL_BACK_COLOR),
2029 (2 * RGBLEN) + 4);
2030 ins = gf_line_test_new_ins(ins, line+strlen(line),
2031 repeat_char(total_wid-wid, ' '), total_wid-wid);
2032 ins = gf_line_test_new_ins(ins, line + strlen(line),
2033 color_embed(VAR_NORM_FORE_COLOR,
2034 VAR_NORM_BACK_COLOR),
2035 (2 * RGBLEN) + 4);
2038 return(0);
2043 #define UES_LEN 12
2044 #define UES_MAX 32
2046 url_external_specific_handler(char *url, int len)
2048 static char list[UES_LEN * UES_MAX];
2050 if(url){
2051 char *p;
2052 int i;
2054 for(i = 0; i < UES_MAX && *(p = &list[i * UES_LEN]); i++)
2055 if(strlen(p) == len && !struncmp(p, url, len))
2056 return(1);
2058 else{ /* initialize! */
2059 char **l, *test, *cmd, *p, *p2;
2060 int i = 0, n;
2062 memset(list, 0, sizeof(list));
2063 for(l = ps_global->VAR_BROWSER ; l && *l; l++){
2064 get_pair(*l, &test, &cmd, 1, 1);
2066 if((p = srchstr(test, "_scheme(")) && (p2 = strstr(p+8, ")_"))){
2067 *p2 = '\0';
2069 for(p += 8; *p && i < UES_MAX; p += n)
2070 if((p2 = strchr(p, ',')) != NULL){
2071 if((n = p2 - p) < UES_LEN){
2072 strncpy(&list[i * UES_LEN], p, MIN(n, sizeof(list)-(i * UES_LEN)));
2073 i++;
2075 else
2076 dprint((1,
2077 "* * * HANDLER TOO LONG: %.*s\n", n,
2078 p ? p : "?"));
2080 n++;
2082 else{
2083 if(strlen(p) <= UES_LEN){
2084 strncpy(&list[i * UES_LEN], p, sizeof(list)-(i * UES_LEN));
2085 i++;
2088 break;
2092 if(test)
2093 fs_give((void **) &test);
2095 if(cmd)
2096 fs_give((void **) &cmd);
2100 return(0);
2105 url_imap_folder(char *true_url, char **folder, imapuid_t *uid_val,
2106 imapuid_t *uid, char **search, int silent)
2108 char *url, *scheme, *p, *cmd, *server = NULL,
2109 *user = NULL, *auth = NULL, *mailbox = NULL,
2110 *section = NULL;
2111 size_t l;
2112 int rv = URL_IMAP_ERROR;
2115 * Since we're planting nulls, operate on a temporary copy...
2117 scheme = silent ? NULL : "IMAP";
2118 url = cpystr(true_url + 7);
2120 /* Try to pick apart the "iserver" portion */
2121 if((cmd = strchr(url, '/')) != NULL){ /* iserver "/" [mailbox] ? */
2122 *cmd++ = '\0';
2124 else{
2125 dprint((2, "-- URL IMAP FOLDER: missing: %s\n",
2126 url ? url : "?"));
2127 cmd = &url[strlen(url)-1]; /* assume only iserver */
2130 if((p = strchr(url, '@')) != NULL){ /* user | auth | pass? */
2131 *p++ = '\0';
2132 server = rfc1738_str(p);
2134 /* only ";auth=*" supported (and also ";auth=anonymous") */
2135 if((p = srchstr(url, ";auth=")) != NULL){
2136 *p = '\0';
2137 auth = rfc1738_str(p + 6);
2140 if(*url)
2141 user = rfc1738_str(url);
2143 else
2144 server = rfc1738_str(url);
2146 if(!*server)
2147 return(url_bogus_imap(&url, scheme, "No server specified"));
2150 * "iserver" in hand, pick apart the "icommand"...
2152 p = NULL;
2153 if(!*cmd || (p = srchstr(cmd, ";type="))){
2154 char *criteria;
2157 * No "icommand" (all top-level folders) or "imailboxlist"...
2159 if(p){
2160 *p = '\0'; /* tie off criteria */
2161 criteria = rfc1738_str(cmd); /* get "enc_list_mailbox" */
2162 if(!strucmp(p = rfc1738_str(p+6), "lsub"))
2163 rv |= URL_IMAP_IMBXLSTLSUB;
2164 else if(strucmp(p, "list"))
2165 return(url_bogus_imap(&url, scheme,
2166 "Invalid list type specified"));
2168 else{
2169 rv |= URL_IMAP_ISERVERONLY;
2170 criteria = "";
2173 /* build folder list from specified server/criteria/list-method */
2174 l = strlen(server) + strlen(criteria) + 10 + (user ? (strlen(user)+2) : 9);
2175 *folder = (char *) fs_get((l+1) * sizeof(char));
2176 snprintf(*folder, l+1, "{%s/%s%s%s}%s%s%s", server,
2177 user ? "user=\"" : "Anonymous",
2178 user ? user : "",
2179 user ? "\"" : "",
2180 *criteria ? "[" : "", criteria, *criteria ? "[" : "");
2181 (*folder)[l] = '\0';
2182 rv |= URL_IMAP_IMAILBOXLIST;
2184 else{
2185 if((p = srchstr(cmd, "/;uid=")) != NULL){ /* "imessagepart" */
2186 *p = '\0'; /* tie off mailbox [uidvalidity] */
2187 if((section = srchstr(p += 6, "/;section=")) != NULL){
2188 *section = '\0'; /* tie off UID */
2189 section = rfc1738_str(section + 10);
2190 /* BUG: verify valid section spec ala rfc 2060 */
2191 dprint((2,
2192 "-- URL IMAP FOLDER: section not used: %s\n",
2193 section ? section : "?"));
2196 if(!(*uid = rfc1738_num(&p)) || *p) /* decode UID */
2197 return(url_bogus_imap(&url, scheme, "Invalid data in UID"));
2199 /* optional "uidvalidity"? */
2200 if((p = srchstr(cmd, ";uidvalidity=")) != NULL){
2201 *p = '\0';
2202 p += 13;
2203 if(!(*uid_val = rfc1738_num(&p)) || *p)
2204 return(url_bogus_imap(&url, scheme,
2205 "Invalid UIDVALIDITY"));
2208 mailbox = rfc1738_str(cmd);
2209 rv = URL_IMAP_IMESSAGEPART;
2211 else{ /* "imessagelist" */
2212 /* optional "uidvalidity"? */
2213 if((p = srchstr(cmd, ";uidvalidity=")) != NULL){
2214 *p = '\0';
2215 p += 13;
2216 if(!(*uid_val = rfc1738_num(&p)) || *p)
2217 return(url_bogus_imap(&url, scheme,
2218 "Invalid UIDVALIDITY"));
2221 /* optional "enc_search"? */
2222 if((p = strchr(cmd, '?')) != NULL){
2223 *p = '\0';
2224 if(search)
2225 *search = cpystr(rfc1738_str(p + 1));
2226 /* BUG: verify valid search spec ala rfc 2060 */
2229 mailbox = rfc1738_str(cmd);
2230 rv = URL_IMAP_IMESSAGELIST;
2233 if(auth && *auth != '*' && strucmp(auth, "anonymous"))
2234 q_status_message(SM_ORDER, 3, 3,
2235 "Unsupported authentication method. Using standard login.");
2238 * At this point our structure should contain the
2239 * digested url. Now put it together for c-client...
2241 l = strlen(server) + 8 + (mailbox ? strlen(mailbox) : 0)
2242 + (user ? (strlen(user)+2) : 9);
2243 *folder = (char *) fs_get((l+1) * sizeof(char));
2244 snprintf(*folder, l+1, "{%s%s%s%s%s}%s", server,
2245 (user || !(auth && strucmp(auth, "anonymous"))) ? "/" : "",
2246 user ? "user=\"" : ((auth && strucmp(auth, "anonymous")) ? "" : "Anonymous"),
2247 user ? user : "",
2248 user ? "\"" : "",
2249 mailbox);
2250 (*folder)[l] = '\0';
2253 fs_give((void **) &url);
2254 return(rv);
2259 url_bogus_imap(char **freeme, char *url, char *problem)
2261 fs_give((void **) freeme);
2262 (void) url_bogus(url, problem);
2263 return(URL_IMAP_ERROR);
2268 * url_bogus - report url syntax errors and such
2271 url_bogus(char *url, char *reason)
2273 dprint((2, "-- bogus url \"%s\": %s\n",
2274 url ? url : "<NULL URL>", reason ? reason : "?"));
2275 if(url)
2276 q_status_message3(SM_ORDER|SM_DING, 2, 3,
2277 "Malformed \"%.*s\" URL: %.200s",
2278 (void *) (strchr(url, ':') - url), url, reason);
2280 return(0);
2285 /*----------------------------------------------------------------------
2286 Format header text suitable for display
2288 Args: stream -- mail stream for various header text fetches
2289 msgno -- sequence number in stream of message we're interested in
2290 section -- which section of message
2291 env -- pointer to msg's envelope
2292 hdrs -- struct containing what's to get formatted
2293 prefix -- prefix to append to each output line
2294 handlesp -- address of pointer to the message's handles
2295 flags -- FM_ flags, see pith/mailview.h
2296 final_pc -- function to write header text with
2298 Result: 0 if all's well, -1 if write error, 1 if fetch error
2300 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2301 in the local convention.
2303 ----*/
2304 #define FHT_OK 0
2305 #define FHT_WRTERR -1
2306 #define FHT_FTCHERR 1
2308 format_header(MAILSTREAM *stream, long int msgno, char *section, ENVELOPE *env,
2309 HEADER_S *hdrs, char *prefix, HANDLE_S **handlesp, int flags,
2310 fmt_env_t fmt_env, gf_io_t final_pc)
2312 int rv = FHT_OK;
2313 int nfields, i;
2314 char *h = NULL, **fields = NULL, **v, *q, *start,
2315 *finish, *current;
2316 STORE_S *tmp_store;
2317 URL_HILITE_S uh;
2318 gf_io_t tmp_pc, tmp_gc;
2319 struct variable *vars = ps_global->vars;
2321 if((tmp_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL)
2322 gf_set_so_writec(&tmp_pc, tmp_store);
2323 else
2324 return(FHT_WRTERR);
2326 if(!fmt_env)
2327 fmt_env = format_envelope;
2329 if(ps_global->full_header == 2){
2330 rv = format_raw_header(stream, msgno, section, tmp_pc);
2332 else{
2334 * First, calculate how big a fields array we need.
2337 /* Custom header viewing list specified */
2338 if(hdrs->type == HD_LIST){
2339 /* view all these headers */
2340 if(!hdrs->except){
2341 for(nfields = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
2342 if(!is_an_env_hdr(q))
2343 nfields++;
2345 /* view all except these headers */
2346 else{
2347 for(nfields = 0, v = hdrs->h.l; *v != NULL; v++)
2348 nfields++;
2350 if(nfields > 1)
2351 nfields--; /* subtract one for ALL_EXCEPT field */
2354 else
2355 nfields = 6; /* default view */
2357 /* allocate pointer space */
2358 if(nfields){
2359 fields = (char **)fs_get((size_t)(nfields+1) * sizeof(char *));
2360 memset(fields, 0, (size_t)(nfields+1) * sizeof(char *));
2363 if(hdrs->type == HD_LIST){
2364 /* view all these headers */
2365 if(!hdrs->except){
2366 /* put the non-envelope headers in fields */
2367 if(nfields)
2368 for(i = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
2369 if(!is_an_env_hdr(q))
2370 fields[i++] = q;
2372 /* view all except these headers */
2373 else{
2374 /* put the list of headers not to view in fields */
2375 if(nfields)
2376 for(i = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
2377 if(strucmp(ALL_EXCEPT, q))
2378 fields[i++] = q;
2381 v = hdrs->h.l;
2383 else{
2384 if(nfields){
2385 fields[i = 0] = "Resent-Date";
2386 fields[++i] = "Resent-From";
2387 fields[++i] = "Resent-To";
2388 fields[++i] = "Resent-cc";
2389 fields[++i] = "Resent-Subject";
2392 v = fields;
2395 /* custom view with exception list */
2396 if(hdrs->type == HD_LIST && hdrs->except){
2398 * Go through each header in h and print it.
2399 * First we check to see if it is an envelope header so we
2400 * can print our envelope version of it instead of the raw version.
2403 /* fetch all the other headers */
2404 if(nfields)
2405 h = pine_fetchheader_lines_not(stream, msgno, section, fields);
2407 for(current = h;
2408 h && delineate_this_header(NULL, current, &start, &finish);
2409 current = finish){
2410 char tmp[MAILTMPLEN+1];
2411 char *colon_loc;
2413 colon_loc = strindex(start, ':');
2414 if(colon_loc && colon_loc < finish){
2415 strncpy(tmp, start, MIN(colon_loc-start, sizeof(tmp)-1));
2416 tmp[MIN(colon_loc-start, sizeof(tmp)-1)] = '\0';
2418 else
2419 colon_loc = NULL;
2421 if(colon_loc && is_an_env_hdr(tmp)){
2422 char *dummystart, *dummyfinish;
2425 * Pretty format for env hdrs.
2426 * If the same header appears more than once, only
2427 * print the last to avoid duplicates.
2428 * They should have been combined in the env when parsed.
2430 if(!delineate_this_header(tmp, current+1, &dummystart,
2431 &dummyfinish))
2432 format_env_hdr(stream, msgno, section, env,
2433 fmt_env, tmp_pc, tmp, hdrs->charset, flags);
2435 else{
2436 if((rv = format_raw_hdr_string(start, finish, tmp_pc,
2437 hdrs->charset, flags)) != 0)
2438 goto write_error;
2439 else
2440 start = finish;
2444 /* custom view or default */
2445 else{
2446 /* fetch the non-envelope headers */
2447 if(nfields)
2448 h = pine_fetchheader_lines(stream, msgno, section, fields);
2450 /* default envelope for default view */
2451 if(hdrs->type == HD_BFIELD)
2452 (*fmt_env)(stream, msgno, section, env, tmp_pc, hdrs->h.b, hdrs->charset, flags);
2454 /* go through each header in list, v initialized above */
2455 for(; (q = *v) != NULL; v++){
2456 if(is_an_env_hdr(q)){
2457 /* pretty format for env hdrs */
2458 format_env_hdr(stream, msgno, section, env,
2459 fmt_env, tmp_pc, q, hdrs->charset, flags);
2461 else{
2463 * Go through h finding all occurrences of this header
2464 * and all continuation lines, and output.
2466 for(current = h;
2467 h && delineate_this_header(q,current,&start,&finish);
2468 current = finish){
2469 if((rv = format_raw_hdr_string(start, finish, tmp_pc,
2470 hdrs->charset, flags)) != 0)
2471 goto write_error;
2472 else
2473 start = finish;
2481 write_error:
2483 gf_clear_so_writec(tmp_store);
2485 if(!rv){ /* valid data? Do wrapping and filtering... */
2486 int column;
2487 char *errstr, *display_filter = NULL, trigger[MAILTMPLEN];
2488 STORE_S *df_store = NULL;
2490 so_seek(tmp_store, 0L, 0);
2492 column = (flags & FM_DISPLAY) ? ps_global->ttyo->screen_cols : 80;
2495 * Test for and act on any display filter
2496 * This barely makes sense. The display filter is going
2497 * to be getting UTF-8'ized headers here. In pre-alpine
2498 * pine the display filter was being fed already decoded
2499 * headers in whatever character set they were in.
2500 * The good news is that that didn't make much
2501 * sense either, so this shouldn't break anything.
2502 * It seems unlikely that anybody is doing anything useful
2503 * with the header part of display filters.
2505 if(ps_global->tools.display_filter
2506 && ps_global->tools.display_filter_trigger
2507 && (display_filter = (*ps_global->tools.display_filter_trigger)(NULL, trigger, sizeof(trigger)))){
2508 if((df_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
2510 gf_set_so_writec(&tmp_pc, df_store);
2511 gf_set_so_readc(&tmp_gc, df_store);
2512 if((errstr = (*ps_global->tools.display_filter)(display_filter, tmp_store, tmp_pc, NULL)) != NULL){
2513 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2514 _("Formatting error: %s"), errstr);
2515 rv = FHT_WRTERR;
2517 else
2518 so_seek(df_store, 0L, 0);
2520 gf_clear_so_writec(df_store);
2522 else{
2523 q_status_message(SM_ORDER | SM_DING, 3, 3,
2524 "No space for filtered text.");
2525 rv = FHT_WRTERR;
2528 else{
2529 so_seek(tmp_store, 0L, 0);
2530 gf_set_so_readc(&tmp_gc, tmp_store);
2533 if(!rv){
2534 int *margin, wrapflags = GFW_ONCOMMA;
2536 gf_filter_init();
2537 gf_link_filter(gf_local_nvtnl, NULL);
2539 if((F_ON(F_VIEW_SEL_URL, ps_global)
2540 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
2541 || F_ON(F_SCAN_ADDR, ps_global))
2542 && handlesp){
2543 gf_link_filter(gf_line_test,
2544 gf_line_test_opt(url_hilite_hdr,
2545 gf_url_hilite_opt(&uh,handlesp,1)));
2546 wrapflags |= GFW_HANDLES;
2549 if((flags & FM_DISPLAY)
2550 && !(flags & FM_NOCOLOR)
2551 && pico_usingcolor()
2552 && ((VAR_NORM_FORE_COLOR
2553 && VAR_HEADER_GENERAL_FORE_COLOR
2554 && colorcmp(VAR_NORM_FORE_COLOR, VAR_HEADER_GENERAL_FORE_COLOR))
2556 (VAR_NORM_BACK_COLOR
2557 && VAR_HEADER_GENERAL_BACK_COLOR
2558 && colorcmp(VAR_NORM_BACK_COLOR, VAR_HEADER_GENERAL_BACK_COLOR))))
2559 wrapflags |= GFW_HDRCOLOR;
2561 if((flags & FM_DISPLAY)
2562 && !(flags & FM_NOCOLOR)
2563 && pico_usingcolor()
2564 && ps_global->hdr_colors){
2565 gf_link_filter(gf_line_test,
2566 gf_line_test_opt(color_headers, NULL));
2567 wrapflags |= (GFW_HANDLES | GFW_HDRCOLOR);
2570 if(prefix && *prefix)
2571 column = MAX(column-strlen(prefix), 50);
2573 margin = format_view_margin();
2575 if(!(flags & FM_NOWRAP))
2576 gf_link_filter(gf_wrap,
2577 gf_wrap_filter_opt(column, column,
2578 (flags & FM_NOINDENT) ? NULL : margin,
2579 4, wrapflags));
2581 if(prefix && *prefix)
2582 gf_link_filter(gf_prefix, gf_prefix_opt(prefix));
2584 if((flags & FM_DISPLAY)
2585 && !(flags & FM_NOCOLOR)
2586 && pico_usingcolor()
2587 && ((VAR_NORM_FORE_COLOR
2588 && VAR_HEADER_GENERAL_FORE_COLOR
2589 && colorcmp(VAR_NORM_FORE_COLOR, VAR_HEADER_GENERAL_FORE_COLOR))
2591 (VAR_NORM_BACK_COLOR
2592 && VAR_HEADER_GENERAL_BACK_COLOR
2593 && colorcmp(VAR_NORM_BACK_COLOR, VAR_HEADER_GENERAL_BACK_COLOR)))){
2594 int right_margin;
2595 int total_wid;
2597 right_margin = margin ? margin[1] : 0;
2598 total_wid = column - right_margin;
2600 gf_link_filter(gf_line_test,
2601 gf_line_test_opt(pad_to_right_edge, (void *) &total_wid));
2602 wrapflags |= GFW_HANDLES;
2605 gf_link_filter(gf_nvtnl_local, NULL);
2607 if((errstr = gf_pipe(tmp_gc, final_pc)) != NULL){
2608 rv = FHT_WRTERR;
2609 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2610 "Can't build header : %.200s", errstr);
2614 if(df_store){
2615 gf_clear_so_readc(df_store);
2616 so_give(&df_store);
2618 else
2619 gf_clear_so_readc(tmp_store);
2622 so_give(&tmp_store);
2624 if(h)
2625 fs_give((void **)&h);
2627 if(fields)
2628 fs_give((void **)&fields);
2630 return(rv);
2634 /*----------------------------------------------------------------------
2635 Format RAW header text for display
2637 Args: stream -- mail stream for various header text fetches
2638 rawno -- sequence number in stream of message we're interested in
2639 section -- which section of message
2640 pc -- function to write header text with
2642 Result: 0 if all's well, -1 if write error, 1 if fetch error
2644 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2645 in the local convention.
2647 ----*/
2649 format_raw_header(MAILSTREAM *stream, long int msgno, char *section, gf_io_t pc)
2651 char *h = mail_fetch_header(stream, msgno, section, NULL, NULL, FT_PEEK);
2652 unsigned char c;
2654 if(h){
2655 while(*h){
2656 if(ISRFCEOL(h)){
2657 h += 2;
2658 if(!gf_puts(NEWLINE, pc))
2659 return(FHT_WRTERR);
2661 if(ISRFCEOL(h)) /* all done! */
2662 return(FHT_OK);
2664 else if((unsigned char)(*h) < 0x80 && FILTER_THIS(*h) &&
2665 !(*(h+1) && *h == ESCAPE && match_escapes(h+1))){
2666 c = (unsigned char) *h++;
2667 if(!((*pc)(c >= 0x80 ? '~' : '^')
2668 && (*pc)((c == 0x7f) ? '?' : (c & 0x1f) + '@')))
2669 return(FHT_WRTERR);
2671 else if(!(*pc)(*h++))
2672 return(FHT_WRTERR);
2675 else
2676 return(FHT_FTCHERR);
2678 return(FHT_OK);
2683 /*----------------------------------------------------------------------
2684 Format c-client envelope data suitable for display
2686 Args: s -- mail stream for various header text fetches
2687 n -- raw sequence number in stream of message we're interested in
2688 sect -- which section of message
2689 e -- pointer to msg's envelope
2690 pc -- function to write header text with
2691 which -- which header lines to write
2692 oacs --
2693 flags -- FM_ flags, see pith/mailview.h
2695 Result: 0 if all's well, -1 if write error, 1 if fetch error
2697 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2698 in the local convention.
2700 ----*/
2701 void
2702 format_envelope(MAILSTREAM *s, long int n, char *sect, ENVELOPE *e, gf_io_t pc,
2703 long int which, char *oacs, int flags)
2705 char *q, *p2, buftmp[MAILTMPLEN];
2707 if(!e)
2708 return;
2710 if((which & FE_DATE) && e->date) {
2711 q = "Date: ";
2712 snprintf(buftmp, sizeof(buftmp), "%s",
2713 F_ON(F_DATES_TO_LOCAL,ps_global)
2714 ? convert_date_to_local((char *) e->date) : (char *) e->date);
2715 buftmp[sizeof(buftmp)-1] = '\0';
2716 p2 = (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
2717 SIZEOF_20KBUF, buftmp);
2718 gf_puts(q, pc);
2719 format_env_puts(p2, pc);
2720 gf_puts(NEWLINE, pc);
2723 if((which & FE_FROM) && e->from)
2724 format_addr_string(s, n, sect, "From: ", e->from, flags, oacs, pc);
2726 if((which & FE_REPLYTO) && e->reply_to
2727 && (!e->from || !address_is_same(e->reply_to, e->from)))
2728 format_addr_string(s, n, sect, "Reply-To: ", e->reply_to, flags, oacs, pc);
2730 if((which & FE_TO) && e->to)
2731 format_addr_string(s, n, sect, "To: ", e->to, flags, oacs, pc);
2733 if((which & FE_CC) && e->cc)
2734 format_addr_string(s, n, sect, "Cc: ", e->cc, flags, oacs, pc);
2736 if((which & FE_BCC) && e->bcc)
2737 format_addr_string(s, n, sect, "Bcc: ", e->bcc, flags, oacs, pc);
2739 if((which & FE_RETURNPATH) && e->return_path)
2740 format_addr_string(s, n, sect, "Return-Path: ", e->return_path,
2741 flags, oacs, pc);
2743 if((which & FE_NEWSGROUPS) && e->newsgroups){
2744 int bogus = NIL;
2745 format_newsgroup_string("Newsgroups: ", e->newsgroups, flags, pc);
2746 if (!e->ngpathexists && e->message_id &&
2747 strncmp (e->message_id,"<alpine.",8) &&
2748 strncmp (e->message_id,"<Pine.",6) &&
2749 strncmp (e->message_id,"<MS-C.",6) &&
2750 strncmp (e->message_id,"<MailManager.",13) &&
2751 strncmp (e->message_id,"<EasyMail.",11) &&
2752 strncmp (e->message_id,"<ML-",4)) bogus = T;
2754 if(bogus)
2755 q_status_message(SM_ORDER, 0, 3,
2756 "Unverified Newsgroup header -- Message MAY or MAY NOT have been posted");
2759 if((which & FE_FOLLOWUPTO) && e->followup_to)
2760 format_newsgroup_string("Followup-To: ", e->followup_to, flags, pc);
2762 if((which & FE_SUBJECT) && e->subject && e->subject[0]){
2763 char *freeme = NULL;
2765 q = "Subject: ";
2766 gf_puts(q, pc);
2768 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->subject), SIZEOF_20KBUF-10000);
2770 if(flags & FM_DISPLAY
2771 && (ps_global->display_keywords_in_subject
2772 || ps_global->display_keywordinits_in_subject)){
2774 /* don't bother if no keywords are defined */
2775 if(some_user_flags_defined(s))
2776 p2 = freeme = prepend_keyword_subject(s, n, p2,
2777 ps_global->display_keywords_in_subject ? KW : KWInit,
2778 NULL, ps_global->VAR_KW_BRACES);
2781 format_env_puts(p2, pc);
2783 if(freeme)
2784 fs_give((void **) &freeme);
2786 gf_puts(NEWLINE, pc);
2789 if((which & FE_SENDER) && e->sender
2790 && (!e->from || !address_is_same(e->sender, e->from)))
2791 format_addr_string(s, n, sect, "Sender: ", e->sender, flags, oacs, pc);
2793 if((which & FE_MESSAGEID) && e->message_id){
2794 q = "Message-ID: ";
2795 gf_puts(q, pc);
2796 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->message_id), SIZEOF_20KBUF-10000);
2797 format_env_puts(p2, pc);
2798 gf_puts(NEWLINE, pc);
2801 if((which & FE_INREPLYTO) && e->in_reply_to){
2802 q = "In-Reply-To: ";
2803 gf_puts(q, pc);
2804 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);
2805 format_env_puts(p2, pc);
2806 gf_puts(NEWLINE, pc);
2809 if((which & FE_REFERENCES) && e->references) {
2810 q = "References: ";
2811 gf_puts(q, pc);
2812 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->references), SIZEOF_20KBUF-10000);
2813 format_env_puts(p2, pc);
2814 gf_puts(NEWLINE, pc);
2822 * The argument fieldname is something like "Subject:..." or "Subject".
2823 * Look through the specs in speccolor for a match of the fieldname,
2824 * and return the color that goes with any match, or NULL.
2825 * Caller should free the color.
2827 COLOR_PAIR *
2828 hdr_color(char *fieldname, char *value, SPEC_COLOR_S *speccolor)
2830 SPEC_COLOR_S *hc = NULL;
2831 COLOR_PAIR *color_pair = NULL;
2832 char *colon, *fname;
2833 char fbuf[FBUF_LEN+1];
2834 int gotit;
2835 PATTERN_S *pat;
2837 colon = strindex(fieldname, ':');
2838 if(colon){
2839 strncpy(fbuf, fieldname, MIN(colon-fieldname,FBUF_LEN));
2840 fbuf[MIN(colon-fieldname,FBUF_LEN)] = '\0';
2841 fname = fbuf;
2843 else
2844 fname = fieldname;
2846 if(fname && *fname)
2847 for(hc = speccolor; hc; hc = hc->next)
2848 if(hc->spec && !strucmp(fname, hc->spec)){
2849 if(!hc->val)
2850 break;
2852 gotit = 0;
2853 for(pat = hc->val; !gotit && pat; pat = pat->next)
2854 if(srchstr(value, pat->substring))
2855 gotit++;
2857 if(gotit)
2858 break;
2861 if(hc && hc->fg && hc->fg[0] && hc->bg && hc->bg[0])
2862 color_pair = new_color_pair(hc->fg, hc->bg);
2864 return(color_pair);
2869 * The argument fieldname is something like "Subject:..." or "Subject".
2870 * Look through the specs in hdr_colors for a match of the fieldname,
2871 * and return 1 if that fieldname is in one of the patterns, 0 otherwise.
2874 any_hdr_color(char *fieldname)
2876 SPEC_COLOR_S *hc = NULL;
2877 char *colon, *fname;
2878 char fbuf[FBUF_LEN+1];
2880 colon = strindex(fieldname, ':');
2881 if(colon){
2882 strncpy(fbuf, fieldname, MIN(colon-fieldname,FBUF_LEN));
2883 fbuf[MIN(colon-fieldname,FBUF_LEN)] = '\0';
2884 fname = fbuf;
2886 else
2887 fname = fieldname;
2889 if(fname && *fname)
2890 for(hc = ps_global->hdr_colors; hc; hc = hc->next)
2891 if(hc->spec && !strucmp(fname, hc->spec))
2892 break;
2894 return(hc ? 1 : 0);
2898 /*----------------------------------------------------------------------
2899 Format an address field, wrapping lines nicely at commas
2901 Args: field_name -- The name of the field we're formatting ("TO: ", ...)
2902 addr -- ADDRESS structure to format
2904 Result: A formatted, malloced string is returned.
2905 ----------------------------------------------------------------------*/
2906 void
2907 format_addr_string(MAILSTREAM *stream, long int msgno, char *section, char *field_name,
2908 struct mail_address *addr, int flags, char *oacs, gf_io_t pc)
2910 char *ptmp, *mtmp = NULL;
2911 int trailing = 0, group = 0;
2912 ADDRESS *atmp;
2914 if(!addr)
2915 return;
2918 * quickly run down address list to make sure none are patently bogus.
2919 * If so, just blat raw field out.
2921 for(atmp = addr; stream && atmp; atmp = atmp->next)
2922 if(atmp->host && atmp->host[0] == '.'){
2923 char *field, *fields[2];
2925 fields[1] = NULL;
2926 fields[0] = cpystr(field_name);
2927 if((ptmp = strchr(fields[0], ':')) != NULL)
2928 *ptmp = '\0';
2930 if((field = pine_fetchheader_lines(stream, msgno, section, fields)) != NULL){
2931 char *h, *t;
2933 for(t = h = field; *h ; t++)
2934 if(*t == '\015' && *(t+1) == '\012'){
2935 *t = '\0'; /* tie off line */
2936 format_env_puts(h, pc);
2937 if(*(h = (++t) + 1)) /* set new h and skip CRLF */
2938 gf_puts(NEWLINE, pc); /* more to write */
2939 else
2940 break;
2942 else if(!*t){ /* shouldn't happen much */
2943 if(h != t)
2944 format_env_puts(h, pc);
2946 break;
2949 fs_give((void **)&field);
2952 fs_give((void **)&fields[0]);
2953 gf_puts(NEWLINE, pc);
2954 dprint((2, "Error in \"%s\" field address\n",
2955 field_name ? field_name : "?"));
2956 return;
2959 gf_puts(field_name, pc);
2961 while(addr){
2962 atmp = addr->next; /* remember what's next */
2963 addr->next = NULL;
2964 if(!addr->host && addr->mailbox){
2965 mtmp = addr->mailbox;
2966 addr->mailbox = cpystr((char *)rfc1522_decode_to_utf8(
2967 (unsigned char *)tmp_20k_buf,
2968 SIZEOF_20KBUF, addr->mailbox));
2971 ptmp = addr->personal; /* RFC 1522 personal name? */
2972 addr->personal = iutf8ncpy((char *)tmp_20k_buf, (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+10000), SIZEOF_20KBUF-10000, addr->personal), 10000);
2973 tmp_20k_buf[10000-1] = '\0';
2975 if(!trailing) /* 1st pass, just address */
2976 trailing++;
2977 else{ /* else comma, unless */
2978 if(!((group == 1 && addr->host) /* 1st addr in group, */
2979 || (!addr->host && !addr->mailbox))){ /* or end of group */
2980 gf_puts(",", pc);
2981 #if 0
2982 gf_puts(NEWLINE, pc); /* ONE address/line please */
2983 gf_puts(" ", pc);
2984 #endif
2987 gf_puts(" ", pc);
2990 pine_rfc822_write_address_noquote(addr, pc, &group);
2992 addr->personal = ptmp; /* restore old personal ptr */
2993 if(!addr->host && addr->mailbox){
2994 fs_give((void **)&addr->mailbox);
2995 addr->mailbox = mtmp;
2998 addr->next = atmp;
2999 addr = atmp;
3002 gf_puts(NEWLINE, pc);
3008 const char *rspecials_minus_quote_and_dot = "()<>@,;:\\[]";
3009 /* RFC822 continuation, must start with CRLF */
3010 #define RFC822CONT "\015\012 "
3012 /* Write RFC822 address with some quoting turned off.
3013 * Accepts:
3014 * address to interpret
3016 * (This is a copy of c-client's rfc822_write_address except
3017 * we don't quote double quote and dot in personal names. It writes
3018 * to a gf_io_t instead of to a buffer so that we don't have to worry
3019 * about fixed sized buffer overflowing. It's also special cased to deal
3020 * with only a single address.)
3022 * The idea is that there are some places where we'd just like to display
3023 * the personal name as is before applying confusing quoting. However,
3024 * we do want to be careful not to break things that should be quoted so
3025 * we'll only use this where we are sure. Quoting may look ugly but it
3026 * doesn't usually break anything.
3028 void
3029 pine_rfc822_write_address_noquote(struct mail_address *adr, gf_io_t pc, int *group)
3031 extern const char *rspecials;
3033 if (adr->host) { /* ordinary address? */
3034 if (!(adr->personal || adr->adl)) pine_rfc822_address (adr, pc);
3035 else { /* no, must use phrase <route-addr> form */
3036 if (adr->personal)
3037 pine_rfc822_cat (adr->personal, rspecials_minus_quote_and_dot, pc);
3039 gf_puts(" <", pc); /* write address delimiter */
3040 pine_rfc822_address(adr, pc);
3041 gf_puts (">", pc); /* closing delimiter */
3044 if(*group)
3045 (*group)++;
3047 else if (adr->mailbox) { /* start of group? */
3048 /* yes, write group name */
3049 pine_rfc822_cat (adr->mailbox, rspecials, pc);
3051 gf_puts (": ", pc); /* write group identifier */
3052 *group = 1; /* in a group */
3054 else if (*group) { /* must be end of group (but be paranoid) */
3055 gf_puts (";", pc);
3056 *group = 0; /* no longer in that group */
3061 /* Write RFC822 route-address to string
3062 * Accepts:
3063 * address to interpret
3066 void
3067 pine_rfc822_address(struct mail_address *adr, gf_io_t pc)
3069 extern char *wspecials;
3071 if (adr && adr->host) { /* no-op if no address */
3072 if (adr->adl) { /* have an A-D-L? */
3073 gf_puts (adr->adl, pc);
3074 gf_puts (":", pc);
3076 /* write mailbox name */
3077 pine_rfc822_cat (adr->mailbox, wspecials, pc);
3078 if (*adr->host != '@') { /* unless null host (HIGHLY discouraged!) */
3079 gf_puts ("@", pc); /* host delimiter */
3080 gf_puts (adr->host, pc); /* write host name */
3086 /* Concatenate RFC822 string
3087 * Accepts:
3088 * pointer to string to concatenate
3089 * list of special characters
3092 void
3093 pine_rfc822_cat(char *src, const char *specials, gf_io_t pc)
3095 char *s;
3097 if (strpbrk (src,specials)) { /* any specials present? */
3098 gf_puts ("\"", pc); /* opening quote */
3099 /* truly bizarre characters in there? */
3100 while ((s = strpbrk (src,"\\\"")) != NULL) {
3101 char save[2];
3103 /* turn it into a null-terminated piece */
3104 save[0] = *s;
3105 save[1] = '\0';
3106 *s = '\0';
3107 gf_puts (src, pc); /* yes, output leader */
3108 *s = save[0];
3109 gf_puts ("\\", pc); /* quoting */
3110 gf_puts (save, pc); /* output the bizarre character */
3111 src = ++s; /* continue after the bizarre character */
3113 if (*src) gf_puts (src, pc);/* output non-bizarre string */
3114 gf_puts ("\"", pc); /* closing quote */
3116 else gf_puts (src, pc); /* otherwise it's the easy case */
3120 /*----------------------------------------------------------------------
3121 Format an address field, wrapping lines nicely at commas
3123 Args: field_name -- The name of the field we're formatting ("TO:", Cc:...)
3124 newsgrps -- ADDRESS structure to format
3126 Result: A formatted, malloced string is returned.
3128 The resulting lines formatted are 80 columns wide.
3129 ----------------------------------------------------------------------*/
3130 void
3131 format_newsgroup_string(char *field_name, char *newsgrps, int flags, gf_io_t pc)
3133 char buf[MAILTMPLEN];
3134 int trailing = 0, llen, alen;
3135 char *next_ng;
3137 if(!newsgrps || !*newsgrps)
3138 return;
3140 gf_puts(field_name, pc);
3142 llen = strlen(field_name);
3143 while(*newsgrps){
3144 for(next_ng = newsgrps; *next_ng && *next_ng != ','; next_ng++);
3145 strncpy(buf, newsgrps, MIN(next_ng - newsgrps, sizeof(buf)-1));
3146 buf[MIN(next_ng - newsgrps, sizeof(buf)-1)] = '\0';
3147 newsgrps = next_ng;
3148 if(*newsgrps)
3149 newsgrps++;
3150 alen = strlen(buf);
3151 if(!trailing){ /* first time thru, just address */
3152 llen += alen;
3153 trailing++;
3155 else{ /* else preceding comma */
3156 gf_puts(",", pc);
3157 llen++;
3159 if(alen + llen + 1 > 76){
3160 gf_puts(NEWLINE, pc);
3161 gf_puts(" ", pc);
3162 llen = alen + 5;
3164 else{
3165 gf_puts(" ", pc);
3166 llen += alen + 1;
3170 if(alen && llen > 76){ /* handle long addresses */
3171 register char *q, *p = &buf[alen-1];
3173 while(p > buf){
3174 if(isspace((unsigned char)*p)
3175 && (llen - (alen - (int)(p - buf))) < 76){
3176 for(q = buf; q < p; q++)
3177 (*pc)(*q); /* write character */
3179 gf_puts(NEWLINE, pc);
3180 gf_puts(" ", pc);
3181 gf_puts(p, pc);
3182 break;
3184 else
3185 p--;
3188 if(p == buf) /* no reasonable break point */
3189 gf_puts(buf, pc);
3191 else
3192 gf_puts(buf, pc);
3195 gf_puts(NEWLINE, pc);
3200 /*----------------------------------------------------------------------
3201 Format a text field that's part of some raw (non-envelope) message header
3203 Args: start --
3204 finish --
3205 pc --
3207 Result: Semi-digested text (RFC 1522 decoded, anyway) written with "pc"
3209 ----------------------------------------------------------------------*/
3211 format_raw_hdr_string(char *start, char *finish, gf_io_t pc, char *oacs, int flags)
3213 register char *current;
3214 unsigned char *p, *tmp = NULL, c;
3215 size_t n, len;
3216 char ch;
3217 int rv = FHT_OK;
3219 ch = *finish;
3220 *finish = '\0';
3222 if((n = 4*(finish-start)) > SIZEOF_20KBUF-1){
3223 len = n+1;
3224 p = tmp = (unsigned char *) fs_get(len * sizeof(unsigned char));
3226 else{
3227 len = SIZEOF_20KBUF;
3228 p = (unsigned char *) tmp_20k_buf;
3231 if(islower((unsigned char)(*start)))
3232 *start = toupper((unsigned char)(*start));
3234 current = (char *) rfc1522_decode_to_utf8(p, len, start);
3236 /* output from start to finish */
3237 while(*current && rv == FHT_OK)
3238 if(ISRFCEOL(current)){
3239 if(!gf_puts(NEWLINE, pc))
3240 rv = FHT_WRTERR;
3242 current += 2;
3244 else if((unsigned char)(*current) < 0x80 && FILTER_THIS(*current) &&
3245 !(*(current+1) && *current == ESCAPE && match_escapes(current+1))){
3246 c = (unsigned char) *current++;
3247 if(!((*pc)(c >= 0x80 ? '~' : '^')
3248 && (*pc)((c == 0x7f) ? '?' : (c & 0x1f) + '@')))
3249 rv = FHT_WRTERR;
3251 else if(!(*pc)(*current++))
3252 rv = FHT_WRTERR;
3254 if(tmp)
3255 fs_give((void **) &tmp);
3257 *finish = ch;
3259 return(rv);
3265 /*----------------------------------------------------------------------
3266 Format a text field that's part of some raw (non-envelope) message header
3268 Args: s --
3269 pc --
3271 Result: Output
3273 ----------------------------------------------------------------------*/
3275 format_env_puts(char *s, gf_io_t pc)
3277 if(ps_global->pass_ctrl_chars)
3278 return(gf_puts(s, pc));
3280 for(; *s; s++)
3281 if((unsigned char)(*s) < 0x80 && FILTER_THIS(*s) && !(*(s+1) && *s == ESCAPE && match_escapes(s+1))){
3282 if(!((*pc)((unsigned char) (*s) >= 0x80 ? '~' : '^')
3283 && (*pc)((*s == 0x7f) ? '?' : (*s & 0x1f) + '@')))
3284 return(0);
3286 else if(!(*pc)(*s))
3287 return(0);
3289 return(1);
3293 char *
3294 display_parameters(PARAMETER *params)
3296 int n, longest = 0;
3297 char *d, *printme;
3298 PARAMETER *p;
3299 PARMLIST_S *parmlist;
3301 for(p = params; p; p = p->next) /* ok if we include *'s */
3302 if(p->attribute && (n = strlen(p->attribute)) > longest)
3303 longest = MIN(32, n); /* shouldn't be any bigger than 32 */
3305 d = tmp_20k_buf;
3306 tmp_20k_buf[0] = '\0';
3307 if((parmlist = rfc2231_newparmlist(params)) != NULL){
3308 n = 0; /* n overloaded */
3309 while(rfc2231_list_params(parmlist) && d < tmp_20k_buf + 10000){
3310 if(n++){
3311 snprintf(d, 10000-(d-tmp_20k_buf), "\n");
3312 tmp_20k_buf[10000-1] = '\0';
3313 d += strlen(d);
3316 if(parmlist->value){
3317 if(parmlist->attrib && strucmp(parmlist->attrib, "url") == 0){
3318 snprintf(printme = tmp_20k_buf + 11000, 1000, "%s", parmlist->value);
3319 sqzspaces(printme);
3321 else
3322 printme = strsquish(tmp_20k_buf + 11000, 1000, parmlist->value, 100);
3324 else
3325 printme = "";
3327 snprintf(d, 10000-(d-tmp_20k_buf), "%-*s: %s", longest,
3328 parmlist->attrib ? parmlist->attrib : "", printme);
3330 tmp_20k_buf[10000-1] = '\0';
3331 d += strlen(d);
3334 rfc2231_free_parmlist(&parmlist);
3337 return(tmp_20k_buf);
3341 /*----------------------------------------------------------------------
3342 Fetch the requested header fields from the msgno specified
3344 Args: stream -- mail stream of open folder
3345 msgno -- number of message to get header lines from
3346 fields -- array of pointers to desired fields
3348 Returns: allocated string containing matched header lines,
3349 NULL on error.
3350 ----*/
3351 char *
3352 pine_fetch_header(MAILSTREAM *stream, long int msgno, char *section, char **fields, long int flags)
3354 STRINGLIST *sl;
3355 char *p, *m, *h = NULL, *match = NULL, *free_this, tmp[MAILTMPLEN];
3356 char **pflds = NULL, **pp = NULL, **qq;
3359 * If the user misconfigures it is possible to have one of the fields
3360 * set to the empty string instead of a header name. We want to catch
3361 * that here instead of asking the server the nonsensical question.
3363 for(pp = fields ? &fields[0] : NULL; pp && *pp; pp++)
3364 if(!**pp)
3365 break;
3367 if(pp && *pp){ /* found an empty header field, fix it */
3368 pflds = copy_list_array(fields);
3369 for(pp = pflds; pp && *pp; pp++){
3370 if(!**pp){ /* scoot rest of the lines up */
3371 free_this = *pp;
3372 for(qq = pp; *qq; qq++)
3373 *qq = *(qq+1);
3375 if(free_this)
3376 fs_give((void **) &free_this);
3380 /* no headers to look for, return NULL */
3381 if(pflds && !*pflds && !(flags & FT_NOT)){
3382 free_list_array(&pflds);
3383 return(NULL);
3386 else
3387 pflds = fields;
3389 sl = (pflds && *pflds) ? new_strlst(pflds) : NULL; /* package up fields */
3390 h = mail_fetch_header(stream, msgno, section, sl, NULL, flags | FT_PEEK);
3391 if (sl)
3392 free_strlst(&sl);
3394 if(!h){
3395 if(pflds && pflds != fields)
3396 free_list_array(&pflds);
3398 return(NULL);
3401 while(find_field(&h, tmp, sizeof(tmp))){
3402 for(pp = &pflds[0]; *pp && strucmp(tmp, *pp); pp++)
3405 /* interesting field? */
3406 if((p = (flags & FT_NOT) ? ((*pp) ? NULL : tmp) : *pp) != NULL){
3408 * Hold off allocating space for matching fields until
3409 * we at least find one to copy...
3411 if(!match)
3412 match = m = fs_get(strlen(h) + strlen(p) + 1);
3414 while(*p) /* copy field name */
3415 *m++ = *p++;
3417 while(*h && (*m++ = *h++)) /* header includes colon */
3418 if(*(m-1) == '\n' && (*h == '\r' || !isspace((unsigned char)*h)))
3419 break;
3421 *m = '\0'; /* tie off match string */
3423 else{ /* no match, pass this field */
3424 while(*h && !(*h++ == '\n'
3425 && (*h == '\r' || !isspace((unsigned char)*h))))
3430 if(pflds && pflds != fields)
3431 free_list_array(&pflds);
3433 return(match ? match : cpystr(""));
3438 find_field(char **h, char *tmp, size_t ntmp)
3440 char *otmp = tmp;
3442 if(!h || !*h || !**h || isspace((unsigned char)**h))
3443 return(0);
3445 while(tmp-otmp<ntmp-1 && **h && **h != ':' && !isspace((unsigned char)**h))
3446 *tmp++ = *(*h)++;
3448 *tmp = '\0';
3449 return(1);
3453 static char *_last_embedded_fg_color, *_last_embedded_bg_color;
3457 embed_color(COLOR_PAIR *cp, gf_io_t pc)
3459 if(cp && cp->fg){
3460 if(_last_embedded_fg_color)
3461 fs_give((void **)&_last_embedded_fg_color);
3463 _last_embedded_fg_color = cpystr(cp->fg);
3465 if(!(pc && (*pc)(TAG_EMBED) && (*pc)(TAG_FGCOLOR) &&
3466 gf_puts(color_to_asciirgb(cp->fg), pc)))
3467 return 0;
3470 if(cp && cp->bg){
3471 if(_last_embedded_bg_color)
3472 fs_give((void **)&_last_embedded_bg_color);
3474 _last_embedded_bg_color = cpystr(cp->bg);
3476 if(!(pc && (*pc)(TAG_EMBED) && (*pc)(TAG_BGCOLOR) &&
3477 gf_puts(color_to_asciirgb(cp->bg), pc)))
3478 return 0;
3481 return 1;
3485 COLOR_PAIR *
3486 get_cur_embedded_color(void)
3488 COLOR_PAIR *ret;
3490 if(_last_embedded_fg_color && _last_embedded_bg_color)
3491 ret = new_color_pair(_last_embedded_fg_color, _last_embedded_bg_color);
3492 else
3493 ret = pico_get_cur_color();
3495 return(ret);
3499 void
3500 clear_cur_embedded_color(void)
3502 if(_last_embedded_fg_color)
3503 fs_give((void **)&_last_embedded_fg_color);
3505 if(_last_embedded_bg_color)
3506 fs_give((void **)&_last_embedded_bg_color);
3511 scroll_handle_start_color(char *colorstring, size_t buflen, int *len)
3513 *len = 0;
3515 if(pico_usingcolor()){
3516 char *fg = NULL, *bg = NULL, *s;
3518 if(ps_global->VAR_SLCTBL_FORE_COLOR
3519 && colorcmp(ps_global->VAR_SLCTBL_FORE_COLOR,
3520 ps_global->VAR_NORM_FORE_COLOR))
3521 fg = ps_global->VAR_SLCTBL_FORE_COLOR;
3523 if(ps_global->VAR_SLCTBL_BACK_COLOR
3524 && colorcmp(ps_global->VAR_SLCTBL_BACK_COLOR,
3525 ps_global->VAR_NORM_BACK_COLOR))
3526 bg = ps_global->VAR_SLCTBL_BACK_COLOR;
3528 if(bg || fg){
3529 COLOR_PAIR *tmp;
3532 * The blacks are just known good colors for
3533 * testing whether the other color is good.
3535 if((tmp = new_color_pair(fg ? fg : colorx(COL_BLACK),
3536 bg ? bg : colorx(COL_BLACK))) != NULL){
3537 if(pico_is_good_colorpair(tmp))
3538 for(s = color_embed(fg, bg);
3539 (*len) < buflen && (colorstring[*len] = *s);
3540 s++, (*len)++)
3543 free_color_pair(&tmp);
3547 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
3548 strncpy(colorstring + (*len), url_embed(TAG_BOLDON), MIN(3,buflen-(*len)));
3549 *len += 2;
3553 colorstring[buflen-1] = '\0';
3555 return(*len != 0);
3560 scroll_handle_end_color(char *colorstring, size_t buflen, int *len, int use_hdr_color)
3562 *len = 0;
3563 if(pico_usingcolor()){
3564 char *fg = NULL, *bg = NULL, *s;
3565 char *basefg = NULL, *basebg = NULL;
3567 basefg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_FORE_COLOR
3568 : ps_global->VAR_NORM_FORE_COLOR;
3569 basebg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_BACK_COLOR
3570 : ps_global->VAR_NORM_BACK_COLOR;
3573 * We need to change the fg and bg colors back even if they
3574 * are the same as the Normal Colors so that color_a_quote
3575 * will have a chance to pick up those colors as the ones to
3576 * switch to. We don't do this before the handle above so that
3577 * the quote color will flow into the selectable item when
3578 * the selectable item color is partly the same as the
3579 * normal color. That is, suppose the normal color was black on
3580 * cyan and the selectable color was blue on cyan, only a fg color
3581 * change. We preserve the only-a-fg-color-change in a quote by
3582 * letting the quote background color flow into the selectable text.
3584 if(ps_global->VAR_SLCTBL_FORE_COLOR)
3585 fg = basefg;
3587 if(ps_global->VAR_SLCTBL_BACK_COLOR)
3588 bg = basebg;
3590 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
3591 strncpy(colorstring, url_embed(TAG_BOLDOFF), MIN(3,buflen));
3592 *len = 2;
3595 if(fg || bg)
3596 for(s = color_embed(fg, bg); (*len) < buflen && (colorstring[*len] = *s); s++, (*len)++)
3600 colorstring[buflen-1] = '\0';
3602 return(*len != 0);
3607 * Helper routine that is of limited use.
3608 * We need to tally up the screen width of
3609 * a UTF-8 string as we go through the string.
3610 * We just want the width of the character starting
3611 * at str (and no longer than remaining_octets).
3612 * If we're plopped into the middle of a UTF-8
3613 * character we just want to return width zero.
3616 width_at_this_position(unsigned char *str, unsigned long n)
3618 unsigned char *inputp = str;
3619 unsigned long remaining_octets = n;
3620 UCS ucs;
3621 int width = 0;
3623 ucs = (UCS) utf8_get(&inputp, &remaining_octets);
3624 if(!(ucs & U8G_ERROR || ucs == UBOGON)){
3625 width = wcellwidth(ucs);
3626 /* Writechar will print a '?' */
3627 if(width < 0)
3628 width = 1;
3631 return(width);