* Implement a different way to delete a password from the cache.
[alpine.git] / pith / mailview.c
blobc49d0ac20eb5eedf6889fa5a009c70bdf86da91e
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_o_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_o_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_o_t);
112 void format_envelope(MAILSTREAM *, long, char *, ENVELOPE *,
113 gf_o_t, long, char *, int);
114 int any_hdr_color(char *);
115 void format_addr_string(MAILSTREAM *, long, char *, char *,
116 ADDRESS *, int, char *, gf_o_t);
117 void pine_rfc822_write_address_noquote(ADDRESS *, gf_o_t, int *);
118 void format_newsgroup_string(char *, char *, int, gf_o_t);
119 int format_raw_hdr_string(char *, char *, gf_o_t, char *, int);
120 int format_env_puts(char *, gf_o_t);
121 int find_field(char **, char *, size_t);
122 int embed_color(COLOR_PAIR *, gf_o_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_o_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_o_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_o_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_o_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_o_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_i_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_o_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_o_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_o_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_o_t tmp_pc;
2319 gf_i_t tmp_gc;
2320 struct variable *vars = ps_global->vars;
2322 if((tmp_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL)
2323 gf_set_so_writec(&tmp_pc, tmp_store);
2324 else
2325 return(FHT_WRTERR);
2327 if(!fmt_env)
2328 fmt_env = format_envelope;
2330 if(ps_global->full_header == 2){
2331 rv = format_raw_header(stream, msgno, section, tmp_pc);
2333 else{
2335 * First, calculate how big a fields array we need.
2338 /* Custom header viewing list specified */
2339 if(hdrs->type == HD_LIST){
2340 /* view all these headers */
2341 if(!hdrs->except){
2342 for(nfields = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
2343 if(!is_an_env_hdr(q))
2344 nfields++;
2346 /* view all except these headers */
2347 else{
2348 for(nfields = 0, v = hdrs->h.l; *v != NULL; v++)
2349 nfields++;
2351 if(nfields > 1)
2352 nfields--; /* subtract one for ALL_EXCEPT field */
2355 else
2356 nfields = 6; /* default view */
2358 /* allocate pointer space */
2359 if(nfields){
2360 fields = (char **)fs_get((size_t)(nfields+1) * sizeof(char *));
2361 memset(fields, 0, (size_t)(nfields+1) * sizeof(char *));
2364 if(hdrs->type == HD_LIST){
2365 /* view all these headers */
2366 if(!hdrs->except){
2367 /* put the non-envelope headers in fields */
2368 if(nfields)
2369 for(i = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
2370 if(!is_an_env_hdr(q))
2371 fields[i++] = q;
2373 /* view all except these headers */
2374 else{
2375 /* put the list of headers not to view in fields */
2376 if(nfields)
2377 for(i = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
2378 if(strucmp(ALL_EXCEPT, q))
2379 fields[i++] = q;
2382 v = hdrs->h.l;
2384 else{
2385 if(nfields){
2386 fields[i = 0] = "Resent-Date";
2387 fields[++i] = "Resent-From";
2388 fields[++i] = "Resent-To";
2389 fields[++i] = "Resent-cc";
2390 fields[++i] = "Resent-Subject";
2393 v = fields;
2396 /* custom view with exception list */
2397 if(hdrs->type == HD_LIST && hdrs->except){
2399 * Go through each header in h and print it.
2400 * First we check to see if it is an envelope header so we
2401 * can print our envelope version of it instead of the raw version.
2404 /* fetch all the other headers */
2405 if(nfields)
2406 h = pine_fetchheader_lines_not(stream, msgno, section, fields);
2408 for(current = h;
2409 h && delineate_this_header(NULL, current, &start, &finish);
2410 current = finish){
2411 char tmp[MAILTMPLEN+1];
2412 char *colon_loc;
2414 colon_loc = strindex(start, ':');
2415 if(colon_loc && colon_loc < finish){
2416 strncpy(tmp, start, MIN(colon_loc-start, sizeof(tmp)-1));
2417 tmp[MIN(colon_loc-start, sizeof(tmp)-1)] = '\0';
2419 else
2420 colon_loc = NULL;
2422 if(colon_loc && is_an_env_hdr(tmp)){
2423 char *dummystart, *dummyfinish;
2426 * Pretty format for env hdrs.
2427 * If the same header appears more than once, only
2428 * print the last to avoid duplicates.
2429 * They should have been combined in the env when parsed.
2431 if(!delineate_this_header(tmp, current+1, &dummystart,
2432 &dummyfinish))
2433 format_env_hdr(stream, msgno, section, env,
2434 fmt_env, tmp_pc, tmp, hdrs->charset, flags);
2436 else{
2437 if((rv = format_raw_hdr_string(start, finish, tmp_pc,
2438 hdrs->charset, flags)) != 0)
2439 goto write_error;
2440 else
2441 start = finish;
2445 /* custom view or default */
2446 else{
2447 /* fetch the non-envelope headers */
2448 if(nfields)
2449 h = pine_fetchheader_lines(stream, msgno, section, fields);
2451 /* default envelope for default view */
2452 if(hdrs->type == HD_BFIELD)
2453 (*fmt_env)(stream, msgno, section, env, tmp_pc, hdrs->h.b, hdrs->charset, flags);
2455 /* go through each header in list, v initialized above */
2456 for(; (q = *v) != NULL; v++){
2457 if(is_an_env_hdr(q)){
2458 /* pretty format for env hdrs */
2459 format_env_hdr(stream, msgno, section, env,
2460 fmt_env, tmp_pc, q, hdrs->charset, flags);
2462 else{
2464 * Go through h finding all occurrences of this header
2465 * and all continuation lines, and output.
2467 for(current = h;
2468 h && delineate_this_header(q,current,&start,&finish);
2469 current = finish){
2470 if((rv = format_raw_hdr_string(start, finish, tmp_pc,
2471 hdrs->charset, flags)) != 0)
2472 goto write_error;
2473 else
2474 start = finish;
2482 write_error:
2484 gf_clear_so_writec(tmp_store);
2486 if(!rv){ /* valid data? Do wrapping and filtering... */
2487 int column;
2488 char *errstr, *display_filter = NULL, trigger[MAILTMPLEN];
2489 STORE_S *df_store = NULL;
2491 so_seek(tmp_store, 0L, 0);
2493 column = (flags & FM_DISPLAY) ? ps_global->ttyo->screen_cols : 80;
2496 * Test for and act on any display filter
2497 * This barely makes sense. The display filter is going
2498 * to be getting UTF-8'ized headers here. In pre-alpine
2499 * pine the display filter was being fed already decoded
2500 * headers in whatever character set they were in.
2501 * The good news is that that didn't make much
2502 * sense either, so this shouldn't break anything.
2503 * It seems unlikely that anybody is doing anything useful
2504 * with the header part of display filters.
2506 if(ps_global->tools.display_filter
2507 && ps_global->tools.display_filter_trigger
2508 && (display_filter = (*ps_global->tools.display_filter_trigger)(NULL, trigger, sizeof(trigger)))){
2509 if((df_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
2511 gf_set_so_writec(&tmp_pc, df_store);
2512 gf_set_so_readc(&tmp_gc, df_store);
2513 if((errstr = (*ps_global->tools.display_filter)(display_filter, tmp_store, tmp_pc, NULL)) != NULL){
2514 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2515 _("Formatting error: %s"), errstr);
2516 rv = FHT_WRTERR;
2518 else
2519 so_seek(df_store, 0L, 0);
2521 gf_clear_so_writec(df_store);
2523 else{
2524 q_status_message(SM_ORDER | SM_DING, 3, 3,
2525 "No space for filtered text.");
2526 rv = FHT_WRTERR;
2529 else{
2530 so_seek(tmp_store, 0L, 0);
2531 gf_set_so_readc(&tmp_gc, tmp_store);
2534 if(!rv){
2535 int *margin, wrapflags = GFW_ONCOMMA;
2537 gf_filter_init();
2538 gf_link_filter(gf_local_nvtnl, NULL);
2540 if((F_ON(F_VIEW_SEL_URL, ps_global)
2541 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
2542 || F_ON(F_SCAN_ADDR, ps_global))
2543 && handlesp){
2544 gf_link_filter(gf_line_test,
2545 gf_line_test_opt(url_hilite_hdr,
2546 gf_url_hilite_opt(&uh,handlesp,1)));
2547 wrapflags |= GFW_HANDLES;
2550 if((flags & FM_DISPLAY)
2551 && !(flags & FM_NOCOLOR)
2552 && pico_usingcolor()
2553 && ((VAR_NORM_FORE_COLOR
2554 && VAR_HEADER_GENERAL_FORE_COLOR
2555 && colorcmp(VAR_NORM_FORE_COLOR, VAR_HEADER_GENERAL_FORE_COLOR))
2557 (VAR_NORM_BACK_COLOR
2558 && VAR_HEADER_GENERAL_BACK_COLOR
2559 && colorcmp(VAR_NORM_BACK_COLOR, VAR_HEADER_GENERAL_BACK_COLOR))))
2560 wrapflags |= GFW_HDRCOLOR;
2562 if((flags & FM_DISPLAY)
2563 && !(flags & FM_NOCOLOR)
2564 && pico_usingcolor()
2565 && ps_global->hdr_colors){
2566 gf_link_filter(gf_line_test,
2567 gf_line_test_opt(color_headers, NULL));
2568 wrapflags |= (GFW_HANDLES | GFW_HDRCOLOR);
2571 if(prefix && *prefix)
2572 column = MAX(column-strlen(prefix), 50);
2574 margin = format_view_margin();
2576 if(!(flags & FM_NOWRAP))
2577 gf_link_filter(gf_wrap,
2578 gf_wrap_filter_opt(column, column,
2579 (flags & FM_NOINDENT) ? NULL : margin,
2580 4, wrapflags));
2582 if(prefix && *prefix)
2583 gf_link_filter(gf_prefix, gf_prefix_opt(prefix));
2585 if((flags & FM_DISPLAY)
2586 && !(flags & FM_NOCOLOR)
2587 && pico_usingcolor()
2588 && ((VAR_NORM_FORE_COLOR
2589 && VAR_HEADER_GENERAL_FORE_COLOR
2590 && colorcmp(VAR_NORM_FORE_COLOR, VAR_HEADER_GENERAL_FORE_COLOR))
2592 (VAR_NORM_BACK_COLOR
2593 && VAR_HEADER_GENERAL_BACK_COLOR
2594 && colorcmp(VAR_NORM_BACK_COLOR, VAR_HEADER_GENERAL_BACK_COLOR)))){
2595 int right_margin;
2596 int total_wid;
2598 right_margin = margin ? margin[1] : 0;
2599 total_wid = column - right_margin;
2601 gf_link_filter(gf_line_test,
2602 gf_line_test_opt(pad_to_right_edge, (void *) &total_wid));
2603 wrapflags |= GFW_HANDLES;
2606 gf_link_filter(gf_nvtnl_local, NULL);
2608 if((errstr = gf_pipe(tmp_gc, final_pc)) != NULL){
2609 rv = FHT_WRTERR;
2610 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2611 "Can't build header : %.200s", errstr);
2615 if(df_store){
2616 gf_clear_so_readc(df_store);
2617 so_give(&df_store);
2619 else
2620 gf_clear_so_readc(tmp_store);
2623 so_give(&tmp_store);
2625 if(h)
2626 fs_give((void **)&h);
2628 if(fields)
2629 fs_give((void **)&fields);
2631 return(rv);
2635 /*----------------------------------------------------------------------
2636 Format RAW header text for display
2638 Args: stream -- mail stream for various header text fetches
2639 rawno -- sequence number in stream of message we're interested in
2640 section -- which section of message
2641 pc -- function to write header text with
2643 Result: 0 if all's well, -1 if write error, 1 if fetch error
2645 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2646 in the local convention.
2648 ----*/
2650 format_raw_header(MAILSTREAM *stream, long int msgno, char *section, gf_o_t pc)
2652 char *h = mail_fetch_header(stream, msgno, section, NULL, NULL, FT_PEEK);
2653 unsigned char c;
2655 if(h){
2656 while(*h){
2657 if(ISRFCEOL(h)){
2658 h += 2;
2659 if(!gf_puts(NEWLINE, pc))
2660 return(FHT_WRTERR);
2662 if(ISRFCEOL(h)) /* all done! */
2663 return(FHT_OK);
2665 else if((unsigned char)(*h) < 0x80 && FILTER_THIS(*h) &&
2666 !(*(h+1) && *h == ESCAPE && match_escapes(h+1))){
2667 c = (unsigned char) *h++;
2668 if(!((*pc)(c >= 0x80 ? '~' : '^')
2669 && (*pc)((c == 0x7f) ? '?' : (c & 0x1f) + '@')))
2670 return(FHT_WRTERR);
2672 else if(!(*pc)(*h++))
2673 return(FHT_WRTERR);
2676 else
2677 return(FHT_FTCHERR);
2679 return(FHT_OK);
2684 /*----------------------------------------------------------------------
2685 Format c-client envelope data suitable for display
2687 Args: s -- mail stream for various header text fetches
2688 n -- raw sequence number in stream of message we're interested in
2689 sect -- which section of message
2690 e -- pointer to msg's envelope
2691 pc -- function to write header text with
2692 which -- which header lines to write
2693 oacs --
2694 flags -- FM_ flags, see pith/mailview.h
2696 Result: 0 if all's well, -1 if write error, 1 if fetch error
2698 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2699 in the local convention.
2701 ----*/
2702 void
2703 format_envelope(MAILSTREAM *s, long int n, char *sect, ENVELOPE *e, gf_o_t pc,
2704 long int which, char *oacs, int flags)
2706 char *q, *p2, buftmp[MAILTMPLEN];
2708 if(!e)
2709 return;
2711 if((which & FE_DATE) && e->date) {
2712 q = "Date: ";
2713 snprintf(buftmp, sizeof(buftmp), "%s",
2714 F_ON(F_DATES_TO_LOCAL,ps_global)
2715 ? convert_date_to_local((char *) e->date) : (char *) e->date);
2716 buftmp[sizeof(buftmp)-1] = '\0';
2717 p2 = (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
2718 SIZEOF_20KBUF, buftmp);
2719 gf_puts(q, pc);
2720 format_env_puts(p2, pc);
2721 gf_puts(NEWLINE, pc);
2724 if((which & FE_FROM) && e->from)
2725 format_addr_string(s, n, sect, "From: ", e->from, flags, oacs, pc);
2727 if((which & FE_REPLYTO) && e->reply_to
2728 && (!e->from || !address_is_same(e->reply_to, e->from)))
2729 format_addr_string(s, n, sect, "Reply-To: ", e->reply_to, flags, oacs, pc);
2731 if((which & FE_TO) && e->to)
2732 format_addr_string(s, n, sect, "To: ", e->to, flags, oacs, pc);
2734 if((which & FE_CC) && e->cc)
2735 format_addr_string(s, n, sect, "Cc: ", e->cc, flags, oacs, pc);
2737 if((which & FE_BCC) && e->bcc)
2738 format_addr_string(s, n, sect, "Bcc: ", e->bcc, flags, oacs, pc);
2740 if((which & FE_RETURNPATH) && e->return_path)
2741 format_addr_string(s, n, sect, "Return-Path: ", e->return_path,
2742 flags, oacs, pc);
2744 if((which & FE_NEWSGROUPS) && e->newsgroups){
2745 int bogus = NIL;
2746 format_newsgroup_string("Newsgroups: ", e->newsgroups, flags, pc);
2747 if (!e->ngpathexists && e->message_id &&
2748 strncmp (e->message_id,"<alpine.",8) &&
2749 strncmp (e->message_id,"<Pine.",6) &&
2750 strncmp (e->message_id,"<MS-C.",6) &&
2751 strncmp (e->message_id,"<MailManager.",13) &&
2752 strncmp (e->message_id,"<EasyMail.",11) &&
2753 strncmp (e->message_id,"<ML-",4)) bogus = T;
2755 if(bogus)
2756 q_status_message(SM_ORDER, 0, 3,
2757 "Unverified Newsgroup header -- Message MAY or MAY NOT have been posted");
2760 if((which & FE_FOLLOWUPTO) && e->followup_to)
2761 format_newsgroup_string("Followup-To: ", e->followup_to, flags, pc);
2763 if((which & FE_SUBJECT) && e->subject && e->subject[0]){
2764 char *freeme = NULL;
2766 q = "Subject: ";
2767 gf_puts(q, pc);
2769 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->subject), SIZEOF_20KBUF-10000);
2771 if(flags & FM_DISPLAY
2772 && (ps_global->display_keywords_in_subject
2773 || ps_global->display_keywordinits_in_subject)){
2775 /* don't bother if no keywords are defined */
2776 if(some_user_flags_defined(s))
2777 p2 = freeme = prepend_keyword_subject(s, n, p2,
2778 ps_global->display_keywords_in_subject ? KW : KWInit,
2779 NULL, ps_global->VAR_KW_BRACES);
2782 format_env_puts(p2, pc);
2784 if(freeme)
2785 fs_give((void **) &freeme);
2787 gf_puts(NEWLINE, pc);
2790 if((which & FE_SENDER) && e->sender
2791 && (!e->from || !address_is_same(e->sender, e->from)))
2792 format_addr_string(s, n, sect, "Sender: ", e->sender, flags, oacs, pc);
2794 if((which & FE_MESSAGEID) && e->message_id){
2795 q = "Message-ID: ";
2796 gf_puts(q, pc);
2797 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->message_id), SIZEOF_20KBUF-10000);
2798 format_env_puts(p2, pc);
2799 gf_puts(NEWLINE, pc);
2802 if((which & FE_INREPLYTO) && e->in_reply_to){
2803 q = "In-Reply-To: ";
2804 gf_puts(q, pc);
2805 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);
2806 format_env_puts(p2, pc);
2807 gf_puts(NEWLINE, pc);
2810 if((which & FE_REFERENCES) && e->references) {
2811 q = "References: ";
2812 gf_puts(q, pc);
2813 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->references), SIZEOF_20KBUF-10000);
2814 format_env_puts(p2, pc);
2815 gf_puts(NEWLINE, pc);
2823 * The argument fieldname is something like "Subject:..." or "Subject".
2824 * Look through the specs in speccolor for a match of the fieldname,
2825 * and return the color that goes with any match, or NULL.
2826 * Caller should free the color.
2828 COLOR_PAIR *
2829 hdr_color(char *fieldname, char *value, SPEC_COLOR_S *speccolor)
2831 SPEC_COLOR_S *hc = NULL;
2832 COLOR_PAIR *color_pair = NULL;
2833 char *colon, *fname;
2834 char fbuf[FBUF_LEN+1];
2835 int gotit;
2836 PATTERN_S *pat;
2838 colon = strindex(fieldname, ':');
2839 if(colon){
2840 strncpy(fbuf, fieldname, MIN(colon-fieldname,FBUF_LEN));
2841 fbuf[MIN(colon-fieldname,FBUF_LEN)] = '\0';
2842 fname = fbuf;
2844 else
2845 fname = fieldname;
2847 if(fname && *fname)
2848 for(hc = speccolor; hc; hc = hc->next)
2849 if(hc->spec && !strucmp(fname, hc->spec)){
2850 if(!hc->val)
2851 break;
2853 gotit = 0;
2854 for(pat = hc->val; !gotit && pat; pat = pat->next)
2855 if(srchstr(value, pat->substring))
2856 gotit++;
2858 if(gotit)
2859 break;
2862 if(hc && hc->fg && hc->fg[0] && hc->bg && hc->bg[0])
2863 color_pair = new_color_pair(hc->fg, hc->bg);
2865 return(color_pair);
2870 * The argument fieldname is something like "Subject:..." or "Subject".
2871 * Look through the specs in hdr_colors for a match of the fieldname,
2872 * and return 1 if that fieldname is in one of the patterns, 0 otherwise.
2875 any_hdr_color(char *fieldname)
2877 SPEC_COLOR_S *hc = NULL;
2878 char *colon, *fname;
2879 char fbuf[FBUF_LEN+1];
2881 colon = strindex(fieldname, ':');
2882 if(colon){
2883 strncpy(fbuf, fieldname, MIN(colon-fieldname,FBUF_LEN));
2884 fbuf[MIN(colon-fieldname,FBUF_LEN)] = '\0';
2885 fname = fbuf;
2887 else
2888 fname = fieldname;
2890 if(fname && *fname)
2891 for(hc = ps_global->hdr_colors; hc; hc = hc->next)
2892 if(hc->spec && !strucmp(fname, hc->spec))
2893 break;
2895 return(hc ? 1 : 0);
2899 /*----------------------------------------------------------------------
2900 Format an address field, wrapping lines nicely at commas
2902 Args: field_name -- The name of the field we're formatting ("TO: ", ...)
2903 addr -- ADDRESS structure to format
2905 Result: A formatted, malloced string is returned.
2906 ----------------------------------------------------------------------*/
2907 void
2908 format_addr_string(MAILSTREAM *stream, long int msgno, char *section, char *field_name,
2909 struct mail_address *addr, int flags, char *oacs, gf_o_t pc)
2911 char *ptmp, *mtmp = NULL;
2912 int trailing = 0, group = 0;
2913 ADDRESS *atmp;
2915 if(!addr)
2916 return;
2919 * quickly run down address list to make sure none are patently bogus.
2920 * If so, just blat raw field out.
2922 for(atmp = addr; stream && atmp; atmp = atmp->next)
2923 if(atmp->host && atmp->host[0] == '.'){
2924 char *field, *fields[2];
2926 fields[1] = NULL;
2927 fields[0] = cpystr(field_name);
2928 if((ptmp = strchr(fields[0], ':')) != NULL)
2929 *ptmp = '\0';
2931 if((field = pine_fetchheader_lines(stream, msgno, section, fields)) != NULL){
2932 char *h, *t;
2934 for(t = h = field; *h ; t++)
2935 if(*t == '\015' && *(t+1) == '\012'){
2936 *t = '\0'; /* tie off line */
2937 format_env_puts(h, pc);
2938 if(*(h = (++t) + 1)) /* set new h and skip CRLF */
2939 gf_puts(NEWLINE, pc); /* more to write */
2940 else
2941 break;
2943 else if(!*t){ /* shouldn't happen much */
2944 if(h != t)
2945 format_env_puts(h, pc);
2947 break;
2950 fs_give((void **)&field);
2953 fs_give((void **)&fields[0]);
2954 gf_puts(NEWLINE, pc);
2955 dprint((2, "Error in \"%s\" field address\n",
2956 field_name ? field_name : "?"));
2957 return;
2960 gf_puts(field_name, pc);
2962 while(addr){
2963 atmp = addr->next; /* remember what's next */
2964 addr->next = NULL;
2965 if(!addr->host && addr->mailbox){
2966 mtmp = addr->mailbox;
2967 addr->mailbox = cpystr((char *)rfc1522_decode_to_utf8(
2968 (unsigned char *)tmp_20k_buf,
2969 SIZEOF_20KBUF, addr->mailbox));
2972 ptmp = addr->personal; /* RFC 1522 personal name? */
2973 addr->personal = iutf8ncpy((char *)tmp_20k_buf, (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+10000), SIZEOF_20KBUF-10000, addr->personal), 10000);
2974 tmp_20k_buf[10000-1] = '\0';
2976 if(!trailing) /* 1st pass, just address */
2977 trailing++;
2978 else{ /* else comma, unless */
2979 if(!((group == 1 && addr->host) /* 1st addr in group, */
2980 || (!addr->host && !addr->mailbox))){ /* or end of group */
2981 gf_puts(",", pc);
2982 #if 0
2983 gf_puts(NEWLINE, pc); /* ONE address/line please */
2984 gf_puts(" ", pc);
2985 #endif
2988 gf_puts(" ", pc);
2991 pine_rfc822_write_address_noquote(addr, pc, &group);
2993 addr->personal = ptmp; /* restore old personal ptr */
2994 if(!addr->host && addr->mailbox){
2995 fs_give((void **)&addr->mailbox);
2996 addr->mailbox = mtmp;
2999 addr->next = atmp;
3000 addr = atmp;
3003 gf_puts(NEWLINE, pc);
3009 const char *rspecials_minus_quote_and_dot = "()<>@,;:\\[]";
3010 /* RFC822 continuation, must start with CRLF */
3011 #define RFC822CONT "\015\012 "
3013 /* Write RFC822 address with some quoting turned off.
3014 * Accepts:
3015 * address to interpret
3017 * (This is a copy of c-client's rfc822_write_address except
3018 * we don't quote double quote and dot in personal names. It writes
3019 * to a gf_io_t instead of to a buffer so that we don't have to worry
3020 * about fixed sized buffer overflowing. It's also special cased to deal
3021 * with only a single address.)
3023 * The idea is that there are some places where we'd just like to display
3024 * the personal name as is before applying confusing quoting. However,
3025 * we do want to be careful not to break things that should be quoted so
3026 * we'll only use this where we are sure. Quoting may look ugly but it
3027 * doesn't usually break anything.
3029 void
3030 pine_rfc822_write_address_noquote(struct mail_address *adr, gf_o_t pc, int *group)
3032 extern const char *rspecials;
3034 if (adr->host) { /* ordinary address? */
3035 if (!(adr->personal || adr->adl)) pine_rfc822_address (adr, pc);
3036 else { /* no, must use phrase <route-addr> form */
3037 if (adr->personal)
3038 pine_rfc822_cat (adr->personal, rspecials_minus_quote_and_dot, pc);
3040 gf_puts(" <", pc); /* write address delimiter */
3041 pine_rfc822_address(adr, pc);
3042 gf_puts (">", pc); /* closing delimiter */
3045 if(*group)
3046 (*group)++;
3048 else if (adr->mailbox) { /* start of group? */
3049 /* yes, write group name */
3050 pine_rfc822_cat (adr->mailbox, rspecials, pc);
3052 gf_puts (": ", pc); /* write group identifier */
3053 *group = 1; /* in a group */
3055 else if (*group) { /* must be end of group (but be paranoid) */
3056 gf_puts (";", pc);
3057 *group = 0; /* no longer in that group */
3062 /* Write RFC822 route-address to string
3063 * Accepts:
3064 * address to interpret
3067 void
3068 pine_rfc822_address(struct mail_address *adr, gf_o_t pc)
3070 extern char *wspecials;
3072 if (adr && adr->host) { /* no-op if no address */
3073 if (adr->adl) { /* have an A-D-L? */
3074 gf_puts (adr->adl, pc);
3075 gf_puts (":", pc);
3077 /* write mailbox name */
3078 pine_rfc822_cat (adr->mailbox, wspecials, pc);
3079 if (*adr->host != '@') { /* unless null host (HIGHLY discouraged!) */
3080 gf_puts ("@", pc); /* host delimiter */
3081 gf_puts (adr->host, pc); /* write host name */
3087 /* Concatenate RFC822 string
3088 * Accepts:
3089 * pointer to string to concatenate
3090 * list of special characters
3093 void
3094 pine_rfc822_cat(char *src, const char *specials, gf_o_t pc)
3096 char *s;
3098 if (strpbrk (src,specials)) { /* any specials present? */
3099 gf_puts ("\"", pc); /* opening quote */
3100 /* truly bizarre characters in there? */
3101 while ((s = strpbrk (src,"\\\"")) != NULL) {
3102 char save[2];
3104 /* turn it into a null-terminated piece */
3105 save[0] = *s;
3106 save[1] = '\0';
3107 *s = '\0';
3108 gf_puts (src, pc); /* yes, output leader */
3109 *s = save[0];
3110 gf_puts ("\\", pc); /* quoting */
3111 gf_puts (save, pc); /* output the bizarre character */
3112 src = ++s; /* continue after the bizarre character */
3114 if (*src) gf_puts (src, pc);/* output non-bizarre string */
3115 gf_puts ("\"", pc); /* closing quote */
3117 else gf_puts (src, pc); /* otherwise it's the easy case */
3121 /*----------------------------------------------------------------------
3122 Format an address field, wrapping lines nicely at commas
3124 Args: field_name -- The name of the field we're formatting ("TO:", Cc:...)
3125 newsgrps -- ADDRESS structure to format
3127 Result: A formatted, malloced string is returned.
3129 The resulting lines formatted are 80 columns wide.
3130 ----------------------------------------------------------------------*/
3131 void
3132 format_newsgroup_string(char *field_name, char *newsgrps, int flags, gf_o_t pc)
3134 char buf[MAILTMPLEN];
3135 int trailing = 0, llen, alen;
3136 char *next_ng;
3138 if(!newsgrps || !*newsgrps)
3139 return;
3141 gf_puts(field_name, pc);
3143 llen = strlen(field_name);
3144 while(*newsgrps){
3145 for(next_ng = newsgrps; *next_ng && *next_ng != ','; next_ng++);
3146 strncpy(buf, newsgrps, MIN(next_ng - newsgrps, sizeof(buf)-1));
3147 buf[MIN(next_ng - newsgrps, sizeof(buf)-1)] = '\0';
3148 newsgrps = next_ng;
3149 if(*newsgrps)
3150 newsgrps++;
3151 alen = strlen(buf);
3152 if(!trailing){ /* first time thru, just address */
3153 llen += alen;
3154 trailing++;
3156 else{ /* else preceding comma */
3157 gf_puts(",", pc);
3158 llen++;
3160 if(alen + llen + 1 > 76){
3161 gf_puts(NEWLINE, pc);
3162 gf_puts(" ", pc);
3163 llen = alen + 5;
3165 else{
3166 gf_puts(" ", pc);
3167 llen += alen + 1;
3171 if(alen && llen > 76){ /* handle long addresses */
3172 register char *q, *p = &buf[alen-1];
3174 while(p > buf){
3175 if(isspace((unsigned char)*p)
3176 && (llen - (alen - (int)(p - buf))) < 76){
3177 for(q = buf; q < p; q++)
3178 (*pc)(*q); /* write character */
3180 gf_puts(NEWLINE, pc);
3181 gf_puts(" ", pc);
3182 gf_puts(p, pc);
3183 break;
3185 else
3186 p--;
3189 if(p == buf) /* no reasonable break point */
3190 gf_puts(buf, pc);
3192 else
3193 gf_puts(buf, pc);
3196 gf_puts(NEWLINE, pc);
3201 /*----------------------------------------------------------------------
3202 Format a text field that's part of some raw (non-envelope) message header
3204 Args: start --
3205 finish --
3206 pc --
3208 Result: Semi-digested text (RFC 1522 decoded, anyway) written with "pc"
3210 ----------------------------------------------------------------------*/
3212 format_raw_hdr_string(char *start, char *finish, gf_o_t pc, char *oacs, int flags)
3214 register char *current;
3215 unsigned char *p, *tmp = NULL, c;
3216 size_t n, len;
3217 char ch;
3218 int rv = FHT_OK;
3220 ch = *finish;
3221 *finish = '\0';
3223 if((n = 4*(finish-start)) > SIZEOF_20KBUF-1){
3224 len = n+1;
3225 p = tmp = (unsigned char *) fs_get(len * sizeof(unsigned char));
3227 else{
3228 len = SIZEOF_20KBUF;
3229 p = (unsigned char *) tmp_20k_buf;
3232 if(islower((unsigned char)(*start)))
3233 *start = toupper((unsigned char)(*start));
3235 current = (char *) rfc1522_decode_to_utf8(p, len, start);
3237 /* output from start to finish */
3238 while(*current && rv == FHT_OK)
3239 if(ISRFCEOL(current)){
3240 if(!gf_puts(NEWLINE, pc))
3241 rv = FHT_WRTERR;
3243 current += 2;
3245 else if((unsigned char)(*current) < 0x80 && FILTER_THIS(*current) &&
3246 !(*(current+1) && *current == ESCAPE && match_escapes(current+1))){
3247 c = (unsigned char) *current++;
3248 if(!((*pc)(c >= 0x80 ? '~' : '^')
3249 && (*pc)((c == 0x7f) ? '?' : (c & 0x1f) + '@')))
3250 rv = FHT_WRTERR;
3252 else if(!(*pc)(*current++))
3253 rv = FHT_WRTERR;
3255 if(tmp)
3256 fs_give((void **) &tmp);
3258 *finish = ch;
3260 return(rv);
3266 /*----------------------------------------------------------------------
3267 Format a text field that's part of some raw (non-envelope) message header
3269 Args: s --
3270 pc --
3272 Result: Output
3274 ----------------------------------------------------------------------*/
3276 format_env_puts(char *s, gf_o_t pc)
3278 if(ps_global->pass_ctrl_chars)
3279 return(gf_puts(s, pc));
3281 for(; *s; s++)
3282 if((unsigned char)(*s) < 0x80 && FILTER_THIS(*s) && !(*(s+1) && *s == ESCAPE && match_escapes(s+1))){
3283 if(!((*pc)((unsigned char) (*s) >= 0x80 ? '~' : '^')
3284 && (*pc)((*s == 0x7f) ? '?' : (*s & 0x1f) + '@')))
3285 return(0);
3287 else if(!(*pc)(*s))
3288 return(0);
3290 return(1);
3294 char *
3295 display_parameters(PARAMETER *params)
3297 int n, longest = 0;
3298 char *d, *printme;
3299 PARAMETER *p;
3300 PARMLIST_S *parmlist;
3302 for(p = params; p; p = p->next) /* ok if we include *'s */
3303 if(p->attribute && (n = strlen(p->attribute)) > longest)
3304 longest = MIN(32, n); /* shouldn't be any bigger than 32 */
3306 d = tmp_20k_buf;
3307 tmp_20k_buf[0] = '\0';
3308 if((parmlist = rfc2231_newparmlist(params)) != NULL){
3309 n = 0; /* n overloaded */
3310 while(rfc2231_list_params(parmlist) && d < tmp_20k_buf + 10000){
3311 if(n++){
3312 snprintf(d, 10000-(d-tmp_20k_buf), "\n");
3313 tmp_20k_buf[10000-1] = '\0';
3314 d += strlen(d);
3317 if(parmlist->value){
3318 if(parmlist->attrib && strucmp(parmlist->attrib, "url") == 0){
3319 snprintf(printme = tmp_20k_buf + 11000, 1000, "%s", parmlist->value);
3320 sqzspaces(printme);
3322 else
3323 printme = strsquish(tmp_20k_buf + 11000, 1000, parmlist->value, 100);
3325 else
3326 printme = "";
3328 snprintf(d, 10000-(d-tmp_20k_buf), "%-*s: %s", longest,
3329 parmlist->attrib ? parmlist->attrib : "", printme);
3331 tmp_20k_buf[10000-1] = '\0';
3332 d += strlen(d);
3335 rfc2231_free_parmlist(&parmlist);
3338 return(tmp_20k_buf);
3342 /*----------------------------------------------------------------------
3343 Fetch the requested header fields from the msgno specified
3345 Args: stream -- mail stream of open folder
3346 msgno -- number of message to get header lines from
3347 fields -- array of pointers to desired fields
3349 Returns: allocated string containing matched header lines,
3350 NULL on error.
3351 ----*/
3352 char *
3353 pine_fetch_header(MAILSTREAM *stream, long int msgno, char *section, char **fields, long int flags)
3355 STRINGLIST *sl;
3356 char *p, *m, *h = NULL, *match = NULL, *free_this, tmp[MAILTMPLEN];
3357 char **pflds = NULL, **pp = NULL, **qq;
3360 * If the user misconfigures it is possible to have one of the fields
3361 * set to the empty string instead of a header name. We want to catch
3362 * that here instead of asking the server the nonsensical question.
3364 for(pp = fields ? &fields[0] : NULL; pp && *pp; pp++)
3365 if(!**pp)
3366 break;
3368 if(pp && *pp){ /* found an empty header field, fix it */
3369 pflds = copy_list_array(fields);
3370 for(pp = pflds; pp && *pp; pp++){
3371 if(!**pp){ /* scoot rest of the lines up */
3372 free_this = *pp;
3373 for(qq = pp; *qq; qq++)
3374 *qq = *(qq+1);
3376 if(free_this)
3377 fs_give((void **) &free_this);
3381 /* no headers to look for, return NULL */
3382 if(pflds && !*pflds && !(flags & FT_NOT)){
3383 free_list_array(&pflds);
3384 return(NULL);
3387 else
3388 pflds = fields;
3390 sl = (pflds && *pflds) ? new_strlst(pflds) : NULL; /* package up fields */
3391 h = mail_fetch_header(stream, msgno, section, sl, NULL, flags | FT_PEEK);
3392 if (sl)
3393 free_strlst(&sl);
3395 if(!h){
3396 if(pflds && pflds != fields)
3397 free_list_array(&pflds);
3399 return(NULL);
3402 while(find_field(&h, tmp, sizeof(tmp))){
3403 for(pp = &pflds[0]; *pp && strucmp(tmp, *pp); pp++)
3406 /* interesting field? */
3407 if((p = (flags & FT_NOT) ? ((*pp) ? NULL : tmp) : *pp) != NULL){
3409 * Hold off allocating space for matching fields until
3410 * we at least find one to copy...
3412 if(!match)
3413 match = m = fs_get(strlen(h) + strlen(p) + 1);
3415 while(*p) /* copy field name */
3416 *m++ = *p++;
3418 while(*h && (*m++ = *h++)) /* header includes colon */
3419 if(*(m-1) == '\n' && (*h == '\r' || !isspace((unsigned char)*h)))
3420 break;
3422 *m = '\0'; /* tie off match string */
3424 else{ /* no match, pass this field */
3425 while(*h && !(*h++ == '\n'
3426 && (*h == '\r' || !isspace((unsigned char)*h))))
3431 if(pflds && pflds != fields)
3432 free_list_array(&pflds);
3434 return(match ? match : cpystr(""));
3439 find_field(char **h, char *tmp, size_t ntmp)
3441 char *otmp = tmp;
3443 if(!h || !*h || !**h || isspace((unsigned char)**h))
3444 return(0);
3446 while(tmp-otmp<ntmp-1 && **h && **h != ':' && !isspace((unsigned char)**h))
3447 *tmp++ = *(*h)++;
3449 *tmp = '\0';
3450 return(1);
3454 static char *_last_embedded_fg_color, *_last_embedded_bg_color;
3458 embed_color(COLOR_PAIR *cp, gf_o_t pc)
3460 if(cp && cp->fg){
3461 if(_last_embedded_fg_color)
3462 fs_give((void **)&_last_embedded_fg_color);
3464 _last_embedded_fg_color = cpystr(cp->fg);
3466 if(!(pc && (*pc)(TAG_EMBED) && (*pc)(TAG_FGCOLOR) &&
3467 gf_puts(color_to_asciirgb(cp->fg), pc)))
3468 return 0;
3471 if(cp && cp->bg){
3472 if(_last_embedded_bg_color)
3473 fs_give((void **)&_last_embedded_bg_color);
3475 _last_embedded_bg_color = cpystr(cp->bg);
3477 if(!(pc && (*pc)(TAG_EMBED) && (*pc)(TAG_BGCOLOR) &&
3478 gf_puts(color_to_asciirgb(cp->bg), pc)))
3479 return 0;
3482 return 1;
3486 COLOR_PAIR *
3487 get_cur_embedded_color(void)
3489 COLOR_PAIR *ret;
3491 if(_last_embedded_fg_color && _last_embedded_bg_color)
3492 ret = new_color_pair(_last_embedded_fg_color, _last_embedded_bg_color);
3493 else
3494 ret = pico_get_cur_color();
3496 return(ret);
3500 void
3501 clear_cur_embedded_color(void)
3503 if(_last_embedded_fg_color)
3504 fs_give((void **)&_last_embedded_fg_color);
3506 if(_last_embedded_bg_color)
3507 fs_give((void **)&_last_embedded_bg_color);
3512 scroll_handle_start_color(char *colorstring, size_t buflen, int *len)
3514 *len = 0;
3516 if(pico_usingcolor()){
3517 char *fg = NULL, *bg = NULL, *s;
3519 if(ps_global->VAR_SLCTBL_FORE_COLOR
3520 && colorcmp(ps_global->VAR_SLCTBL_FORE_COLOR,
3521 ps_global->VAR_NORM_FORE_COLOR))
3522 fg = ps_global->VAR_SLCTBL_FORE_COLOR;
3524 if(ps_global->VAR_SLCTBL_BACK_COLOR
3525 && colorcmp(ps_global->VAR_SLCTBL_BACK_COLOR,
3526 ps_global->VAR_NORM_BACK_COLOR))
3527 bg = ps_global->VAR_SLCTBL_BACK_COLOR;
3529 if(bg || fg){
3530 COLOR_PAIR *tmp;
3533 * The blacks are just known good colors for
3534 * testing whether the other color is good.
3536 if((tmp = new_color_pair(fg ? fg : colorx(COL_BLACK),
3537 bg ? bg : colorx(COL_BLACK))) != NULL){
3538 if(pico_is_good_colorpair(tmp))
3539 for(s = color_embed(fg, bg);
3540 (*len) < buflen && (colorstring[*len] = *s);
3541 s++, (*len)++)
3544 free_color_pair(&tmp);
3548 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
3549 strncpy(colorstring + (*len), url_embed(TAG_BOLDON), MIN(3,buflen-(*len)));
3550 *len += 2;
3554 colorstring[buflen-1] = '\0';
3556 return(*len != 0);
3561 scroll_handle_end_color(char *colorstring, size_t buflen, int *len, int use_hdr_color)
3563 *len = 0;
3564 if(pico_usingcolor()){
3565 char *fg = NULL, *bg = NULL, *s;
3566 char *basefg = NULL, *basebg = NULL;
3568 basefg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_FORE_COLOR
3569 : ps_global->VAR_NORM_FORE_COLOR;
3570 basebg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_BACK_COLOR
3571 : ps_global->VAR_NORM_BACK_COLOR;
3574 * We need to change the fg and bg colors back even if they
3575 * are the same as the Normal Colors so that color_a_quote
3576 * will have a chance to pick up those colors as the ones to
3577 * switch to. We don't do this before the handle above so that
3578 * the quote color will flow into the selectable item when
3579 * the selectable item color is partly the same as the
3580 * normal color. That is, suppose the normal color was black on
3581 * cyan and the selectable color was blue on cyan, only a fg color
3582 * change. We preserve the only-a-fg-color-change in a quote by
3583 * letting the quote background color flow into the selectable text.
3585 if(ps_global->VAR_SLCTBL_FORE_COLOR)
3586 fg = basefg;
3588 if(ps_global->VAR_SLCTBL_BACK_COLOR)
3589 bg = basebg;
3591 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
3592 strncpy(colorstring, url_embed(TAG_BOLDOFF), MIN(3,buflen));
3593 *len = 2;
3596 if(fg || bg)
3597 for(s = color_embed(fg, bg); (*len) < buflen && (colorstring[*len] = *s); s++, (*len)++)
3601 colorstring[buflen-1] = '\0';
3603 return(*len != 0);
3608 * Helper routine that is of limited use.
3609 * We need to tally up the screen width of
3610 * a UTF-8 string as we go through the string.
3611 * We just want the width of the character starting
3612 * at str (and no longer than remaining_octets).
3613 * If we're plopped into the middle of a UTF-8
3614 * character we just want to return width zero.
3617 width_at_this_position(unsigned char *str, unsigned long n)
3619 unsigned char *inputp = str;
3620 unsigned long remaining_octets = n;
3621 UCS ucs;
3622 int width = 0;
3624 ucs = (UCS) utf8_get(&inputp, &remaining_octets);
3625 if(!(ucs & U8G_ERROR || ucs == UBOGON)){
3626 width = wcellwidth(ucs);
3627 /* Writechar will print a '?' */
3628 if(width < 0)
3629 width = 1;
3632 return(width);