* New version 2.21.999
[alpine.git] / pith / mailview.c
blob3fe1dd81b51289633c6ff1b9bbe166c15221746e
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: mailview.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2009 University of Washington
8 * Copyright 2013-2018 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
21 mailview.c
22 Implements message data gathering and formatting
24 ====*/
27 #include "headers.h"
28 #include "../pith/ical.h"
29 #include "../pith/body.h"
30 #include "../pith/mailpart.h"
31 #include "../pith/mailview.h"
32 #include "../pith/conf.h"
33 #include "../pith/msgno.h"
34 #include "../pith/editorial.h"
35 #include "../pith/mimedesc.h"
36 #include "../pith/margin.h"
37 #include "../pith/color.h"
38 #include "../pith/strlst.h"
39 #include "../pith/charset.h"
40 #include "../pith/status.h"
41 #include "../pith/maillist.h"
42 #include "../pith/mailcmd.h"
43 #include "../pith/mailindx.h"
44 #include "../pith/imap.h"
45 #include "../pith/detach.h"
46 #include "../pith/text.h"
47 #include "../pith/url.h"
48 #include "../pith/rfc2231.h"
49 #include "../pith/list.h"
50 #include "../pith/stream.h"
51 #include "../pith/send.h"
52 #include "../pith/filter.h"
53 #include "../pith/string.h"
54 #include "../pith/ablookup.h"
55 #include "../pith/escapes.h"
56 #include "../pith/keyword.h"
57 #include "../pith/smime.h"
60 #define FBUF_LEN (50)
62 #define ISRFCEOL(S) (*(S) == '\015' && *((S)+1) == '\012')
66 * This is a list of header fields that are represented canonically
67 * by the c-client's ENVELOPE structure. The list is used by the
68 * two functions below to decide if a given field is included in this
69 * set.
71 static struct envelope_s {
72 char *name;
73 long val;
74 } envelope_hdrs[] = {
75 {"from", FE_FROM},
76 {"sender", FE_SENDER},
77 {"date", FE_DATE},
78 {"to", FE_TO},
79 {"cc", FE_CC},
80 {"bcc", FE_BCC},
81 {"newsgroups", FE_NEWSGROUPS},
82 {"subject", FE_SUBJECT},
83 {"message-id", FE_MESSAGEID},
84 {"reply-to", FE_REPLYTO},
85 {"followup-to", FE_FOLLOWUPTO},
86 {"in-reply-to", FE_INREPLYTO},
87 /* {"return-path", FE_RETURNPATH}, not usually filled in */
88 {"references", FE_REFERENCES},
89 {NULL, 0}
94 * Hook for optional display of rfc2369 content
96 int (*pith_opt_rfc2369_editorial)(long, HANDLE_S **, int, int, gf_io_t);
102 * Internal prototypes
104 int format_blip_seen(long);
105 int is_an_env_hdr(char *);
106 int is_an_addr_hdr(char *);
107 void format_env_hdr(MAILSTREAM *, long, char *, ENVELOPE *,
108 fmt_env_t, gf_io_t, char *, char *, int);
109 int delineate_this_header(char *, char *, char **, char **);
110 char *url_embed(int);
111 int color_headers(long, char *, LT_INS_S **, void *);
112 int url_hilite_hdr(long, char *, LT_INS_S **, void *);
113 int pad_to_right_edge(long, char *, LT_INS_S **, void *);
114 int url_bogus_imap(char **, char *, char *);
115 int format_raw_header(MAILSTREAM *, long, char *, gf_io_t);
116 void format_envelope(MAILSTREAM *, long, char *, ENVELOPE *,
117 gf_io_t, long, char *, int);
118 int any_hdr_color(char *);
119 void format_addr_string(MAILSTREAM *, long, char *, char *,
120 ADDRESS *, int, char *, gf_io_t);
121 void pine_rfc822_write_address_noquote(ADDRESS *, gf_io_t, int *);
122 void format_newsgroup_string(char *, char *, int, gf_io_t);
123 int format_raw_hdr_string(char *, char *, gf_io_t, char *, int);
124 int format_env_puts(char *, gf_io_t);
125 int find_field(char **, char *, size_t);
126 int embed_color(COLOR_PAIR *, gf_io_t);
127 COLOR_PAIR *get_cur_embedded_color(void);
128 void clear_cur_embedded_color(void);
129 void format_calendar_vevent(VCALENDAR_S *, ATTACH_S *, HANDLE_S **, int, int, gf_io_t, int);
133 /*----------------------------------------------------------------------
134 Format a message message for viewing
136 Args: msgno -- The number of the message to view
137 env -- pointer to the message's envelope
138 body -- pointer to the message's body
139 handlesp -- address of pointer to the message's handles
140 flgs -- possible flags listed in pith/mailview.h with
141 prefix FM_
142 pc -- write to this function
144 Result: Returns true if no problems encountered, else false.
146 First the envelope is formatted; next a list of all attachments is
147 formatted if there is more than one. Then all the body parts are
148 formatted, fetching them as needed. This includes headers of included
149 message. Richtext is also formatted. An entry is made in the text for
150 parts that are not displayed or can't be displayed.
152 ----*/
154 format_message(long int msgno, ENVELOPE *env, struct mail_bodystruct *body,
155 HANDLE_S **handlesp, int flgs, gf_io_t pc)
157 char *decode_err = NULL;
158 HEADER_S h;
159 int width;
161 clear_cur_embedded_color();
163 if(!(flgs & FM_DISPLAY))
164 flgs |= FM_NOINDENT;
166 width = (flgs & FM_DISPLAY) ? ps_global->ttyo->screen_cols : 80;
168 /*---- format and copy envelope ----*/
169 if(!(flgs & FM_NOEDITORIAL)){
170 if(ps_global->full_header == 1)
171 /* TRANSLATORS: User is viewing a message and all the quoted text is
172 being shown. */
173 q_status_message(SM_INFO, 0, 3, _("All quoted text being included"));
174 else if(ps_global->full_header == 2)
175 q_status_message(SM_INFO, 0, 3,
176 /* TRANSLATORS: User is viewing a message and all of
177 the header text is being shown. */
178 _("Full header mode ON. All header text being included"));
181 HD_INIT(&h, ps_global->VAR_VIEW_HEADERS, ps_global->view_all_except, FE_DEFAULT);
182 switch(format_header(ps_global->mail_stream, msgno, NULL,
183 env, &h, NULL, handlesp, flgs, NULL, pc)){
185 case -1 : /* write error */
186 goto write_error;
188 case 1 : /* fetch error */
189 if(!(gf_puts("[ Error fetching header ]", pc)
190 && !gf_puts(NEWLINE, pc)))
191 goto write_error;
193 break;
196 if(!(body == NULL
197 || (ps_global->full_header == 2
198 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global)))){
199 format_attachment_list(msgno, body, handlesp, flgs, width, pc);
200 format_calendar(msgno, body, handlesp, flgs, width, pc);
203 /* write delimiter and body */
204 if(gf_puts(NEWLINE, pc)
205 && (decode_err = format_body(msgno, body, handlesp, &h, flgs, width, pc)) == NULL)
206 return(1);
209 write_error:
211 if(!(flgs & FM_DISPLAY))
212 q_status_message1(SM_ORDER, 3, 4, _("Error writing message: %s"),
213 decode_err ? decode_err : error_description(errno));
215 return(0);
218 void
219 format_calendar_vevent(VCALENDAR_S *vcal, ATTACH_S *a, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc, int cflags)
221 int avail, m1, m2, hwid, i, partwid, padwid;
222 int s1, s2, dwid;
223 int *margin;
224 char padding[1024];
225 VEVENT_SUMMARY_S *vesy; /* vevent summary */
227 vesy = ical_vevent_summary(vcal);
229 if(vesy == NULL) return;
231 if((cflags & FC_SUMMARY) && (cflags & FC_FULL))
232 cflags |= ~FC_FULL;
234 avail = width;
235 margin = (cflags & FC_FULL) ? NULL
236 : (flgs & FM_NOINDENT) ? NULL : format_view_margin();
238 m1 = MAX(MIN(margin ? margin[0] : 0, avail), 0);
239 avail -= m1;
241 m2 = MAX(MIN(margin ? margin[1] : 0, avail), 0);
242 avail -= m2;
244 hwid = MAX(avail, 0);
245 padwid = 0;
247 if(ps_global->atmts[1].description == NULL){
248 // && (!ps_global->atmts[0].body
249 // || (ps_global->atmts[0].body->type == TYPETEXT))){
250 avail = width - m1 -2;
252 dwid = MAX(MIN(40, avail), 0);
254 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s%s", m1, m1, "",
255 repeat_char(dwid, '-'));
257 if(!gf_puts(tmp_20k_buf, pc) || !gf_puts(NEWLINE, pc))
258 return;
262 if(cflags & FC_SUMMARY){
263 i = utf8_width(_("Calendar Entry:"));
264 partwid = MIN(i, hwid);
265 if(m1 > 0){
266 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", m1, m1, "");
267 if(!gf_puts(tmp_20k_buf, pc))
268 return;
271 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%-*.*w%*.*s",
272 partwid, partwid, _("Calendar Entry:"),
273 padwid, padwid, "");
275 if(!gf_puts(tmp_20k_buf, pc) || !gf_puts(NEWLINE, pc))
276 return;
278 else
279 partwid = 0;
281 if(m1 > 0){
282 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", m1, m1, "");
283 if(!gf_puts(tmp_20k_buf, pc))
284 return;
287 avail = width - m1 - m2;
289 s1 = MAX(MIN(1, avail), 0);
290 avail -= s1;
292 dwid = MAX(MIN(1, avail), 0);
293 avail -= dwid;
295 s2 = MAX(MIN(1, avail), 0);
296 avail -= s2;
299 if(cflags & FC_SUMMARY)
300 utf8_snprintf(padding, sizeof(padding), "%*.*s%*.*w%*.*s",
301 s1, s1, "", dwid, dwid, "", s2, s2, "");
302 else
303 padding[0] = '\0';
305 if(vesy->cancel){
306 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
307 padding, _("This event was cancelled!"));
308 if((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON)){
309 gf_puts(tmp_20k_buf, pc);
310 gf_puts(NEWLINE, pc);
311 (*pc)(TAG_EMBED);
312 (*pc)(TAG_BOLDOFF);
316 if(vesy->organizer){
317 if(vesy->sender != NULL){
318 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
319 padding, _("Sent-by: "), vesy->sender);
320 gf_puts(tmp_20k_buf, pc);
321 gf_puts(NEWLINE, pc);
324 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
325 padding, _("Organizer: "), vesy->organizer);
326 gf_puts(tmp_20k_buf, pc);
327 gf_puts(NEWLINE, pc);
328 } /* end of if(organizer) */
330 if(vesy->location){
331 ical_remove_escapes(&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 if(cflags & FC_SUMMARY){
382 COLOR_PAIR *lastc = NULL;
383 char numbuf[50];
384 int thisdescwid;
385 COLOR_PAIR *hdrcolor = NULL;
387 if((flgs & FM_DISPLAY)
388 && !(flgs & FM_NOCOLOR)
389 && pico_usingcolor()
390 && ps_global->VAR_HEADER_GENERAL_FORE_COLOR
391 && ps_global->VAR_HEADER_GENERAL_BACK_COLOR
392 && ps_global->VAR_NORM_FORE_COLOR
393 && ps_global->VAR_NORM_BACK_COLOR
394 && (colorcmp(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
395 ps_global->VAR_NORM_FORE_COLOR)
396 || colorcmp(ps_global->VAR_HEADER_GENERAL_BACK_COLOR,
397 ps_global->VAR_NORM_BACK_COLOR))){
399 if((hdrcolor = new_color_pair(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
400 ps_global->VAR_HEADER_GENERAL_BACK_COLOR)) != NULL){
401 if(!pico_is_good_colorpair(hdrcolor))
402 free_color_pair(&hdrcolor);
406 if(!(!hdrcolor || embed_color(hdrcolor, pc)))
407 return;
409 gf_puts(padding, pc);
410 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "[%s]", _("More Details"));
412 if(handlesp){
413 char buf[16], color[64];
414 int l;
415 HANDLE_S *h;
417 h = new_handle(handlesp);
418 h->type = Attach;
419 h->h.attach = a;
421 snprintf(buf, sizeof(buf), "%d", h->key);
422 buf[sizeof(buf)-1] = '\0';
424 if(!(flgs & FM_NOCOLOR)
425 && handle_start_color(color, sizeof(color), &l, 1)){
426 lastc = get_cur_embedded_color();
427 if(!gf_nputs(color, (long) l, pc))
428 return;
430 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)
431 && (!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON))))
432 return;
434 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE)
435 && (*pc)(strlen(buf)) && gf_puts(buf, pc)))
436 return;
437 } else
438 tmp_20k_buf[0] = '\0';
440 if(!format_env_puts(tmp_20k_buf, pc))
441 return;
443 if(handlesp){
444 if(lastc){
445 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
446 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
447 return;
450 if(!embed_color(lastc, pc))
451 return;
453 free_color_pair(&lastc);
455 else if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
456 return;
458 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)))
459 return;
462 if(padwid > 0){
463 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", padwid, padwid, "");
464 if(!gf_puts(tmp_20k_buf, pc))
465 return;
468 if(!gf_puts(NEWLINE, pc))
469 return;
471 } /* end of ATTENDEES */
473 free_vevent_summary(&vesy);
475 avail = width - m1 -2;
477 dwid = MAX(MIN(40, avail), 0);
478 avail -= dwid;
480 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s%s", m1, m1, "",
481 repeat_char(dwid, '-'));
483 gf_puts(tmp_20k_buf, pc);
487 format_calendar(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc)
489 char *rawtext, *caltext;
490 unsigned long callen;
491 VCALENDAR_S *vcal = NULL;
492 ATTACH_S *a;
493 BODY *b;
495 if(flgs & FM_NEW_MESS) {
496 zero_atmts(ps_global->atmts);
497 describe_mime(body, "", 1, 1, 0, flgs);
500 for(a = ps_global->atmts; a->description != NULL; a++){
501 if(MIME_VCALENDAR(a->body->type, a->body->subtype)){
502 b = mail_body (ps_global->mail_stream, msgno, a->number);
503 if(b == NULL){
504 gf_puts(_("Error fetching calendar body part"), pc);
505 gf_puts(NEWLINE, pc);
506 continue;
508 if(b->sparep == NULL){
509 rawtext = mail_fetch_body(ps_global->mail_stream, msgno, a->number, &callen, 0);
510 if(rawtext == NULL || *rawtext == '\0'){
511 gf_puts(_("Error fetching calendar text"), pc);
512 gf_puts(NEWLINE, pc);
513 continue;
515 rawtext[callen] = '\0'; /* chop off cookie */
516 switch(b->encoding){
517 case ENCBASE64:
518 caltext = rfc822_base64(rawtext, strlen(rawtext), &callen);
519 if(caltext == NULL){
520 gf_puts(_("Error in calendar base64 encoding"), pc);
521 gf_puts(NEWLINE, pc);
522 continue;
524 break;
526 case ENCQUOTEDPRINTABLE:
527 caltext = rfc822_qprint ((unsigned char *) rawtext,strlen(rawtext),&callen);
528 if(caltext == NULL){
529 gf_puts(_("Error in calendar quoted printable encoding"), pc);
530 gf_puts(NEWLINE, pc);
531 continue;
533 break;
535 default: caltext = cpystr(rawtext);
537 if(caltext != NULL){
538 vcal = ical_parse_text(caltext);
539 if(vcal != NULL) vcal->encoding = b->encoding;
540 b->sparep = create_body_sparep(iCalType, (void *) vcal);
541 fs_give((void **) &caltext);
544 else if(get_body_sparep_type(b->sparep) == iCalType)
545 vcal = (VCALENDAR_S *) get_body_sparep_data(b->sparep);
546 if(vcal != NULL && vcal->comp != NULL){
547 if(vcal->comp[VEvent] != NULL){
548 format_calendar_vevent(vcal, a, handlesp, flgs, width, pc, FC_SUMMARY);
549 } /* else another type of entry in the calendar */
551 gf_puts(NEWLINE, pc);
554 return 0;
558 char *
559 format_body(long int msgno, BODY *body, HANDLE_S **handlesp, HEADER_S *hp, int flgs, int width, gf_io_t pc)
561 int filt_only_c0 = 0, wrapflags, error_found = 0;
562 int is_in_sig = OUT_SIG_BLOCK;
563 char *charset, *decode_err = NULL, *tmp1, *description;
564 ATTACH_S *a;
565 URL_HILITE_S uh;
566 gf_io_t gc;
568 if(body == NULL
569 || (ps_global->full_header == 2
570 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))) {
572 /*--- Server is not an IMAP2bis, It can't parse MIME
573 so we just show the text here. Hopefully the
574 message isn't a MIME message
575 ---*/
576 void *text2;
578 if((text2 = (void *)pine_mail_fetch_text(ps_global->mail_stream,
579 msgno, NULL, NULL, NIL)) != NULL){
581 if(!gf_puts(NEWLINE, pc)) /* write delimiter */
582 return("Write Error");
584 gf_set_readc(&gc, text2, (unsigned long)strlen(text2), CharStar, 0);
585 gf_filter_init();
588 * We need to translate the message
589 * into UTF-8, but that's trouble in the full header case
590 * because we don't know what to translate from. We'll just
591 * take a guess by looking for the first text part and
592 * using its charset.
594 if(body && body->type == TYPETEXT)
595 charset = parameter_val(body->parameter, "charset");
596 else if(body && body->type == TYPEMULTIPART && body->nested.part
597 && body->nested.part->body.type == TYPETEXT)
598 charset = parameter_val(body->nested.part->body.parameter, "charset");
599 else
600 charset = ps_global->display_charmap;
602 if(strucmp(charset, "us-ascii") && strucmp(charset, "utf-8")){
603 /* transliterate message text to UTF-8 */
604 gf_link_filter(gf_utf8, gf_utf8_opt(charset));
607 /* link in filters, similar to what is done in decode_text() */
608 if(!ps_global->pass_ctrl_chars){
609 gf_link_filter(gf_escape_filter, NULL);
610 filt_only_c0 = 1;
611 gf_link_filter(gf_control_filter,
612 gf_control_filter_opt(&filt_only_c0));
615 gf_link_filter(gf_tag_filter, NULL);
617 if((F_ON(F_VIEW_SEL_URL, ps_global)
618 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
619 || F_ON(F_SCAN_ADDR, ps_global))
620 && handlesp){
621 gf_link_filter(gf_line_test,
622 gf_line_test_opt(url_hilite,
623 gf_url_hilite_opt(&uh,handlesp,0)));
626 if((flgs & FM_DISPLAY)
627 && !(flgs & FM_NOCOLOR)
628 && pico_usingcolor()
629 && ps_global->VAR_SIGNATURE_FORE_COLOR
630 && ps_global->VAR_SIGNATURE_BACK_COLOR){
631 gf_link_filter(gf_line_test, gf_line_test_opt(color_signature, &is_in_sig));
634 if((flgs & FM_DISPLAY)
635 && !(flgs & FM_NOCOLOR)
636 && pico_usingcolor()
637 && ps_global->VAR_QUOTE1_FORE_COLOR
638 && ps_global->VAR_QUOTE1_BACK_COLOR){
639 gf_link_filter(gf_line_test, gf_line_test_opt(color_a_quote, NULL));
642 if(!(flgs & FM_NOWRAP)){
643 wrapflags = (flgs & FM_DISPLAY) ? (GFW_HANDLES|GFW_SOFTHYPHEN) : GFW_NONE;
644 if(flgs & FM_DISPLAY
645 && !(flgs & FM_NOCOLOR)
646 && pico_usingcolor())
647 wrapflags |= GFW_USECOLOR;
648 gf_link_filter(gf_wrap, gf_wrap_filter_opt(width, width,
649 (flgs & FM_NOINDENT)
650 ? NULL : format_view_margin(),
652 wrapflags));
655 gf_link_filter(gf_nvtnl_local, NULL);
656 if((decode_err = gf_pipe(gc, pc)) != NULL){
657 /* TRANSLATORS: There was an error putting together a message for
658 viewing. The arg is the description of the error. */
659 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Formatting error: %s"), decode_err);
660 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
661 if(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
662 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
663 && gf_puts(NEWLINE, pc))
664 decode_err = NULL;
665 else
666 return(decode_err);
670 if(!text2){
671 if(!gf_puts(NEWLINE, pc)
672 || !gf_puts(_(" [ERROR fetching text of message]"), pc)
673 || !gf_puts(NEWLINE, pc)
674 || !gf_puts(NEWLINE, pc))
675 return("Write Error");
678 else{
679 int show_parts = 0;
681 /*======== Now loop through formatting all the parts =======*/
682 for(a = ps_global->atmts; a->description != NULL; a++) {
683 if(MIME_VCALENDAR(a->body->type, a->body->subtype))
684 continue;
686 if(a->body->type == TYPEMULTIPART){
687 #ifdef SMIME
688 if(strucmp(a->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0){
689 if(a->description){
690 if(!(!format_editorial(a->description, width, flgs, handlesp, pc)
691 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
692 return("Write Error");
695 #endif /* SMIME */
696 continue;
699 if(!a->shown) {
700 if(a->suppress_editorial)
701 continue;
703 if(!(flgs & FM_NOEDITORIAL)
704 && (!gf_puts(NEWLINE, pc)
705 || (decode_err = part_desc(a->number, a->body,
706 (flgs & FM_DISPLAY)
707 ? (a->can_display != MCD_NONE)
708 ? 1 : 2
709 : 3, width, flgs, pc))))
710 return("Write Error");
712 continue;
715 switch(a->body->type){
717 case TYPETEXT:
719 * If a message is multipart *and* the first part of it
720 * is text *and that text is empty, there is a good chance that
721 * there was actually something there that c-client was
722 * unable to parse. Here we report the empty message body
723 * and insert the raw RFC822.TEXT (if full-headers are
724 * on).
726 if(body->type == TYPEMULTIPART
727 && a == ps_global->atmts
728 && a->body->size.bytes == 0
729 && F_ON(F_ENABLE_FULL_HDR, ps_global)){
730 char *err = NULL;
732 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
733 "Empty or malformed message%s.",
734 ps_global->full_header == 2
735 ? ". Displaying raw text"
736 : ". Use \"H\" to see raw text");
737 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
739 if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
740 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
741 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
742 return("Write Error");
744 if(ps_global->full_header == 2
745 && (err = detach_raw(ps_global->mail_stream, msgno,
746 a->number, pc, flgs))){
747 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
748 "%s%s [ Formatting error: %s ]%s%s",
749 NEWLINE, NEWLINE, err, NEWLINE, NEWLINE);
750 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
751 if(!gf_puts(tmp_20k_buf, pc))
752 return("Write Error");
755 break;
759 * Don't write our delimiter if this text part is
760 * the first part of a message/rfc822 segment...
762 if(show_parts && a != ps_global->atmts
763 && !((a[-1].body && a[-1].body->type == TYPEMESSAGE)
764 #ifdef SMIME
765 || (a[-1].body->type == TYPEMULTIPART
766 && a[-1].body->subtype
767 && (strucmp(a[-1].body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0)
768 && &a[-1] != ps_global->atmts
769 && a[-2].body && a[-2].body->type == TYPEMESSAGE)
770 #endif /* SMIME */
772 && !(flgs & FM_NOEDITORIAL)){
773 if(MIME_VCALENDAR(a->body->type, a->body->subtype))
774 tmp1 = "Calendar entry";
775 else
776 tmp1 = a->body->description ? a->body->description
777 : "Attached Text";
778 description = iutf8ncpy((char *)(tmp_20k_buf+10000),
779 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+15000), 5000, tmp1), 5000);
781 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Part %s: \"%.1024s\"", a->number,
782 description);
783 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
784 if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
785 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
786 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
787 return("Write Error");
789 /* skip calendar */
790 if(!MIME_VCALENDAR(a->body->type, a->body->subtype))
791 error_found += decode_text(a, msgno, pc, handlesp,
792 (flgs & FM_DISPLAY) ? InLine : QStatus,
793 flgs);
794 break;
796 case TYPEMESSAGE:
797 tmp1 = a->body->description ? a->body->description
798 : (strucmp(a->body->subtype, "delivery-status") == 0)
799 ? "Delivery Status"
800 : "Included Message";
801 description = iutf8ncpy((char *)(tmp_20k_buf+10000),
802 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+15000), 5000, tmp1), 5000);
804 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Part %s: \"%.1024s\"", a->number,
805 description);
806 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
808 if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
809 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
810 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
811 return("Write Error");
813 if(a->body->subtype && strucmp(a->body->subtype, "rfc822") == 0){
814 /* imapenvonly, we may not have all the headers we need */
815 if(a->body->nested.msg->env->imapenvonly)
816 mail_fetch_header(ps_global->mail_stream, msgno,
817 a->number, NULL, NULL, FT_PEEK);
818 switch(format_header(ps_global->mail_stream, msgno, a->number,
819 a->body->nested.msg->env, hp,
820 NULL, handlesp, flgs, NULL, pc)){
821 case -1 : /* write error */
822 return("Write Error");
824 case 1 : /* fetch error */
825 if(!(gf_puts("[ Error fetching header ]", pc)
826 && !gf_puts(NEWLINE, pc)))
827 return("Write Error");
829 break;
832 else if(a->body->subtype && strucmp(a->body->subtype, "external-body") == 0){
833 int *margin, avail, m1, m2;
835 avail = width;
836 margin = (flgs & FM_NOINDENT) ? NULL : format_view_margin();
838 m1 = MAX(MIN(margin ? margin[0] : 0, avail), 0);
839 avail -= m1;
841 m2 = MAX(MIN(margin ? margin[1] : 0, avail), 0);
842 avail -= m2;
844 if(format_editorial("This part is not included and can be fetched as follows:", avail, flgs, handlesp, pc)
845 || !gf_puts(NEWLINE, pc)
846 || format_editorial(display_parameters(a->body->parameter), avail, flgs, handlesp, pc))
847 return("Write Error");
849 else
850 error_found += decode_text(a, msgno, pc, handlesp,
851 (flgs&FM_DISPLAY) ? InLine : QStatus,
852 flgs);
854 if(!gf_puts(NEWLINE, pc))
855 return("Write Error");
857 break;
859 default:
860 if((decode_err = part_desc(a->number, a->body,
861 (flgs & FM_DISPLAY) ? 1 : 3,
862 width, flgs, pc)) != NULL)
863 return("Write Error");
866 show_parts++;
869 if(!(!error_found
870 && (pith_opt_rfc2369_editorial ? (*pith_opt_rfc2369_editorial)(msgno, handlesp, flgs, width, pc) : 1)
871 && format_blip_seen(msgno)))
872 return("Cannot format body.");
875 return(NULL);
880 format_attachment_list(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc)
882 ATTACH_S *a;
884 if(flgs & FM_NEW_MESS) {
885 zero_atmts(ps_global->atmts);
886 describe_mime(body, "", 1, 1, 0, flgs);
889 /*----- First do the list of parts/attachments if needed ----*/
890 if((flgs & FM_DISPLAY)
891 && (ps_global->atmts[1].description
892 || (ps_global->atmts[0].body
893 && ps_global->atmts[0].body->type != TYPETEXT))){
894 char tmp[6*MAX_SCREEN_COLS + 1], *tmpp;
895 int i, n, maxnumwid = 0, maxsizewid = 0, *margin;
896 int avail, m1, m2, hwid, s1, s2, s3, s4, s5, dwid, shownwid;
897 int sizewid, descwid, dashwid, partwid, padwid;
898 COLOR_PAIR *hdrcolor = NULL;
900 if((flgs & FM_DISPLAY)
901 && !(flgs & FM_NOCOLOR)
902 && pico_usingcolor()
903 && ps_global->VAR_HEADER_GENERAL_FORE_COLOR
904 && ps_global->VAR_HEADER_GENERAL_BACK_COLOR
905 && ps_global->VAR_NORM_FORE_COLOR
906 && ps_global->VAR_NORM_BACK_COLOR
907 && (colorcmp(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
908 ps_global->VAR_NORM_FORE_COLOR)
909 || colorcmp(ps_global->VAR_HEADER_GENERAL_BACK_COLOR,
910 ps_global->VAR_NORM_BACK_COLOR))){
912 if((hdrcolor = new_color_pair(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
913 ps_global->VAR_HEADER_GENERAL_BACK_COLOR)) != NULL){
914 if(!pico_is_good_colorpair(hdrcolor))
915 free_color_pair(&hdrcolor);
919 margin = (flgs & FM_NOINDENT) ? NULL : format_view_margin();
922 * Attachment list header
925 avail = width;
927 m1 = MAX(MIN(margin ? margin[0] : 0, avail), 0);
928 avail -= m1;
930 m2 = MAX(MIN(margin ? margin[1] : 0, avail), 0);
931 avail -= m2;
933 hwid = MAX(avail, 0);
935 i = utf8_width(_("Parts/Attachments:"));
936 partwid = MIN(i, hwid);
937 padwid = hdrcolor ? (hwid-partwid) : 0;
939 if(m1 > 0){
940 snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
941 if(!gf_puts(tmp, pc))
942 return(0);
945 utf8_snprintf(tmp, sizeof(tmp),
946 "%-*.*w%*.*s",
947 /* TRANSLATORS: A label */
948 partwid, partwid, _("Parts/Attachments:"),
949 padwid, padwid, "");
951 if(!((!hdrcolor || embed_color(hdrcolor, pc)) && gf_puts(tmp, pc) && gf_puts(NEWLINE, pc)))
952 return(0);
955 /*----- Figure max display widths -----*/
956 for(a = ps_global->atmts; a->description != NULL; a++){
957 if(MIME_VCALENDAR(a->body->type, a->body->subtype))
958 continue;
960 if((n = utf8_width(a->number)) > maxnumwid)
961 maxnumwid = n;
963 if((n = utf8_width(a->size)) > maxsizewid)
964 maxsizewid = n;
968 * ----- adjust max lengths for nice display -----
970 * marg _ D _ number _ Shown _ _ _ size _ _ description marg
974 avail = width - m1 - m2;
976 s1 = MAX(MIN(1, avail), 0);
977 avail -= s1;
979 dwid = MAX(MIN(1, avail), 0);
980 avail -= dwid;
982 s2 = MAX(MIN(1, avail), 0);
983 avail -= s2;
985 maxnumwid = MIN(maxnumwid, width/3);
986 maxnumwid = MAX(MIN(maxnumwid, avail), 0);
987 avail -= maxnumwid;
989 s3 = MAX(MIN(1, avail), 0);
990 avail -= s3;
992 shownwid = MAX(MIN(5, avail), 0);
993 avail -= shownwid;
995 s4 = MAX(MIN(3, avail), 0);
996 avail -= s4;
998 sizewid = MAX(MIN(maxsizewid, avail), 0);
999 avail -= sizewid;
1001 s5 = MAX(MIN(2, avail), 0);
1002 avail -= s5;
1004 descwid = MAX(0, avail);
1006 /*----- Format the list of attachments -----*/
1007 for(a = ps_global->atmts; a->description != NULL; a++){
1008 COLOR_PAIR *lastc = NULL;
1009 char numbuf[50];
1010 int thisdescwid, padwid;
1012 if(MIME_VCALENDAR(a->body->type, a->body->subtype))
1013 continue;
1014 #ifdef SMIME
1015 if(a->body->type == TYPEMULTIPART
1016 && (strucmp(a->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0))
1017 continue;
1018 #endif /* SMIME */
1020 i = utf8_width((descwid > 2 && a->description) ? a->description : "");
1021 thisdescwid = MIN(i, descwid);
1022 padwid = hdrcolor ? (descwid-thisdescwid) : 0;
1024 if(m1 > 0){
1025 snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
1026 if(!gf_puts(tmp, pc))
1027 return(0);
1030 utf8_snprintf(tmp, sizeof(tmp),
1031 "%*.*s%*.*w%*.*s%-*.*w%*.*s%*.*w%*.*s%*.*w%*.*s%-*.*w",
1032 s1, s1, "",
1033 dwid, dwid,
1034 msgno_part_deleted(ps_global->mail_stream, msgno, a->number) ? "D" : "",
1035 s2, s2, "",
1036 maxnumwid, maxnumwid,
1037 a->number
1038 ? short_str(a->number, numbuf, sizeof(numbuf), maxnumwid, FrontDots)
1039 : "",
1040 s3, s3, "",
1041 shownwid, shownwid,
1042 a->shown ? "Shown" :
1043 (a->can_display != MCD_NONE && !(a->can_display & MCD_EXT_PROMPT))
1044 ? "OK " : "",
1045 s4, s4, "",
1046 sizewid, sizewid,
1047 a->size ? a->size : "",
1048 s5, s5, "",
1049 thisdescwid, thisdescwid,
1050 (descwid > 2 && a->description) ? a->description : "");
1052 if(!(!hdrcolor || embed_color(hdrcolor, pc)))
1053 return(0);
1055 if(F_ON(F_VIEW_SEL_ATTACH, ps_global) && handlesp){
1056 char buf[16], color[64];
1057 int l;
1058 HANDLE_S *h;
1060 for(tmpp = tmp; *tmpp && *tmpp == ' '; tmpp++)
1061 if(!(*pc)(' '))
1062 return(0);
1064 h = new_handle(handlesp);
1065 h->type = Attach;
1066 h->h.attach = a;
1068 snprintf(buf, sizeof(buf), "%d", h->key);
1069 buf[sizeof(buf)-1] = '\0';
1071 if(!(flgs & FM_NOCOLOR)
1072 && handle_start_color(color, sizeof(color), &l, 1)){
1073 lastc = get_cur_embedded_color();
1074 if(!gf_nputs(color, (long) l, pc))
1075 return(0);
1077 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)
1078 && (!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON))))
1079 return(0);
1081 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE)
1082 && (*pc)(strlen(buf)) && gf_puts(buf, pc)))
1083 return(0);
1085 else
1086 tmpp = tmp;
1088 if(!format_env_puts(tmpp, pc))
1089 return(0);
1091 if(F_ON(F_VIEW_SEL_ATTACH, ps_global) && handlesp){
1092 if(lastc){
1093 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
1094 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
1095 return(0);
1098 if(!embed_color(lastc, pc))
1099 return(0);
1101 free_color_pair(&lastc);
1103 else if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
1104 return(0);
1106 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)))
1107 return(0);
1110 if(padwid > 0){
1111 snprintf(tmp, sizeof(tmp), "%*.*s", padwid, padwid, "");
1112 if(!gf_puts(tmp, pc))
1113 return(0);
1116 if(!gf_puts(NEWLINE, pc))
1117 return(0);
1121 * Dashed line after list
1124 if(hdrcolor){
1125 avail = width - m1 - m2;
1126 hwid = MAX(avail, 0);
1128 dashwid = MAX(MIN(40, hwid-2), 0);
1129 padwid = hwid - dashwid;
1130 if(m1 > 0){
1131 snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
1132 if(!gf_puts(tmp, pc))
1133 return(0);
1136 snprintf(tmp, sizeof(tmp),
1137 "%s%*.*s",
1138 repeat_char(dashwid, '-'),
1139 padwid, padwid, "");
1141 else{
1142 avail = width - m1 -2;
1144 dashwid = MAX(MIN(40, avail), 0);
1145 avail -= dashwid;
1147 snprintf(tmp, sizeof(tmp),
1148 "%*.*s%s",
1149 m1, m1, "",
1150 repeat_char(dashwid, '-'));
1153 if(!((!hdrcolor || embed_color(hdrcolor, pc)) && gf_puts(tmp, pc) && gf_puts(NEWLINE, pc)))
1154 return(0);
1156 if(hdrcolor)
1157 free_color_pair(&hdrcolor);
1160 return(1);
1166 * format_blip_seen - if seen bit (which is usually cleared as a side-effect
1167 * of body part fetches as we're formatting) for the
1168 * given message isn't set (likely because there
1169 * weren't any parts suitable for display), then make
1170 * sure to set it here.
1173 format_blip_seen(long int msgno)
1175 MESSAGECACHE *mc;
1177 if(msgno > 0L && ps_global->mail_stream
1178 && msgno <= ps_global->mail_stream->nmsgs
1179 && (mc = mail_elt(ps_global->mail_stream, msgno))
1180 && !mc->seen
1181 && !ps_global->mail_stream->rdonly)
1182 mail_flag(ps_global->mail_stream, long2string(msgno), "\\SEEN", ST_SET);
1184 return(1);
1189 * is_an_env_hdr - is this name a header in the envelope structure?
1191 * name - the header name to check
1194 is_an_env_hdr(char *name)
1196 register int i;
1198 for(i = 0; envelope_hdrs[i].name; i++)
1199 if(!strucmp(name, envelope_hdrs[i].name))
1200 return(1);
1202 return(0);
1209 * is_an_addr_hdr - is this an address header?
1211 * name - the header name to check
1214 is_an_addr_hdr(char *fieldname)
1216 char fbuf[FBUF_LEN+1];
1217 char *colon, *fname;
1218 static char *addr_headers[] = {
1219 "from",
1220 "reply-to",
1221 "to",
1222 "cc",
1223 "bcc",
1224 "return-path",
1225 "sender",
1226 "x-sender",
1227 "x-x-sender",
1228 "resent-from",
1229 "resent-to",
1230 "resent-cc",
1231 NULL
1234 /* so it is pointing to NULL */
1235 char **p = addr_headers + sizeof(addr_headers)/sizeof(*addr_headers) - 1;
1237 if((colon = strindex(fieldname, ':')) != NULL){
1238 strncpy(fbuf, fieldname, MIN(colon-fieldname,sizeof(fbuf)));
1239 fbuf[MIN(colon-fieldname,sizeof(fbuf)-1)] = '\0';
1240 fname = fbuf;
1242 else
1243 fname = fieldname;
1245 if(fname && *fname){
1246 for(p = addr_headers; *p; p++)
1247 if(!strucmp(fname, *p))
1248 break;
1251 return((*p) ? 1 : 0);
1256 * Format a single field from the envelope
1258 void
1259 format_env_hdr(MAILSTREAM *stream, long int msgno, char *section, ENVELOPE *env,
1260 fmt_env_t fmt_env, gf_io_t pc, char *field, char *oacs, int flags)
1262 register int i;
1264 if(!fmt_env)
1265 fmt_env = format_envelope;
1267 for(i = 0; envelope_hdrs[i].name; i++)
1268 if(!strucmp(field, envelope_hdrs[i].name)){
1269 (*fmt_env)(stream, msgno, section, env, pc, envelope_hdrs[i].val, oacs, flags);
1270 return;
1276 * Look through header string beginning with "begin", for the next
1277 * occurrence of header "field". Set "start" to that. Set "end" to point one
1278 * position past all of the continuation lines that go with "field".
1279 * That is, if "end" is converted to a null
1280 * character then the string "start" will be the next occurence of header
1281 * "field" including all of its continuation lines. Assume we
1282 * have CRLF's as end of lines.
1284 * If "field" is NULL, then we just leave "start" pointing to "begin" and
1285 * make "end" the end of that header.
1287 * Returns 1 if found, 0 if not.
1290 delineate_this_header(char *field, char *begin, char **start, char **end)
1292 char tmpfield[MAILTMPLEN+2]; /* copy of field with colon appended */
1293 char *p;
1294 char *begin_srch;
1296 if(field == NULL){
1297 if(!begin || !*begin || isspace((unsigned char)*begin))
1298 return 0;
1299 else
1300 *start = begin;
1302 else{
1303 strncpy(tmpfield, field, sizeof(tmpfield)-2);
1304 tmpfield[sizeof(tmpfield)-2] = '\0';
1305 strncat(tmpfield, ":", sizeof(tmpfield)-strlen(tmpfield)-1);
1306 tmpfield[sizeof(tmpfield)-1] = '\0';
1309 * We require that start is at the beginning of a line, so
1310 * either it equals begin (which we assume is the beginning of a
1311 * line) or it is preceded by a CRLF.
1313 begin_srch = begin;
1314 *start = srchstr(begin_srch, tmpfield);
1315 while(*start && *start != begin
1316 && !(*start - 2 >= begin && ISRFCEOL(*start - 2))){
1317 begin_srch = *start + 1;
1318 *start = srchstr(begin_srch, tmpfield);
1321 if(!*start)
1322 return 0;
1325 for(p = *start; *p; p++){
1326 if(ISRFCEOL(p)
1327 && (!isspace((unsigned char)*(p+2)) || *(p+2) == '\015')){
1329 * The final 015 in the test above is to test for the end
1330 * of the headers.
1332 *end = p+2;
1333 break;
1337 if(!*p)
1338 *end = p;
1340 return 1;
1346 handle_start_color(char *colorstring, size_t buflen, int *len, int use_hdr_color)
1348 *len = 0;
1350 if(pico_usingcolor()){
1351 char *fg = NULL, *bg = NULL, *s;
1352 char *basefg = NULL, *basebg = NULL;
1354 basefg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_FORE_COLOR
1355 : ps_global->VAR_NORM_FORE_COLOR;
1356 basebg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_BACK_COLOR
1357 : ps_global->VAR_NORM_BACK_COLOR;
1359 if(ps_global->VAR_SLCTBL_FORE_COLOR
1360 && colorcmp(ps_global->VAR_SLCTBL_FORE_COLOR, basefg))
1361 fg = ps_global->VAR_SLCTBL_FORE_COLOR;
1363 if(ps_global->VAR_SLCTBL_BACK_COLOR
1364 && colorcmp(ps_global->VAR_SLCTBL_BACK_COLOR, basebg))
1365 bg = ps_global->VAR_SLCTBL_BACK_COLOR;
1367 if(bg || fg){
1368 COLOR_PAIR *tmp;
1371 * The blacks are just known good colors for
1372 * testing whether the other color is good.
1374 if((tmp = new_color_pair(fg ? fg : colorx(COL_BLACK),
1375 bg ? bg : colorx(COL_BLACK))) != NULL){
1376 if(pico_is_good_colorpair(tmp))
1377 for(s = color_embed(fg, bg);
1378 (*len) < buflen && (colorstring[*len] = *s);
1379 s++, (*len)++)
1382 free_color_pair(&tmp);
1386 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
1387 strncpy(colorstring + (*len), url_embed(TAG_BOLDON), MIN(3,buflen-(*len)));
1388 *len += 2;
1392 colorstring[buflen-1] = '\0';
1394 return(*len != 0);
1399 handle_end_color(char *colorstring, size_t buflen, int *len)
1401 *len = 0;
1402 if(pico_usingcolor()){
1403 char *fg = NULL, *bg = NULL, *s;
1406 * We need to change the fg and bg colors back even if they
1407 * are the same as the Normal Colors so that color_a_quote
1408 * will have a chance to pick up those colors as the ones to
1409 * switch to. We don't do this before the handle above so that
1410 * the quote color will flow into the selectable item when
1411 * the selectable item color is partly the same as the
1412 * normal color. That is, suppose the normal color was black on
1413 * cyan and the selectable color was blue on cyan, only a fg color
1414 * change. We preserve the only-a-fg-color-change in a quote by
1415 * letting the quote background color flow into the selectable text.
1417 if(ps_global->VAR_SLCTBL_FORE_COLOR)
1418 fg = ps_global->VAR_NORM_FORE_COLOR;
1420 if(ps_global->VAR_SLCTBL_BACK_COLOR)
1421 bg = ps_global->VAR_NORM_BACK_COLOR;
1423 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
1424 strncpy(colorstring, url_embed(TAG_BOLDOFF), MIN(3,buflen));
1425 *len = 2;
1428 if(fg || bg)
1429 for(s = color_embed(fg, bg); (*len) < buflen && (colorstring[*len] = *s); s++, (*len)++)
1433 colorstring[buflen-1] = '\0';
1435 return(*len != 0);
1439 char *
1440 url_embed(int embed)
1442 static char buf[3] = {TAG_EMBED};
1443 buf[1] = embed;
1444 buf[2] = '\0';
1445 return(buf);
1450 * Paint the signature.
1453 color_signature(long int linenum, char *line, LT_INS_S **ins, void *is_in_sig)
1455 struct variable *vars = ps_global->vars;
1456 int *in_sig_block;
1457 COLOR_PAIR *col = NULL;
1459 if(is_in_sig == NULL)
1460 return 0;
1462 in_sig_block = (int *) is_in_sig;
1464 if(!strcmp(line, SIGDASHES))
1465 *in_sig_block = START_SIG_BLOCK;
1466 else if(*line == '\0')
1468 * Suggested by Eduardo: allow for a blank line right after
1469 * the sigdashes.
1471 *in_sig_block = (*in_sig_block == START_SIG_BLOCK)
1472 ? IN_SIG_BLOCK : OUT_SIG_BLOCK;
1473 else
1474 *in_sig_block = (*in_sig_block != OUT_SIG_BLOCK)
1475 ? IN_SIG_BLOCK : OUT_SIG_BLOCK;
1477 if(*in_sig_block != OUT_SIG_BLOCK
1478 && VAR_SIGNATURE_FORE_COLOR && VAR_SIGNATURE_BACK_COLOR
1479 && (col = new_color_pair(VAR_SIGNATURE_FORE_COLOR,
1480 VAR_SIGNATURE_BACK_COLOR))){
1481 if(!pico_is_good_colorpair(col))
1482 free_color_pair(&col);
1485 if(col){
1486 char *p, fg[RGBLEN + 1], bg[RGBLEN + 1], rgbbuf[RGBLEN + 1];
1488 ins = gf_line_test_new_ins(ins, line,
1489 color_embed(col->fg, col->bg),
1490 (2 * RGBLEN) + 4);
1492 strncpy(fg, color_to_asciirgb(VAR_NORM_FORE_COLOR), sizeof(fg));
1493 fg[sizeof(fg)-1] = '\0';
1494 strncpy(bg, color_to_asciirgb(VAR_NORM_BACK_COLOR), sizeof(bg));
1495 bg[sizeof(bg)-1] = '\0';
1498 * Loop watching colors, and override with
1499 * signature color whenever the normal foreground and background
1500 * colors are in force.
1503 for(p = line; *p; )
1504 if(*p++ == TAG_EMBED){
1506 switch(*p++){
1507 case TAG_HANDLE :
1508 p += *p + 1; /* skip handle key */
1509 break;
1511 case TAG_FGCOLOR :
1512 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1513 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1514 p += RGBLEN; /* advance past color value */
1516 if(!colorcmp(rgbbuf, VAR_NORM_FORE_COLOR)
1517 && !colorcmp(bg, VAR_NORM_BACK_COLOR))
1518 ins = gf_line_test_new_ins(ins, p,
1519 color_embed(col->fg,NULL),
1520 RGBLEN + 2);
1521 break;
1523 case TAG_BGCOLOR :
1524 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1525 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1526 p += RGBLEN; /* advance past color value */
1528 if(!colorcmp(rgbbuf, VAR_NORM_BACK_COLOR)
1529 && !colorcmp(fg, VAR_NORM_FORE_COLOR))
1530 ins = gf_line_test_new_ins(ins, p,
1531 color_embed(NULL,col->bg),
1532 RGBLEN + 2);
1534 break;
1536 default :
1537 break;
1541 ins = gf_line_test_new_ins(ins, line + strlen(line),
1542 color_embed(VAR_NORM_FORE_COLOR,
1543 VAR_NORM_BACK_COLOR),
1544 (2 * RGBLEN) + 4);
1545 free_color_pair(&col);
1548 return 0;
1553 * Line filter to add color to displayed headers.
1556 color_headers(long int linenum, char *line, LT_INS_S **ins, void *local)
1558 static char field[FBUF_LEN + 1];
1559 char fg[RGBLEN + 1], bg[RGBLEN + 1], rgbbuf[RGBLEN + 1];
1560 char *p, *q, *value, *beg;
1561 COLOR_PAIR *color;
1562 int in_quote = 0, in_comment = 0, did_color = 0;
1563 struct variable *vars = ps_global->vars;
1565 field[FBUF_LEN] = '\0';
1567 if(isspace((unsigned char)*line)) /* continuation line */
1568 value = line;
1569 else{
1570 if(!(value = strindex(line, ':')))
1571 return(0);
1573 memset(field, 0, sizeof(field));
1574 strncpy(field, line, MIN(value-line, sizeof(field)-1));
1577 for(value++; isspace((unsigned char)*value); value++)
1580 strncpy(fg, color_to_asciirgb(VAR_HEADER_GENERAL_FORE_COLOR), sizeof(fg));
1581 fg[sizeof(fg)-1] = '\0';
1582 strncpy(bg, color_to_asciirgb(VAR_HEADER_GENERAL_BACK_COLOR), sizeof(bg));
1583 bg[sizeof(bg)-1] = '\0';
1586 * Split into two cases depending on whether this is a header which
1587 * contains addresses or not. We may color addresses separately.
1589 if(is_an_addr_hdr(field)){
1592 * If none of the patterns are for this header, don't bother parsing
1593 * and checking each address.
1595 if(!any_hdr_color(field))
1596 return(0);
1599 * First check for patternless patterns which color whole line.
1601 if((color = hdr_color(field, NULL, ps_global->hdr_colors)) != NULL){
1602 if(pico_is_good_colorpair(color)){
1603 ins = gf_line_test_new_ins(ins, value,
1604 color_embed(color->fg, color->bg),
1605 (2 * RGBLEN) + 4);
1606 strncpy(fg, color_to_asciirgb(color->fg), sizeof(fg));
1607 fg[sizeof(fg)-1] = '\0';
1608 strncpy(bg, color_to_asciirgb(color->bg), sizeof(bg));
1609 bg[sizeof(bg)-1] = '\0';
1610 did_color++;
1612 else
1613 free_color_pair(&color);
1617 * Then go through checking address by address.
1618 * Keep track of quotes and watch for color changes, and override
1619 * with most recent header color whenever the normal foreground
1620 * and background colors are in force.
1622 beg = p = value;
1623 while(*p){
1624 switch(*p){
1625 case '\\':
1626 /* skip next character */
1627 if(*(p+1) && (in_comment || in_quote))
1628 p += 2;
1629 else
1630 p++;
1632 break;
1634 case '"':
1635 if(!in_comment)
1636 in_quote = 1 - in_quote;
1638 p++;
1639 break;
1641 case '(':
1642 in_comment++;
1643 p++;
1644 break;
1646 case ')':
1647 if(in_comment > 0)
1648 in_comment--;
1650 p++;
1651 break;
1653 case ',':
1654 if(!(in_quote || in_comment)){
1655 /* we reached the end of this address */
1656 *p = '\0';
1657 if(color)
1658 free_color_pair(&color);
1660 if((color = hdr_color(field, beg,
1661 ps_global->hdr_colors)) != NULL){
1662 if(pico_is_good_colorpair(color)){
1663 did_color++;
1664 ins = gf_line_test_new_ins(ins, beg,
1665 color_embed(color->fg,
1666 color->bg),
1667 (2 * RGBLEN) + 4);
1668 *p = ',';
1669 for(q = p; q > beg &&
1670 isspace((unsigned char)*(q-1)); q--)
1673 ins = gf_line_test_new_ins(ins, q,
1674 color_embed(fg, bg),
1675 (2 * RGBLEN) + 4);
1677 else
1678 free_color_pair(&color);
1680 else
1681 *p = ',';
1683 for(p++; isspace((unsigned char)*p); p++)
1686 beg = p;
1688 else
1689 p++;
1691 break;
1693 case TAG_EMBED:
1694 switch(*(++p)){
1695 case TAG_HANDLE:
1696 p++;
1697 p += *p + 1; /* skip handle key */
1698 break;
1700 case TAG_FGCOLOR:
1701 p++;
1702 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1703 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1704 p += RGBLEN; /* advance past color value */
1706 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_FORE_COLOR))
1707 ins = gf_line_test_new_ins(ins, p,
1708 color_embed(color->fg,NULL),
1709 RGBLEN + 2);
1710 break;
1712 case TAG_BGCOLOR:
1713 p++;
1714 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1715 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1716 p += RGBLEN; /* advance past color value */
1718 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_BACK_COLOR))
1719 ins = gf_line_test_new_ins(ins, p,
1720 color_embed(NULL,color->bg),
1721 RGBLEN + 2);
1723 break;
1725 default:
1726 break;
1729 break;
1731 default:
1732 p++;
1733 break;
1737 for(q = beg; *q && isspace((unsigned char)*q); q++)
1740 if(*q && !(in_quote || in_comment)){
1741 /* we reached the end of this address */
1742 if(color)
1743 free_color_pair(&color);
1745 if((color = hdr_color(field, beg, ps_global->hdr_colors)) != NULL){
1746 if(pico_is_good_colorpair(color)){
1747 did_color++;
1748 ins = gf_line_test_new_ins(ins, beg,
1749 color_embed(color->fg,
1750 color->bg),
1751 (2 * RGBLEN) + 4);
1752 for(q = p; q > beg && isspace((unsigned char)*(q-1)); q--)
1755 ins = gf_line_test_new_ins(ins, q,
1756 color_embed(fg, bg),
1757 (2 * RGBLEN) + 4);
1759 else
1760 free_color_pair(&color);
1764 if(color)
1765 free_color_pair(&color);
1767 if(did_color)
1768 ins = gf_line_test_new_ins(ins, line + strlen(line),
1769 color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
1770 VAR_HEADER_GENERAL_BACK_COLOR),
1771 (2 * RGBLEN) + 4);
1773 else{
1775 color = hdr_color(field, value, ps_global->hdr_colors);
1777 if(color){
1778 if(pico_is_good_colorpair(color)){
1779 ins = gf_line_test_new_ins(ins, value,
1780 color_embed(color->fg, color->bg),
1781 (2 * RGBLEN) + 4);
1784 * Loop watching colors, and override with header
1785 * color whenever the normal foreground and background
1786 * colors are in force.
1788 p = value;
1789 while(*p)
1790 if(*p++ == TAG_EMBED){
1792 switch(*p++){
1793 case TAG_HANDLE:
1794 p += *p + 1; /* skip handle key */
1795 break;
1797 case TAG_FGCOLOR:
1798 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1799 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1800 p += RGBLEN; /* advance past color value */
1802 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_FORE_COLOR)
1803 && !colorcmp(bg, VAR_HEADER_GENERAL_BACK_COLOR))
1804 ins = gf_line_test_new_ins(ins, p,
1805 color_embed(color->fg,NULL),
1806 RGBLEN + 2);
1807 break;
1809 case TAG_BGCOLOR:
1810 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1811 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1812 p += RGBLEN; /* advance past color value */
1814 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_BACK_COLOR)
1815 && !colorcmp(fg, VAR_HEADER_GENERAL_FORE_COLOR))
1816 ins = gf_line_test_new_ins(ins, p,
1817 color_embed(NULL,color->bg),
1818 RGBLEN + 2);
1820 break;
1822 default:
1823 break;
1827 ins = gf_line_test_new_ins(ins, line + strlen(line),
1828 color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
1829 VAR_HEADER_GENERAL_BACK_COLOR),
1830 (2 * RGBLEN) + 4);
1833 free_color_pair(&color);
1837 return(0);
1842 url_hilite(long int linenum, char *line, LT_INS_S **ins, void *local)
1844 register char *lp, *up = NULL, *urlp = NULL,
1845 *weburlp = NULL, *mailurlp = NULL;
1846 int n, n1, n2, n3, l;
1847 char buf[256], color[256];
1848 HANDLE_S *h;
1849 URL_HILITE_S *uh;
1851 for(lp = line; ; lp = up + n){
1852 /* scan for all of them so we can choose the first */
1853 if(F_ON(F_VIEW_SEL_URL,ps_global))
1854 urlp = rfc1738_scan(lp, &n1);
1855 if(F_ON(F_VIEW_SEL_URL_HOST,ps_global))
1856 weburlp = web_host_scan(lp, &n2);
1857 if(F_ON(F_SCAN_ADDR,ps_global))
1858 mailurlp = mail_addr_scan(lp, &n3);
1860 if(urlp || weburlp || mailurlp){
1861 up = urlp ? urlp :
1862 weburlp ? weburlp : mailurlp;
1863 if(up == urlp && weburlp && weburlp < up)
1864 up = weburlp;
1865 if(mailurlp && mailurlp < up)
1866 up = mailurlp;
1868 if(up == urlp){
1869 n = n1;
1870 weburlp = mailurlp = NULL;
1872 else if(up == weburlp){
1873 n = n2;
1874 mailurlp = NULL;
1876 else{
1877 n = n3;
1878 weburlp = NULL;
1881 else
1882 break;
1884 uh = (URL_HILITE_S *) local;
1886 h = new_handle(uh->handlesp);
1887 h->type = URL;
1888 h->h.url.path = (char *) fs_get((n + 10) * sizeof(char));
1889 snprintf(h->h.url.path, n+10, "%s%.*s",
1890 weburlp ? "http://" : (mailurlp ? "mailto:" : ""), n, up);
1891 h->h.url.path[n+10-1] = '\0';
1893 if(handle_start_color(color, sizeof(color), &l, uh->hdr_color))
1894 ins = gf_line_test_new_ins(ins, up, color, l);
1895 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global))
1896 ins = gf_line_test_new_ins(ins, up, url_embed(TAG_BOLDON), 2);
1898 buf[0] = TAG_EMBED;
1899 buf[1] = TAG_HANDLE;
1900 snprintf(&buf[3], sizeof(buf)-3, "%d", h->key);
1901 buf[sizeof(buf)-1] = '\0';
1902 buf[2] = strlen(&buf[3]);
1903 ins = gf_line_test_new_ins(ins, up, buf, (int) buf[2] + 3);
1905 /* in case it was the current selection */
1906 ins = gf_line_test_new_ins(ins, up + n, url_embed(TAG_INVOFF), 2);
1908 if(scroll_handle_end_color(color, sizeof(color), &l, uh->hdr_color))
1909 ins = gf_line_test_new_ins(ins, up + n, color, l);
1910 else
1911 ins = gf_line_test_new_ins(ins, up + n, url_embed(TAG_BOLDOFF), 2);
1913 urlp = weburlp = mailurlp = NULL;
1916 return(0);
1921 url_hilite_hdr(long int linenum, char *line, LT_INS_S **ins, void *local)
1923 static int check_for_urls = 0;
1924 register char *lp;
1926 if(isspace((unsigned char)*line)) /* continuation, check or not
1927 depending on last line */
1928 lp = line;
1929 else{
1930 check_for_urls = 0;
1931 if((lp = strchr(line, ':')) != NULL){ /* there ought to always be a colon */
1932 FieldType ft;
1934 *lp = '\0';
1936 if(((ft = pine_header_standard(line)) == FreeText
1937 || ft == Subject
1938 || ft == TypeUnknown)
1939 && strucmp(line, "message-id")
1940 && strucmp(line, "newsgroups")
1941 && strucmp(line, "references")
1942 && strucmp(line, "in-reply-to")
1943 && strucmp(line, "received")
1944 && strucmp(line, "date")){
1945 check_for_urls = 1;
1948 *lp = ':';
1952 if(check_for_urls)
1953 (void) url_hilite(linenum, lp + 1, ins, local);
1955 return(0);
1960 pad_to_right_edge(long int linenum, char *line, LT_INS_S **ins, void *local)
1962 char *p;
1963 int wid = 0;
1964 int total_wid;
1965 struct variable *vars = ps_global->vars;
1967 if(!line[0])
1968 return 0;
1970 total_wid = *((int *) local);
1972 /* calculate width of line */
1973 p = line;
1974 while(*p){
1976 switch(*p){
1977 case TAG_EMBED:
1978 p++;
1979 switch(*p){
1980 case TAG_HANDLE:
1981 p++;
1982 p += *p + 1; /* skip handle key */
1983 break;
1985 case TAG_FGCOLOR :
1986 case TAG_BGCOLOR :
1987 p += (RGBLEN + 1); /* 1 for TAG, RGBLEN for color */
1988 break;
1990 case TAG_INVON:
1991 case TAG_INVOFF:
1992 case TAG_BOLDON:
1993 case TAG_BOLDOFF:
1994 case TAG_ULINEON:
1995 case TAG_ULINEOFF:
1996 p++;
1997 break;
1999 default: /* literal embed char */
2000 break;
2003 break;
2005 case TAB:
2006 p++;
2007 while(((++wid) & 0x07) != 0) /* add tab's spaces */
2010 break;
2012 default:
2013 wid += width_at_this_position((unsigned char *) p, strlen(p));
2014 p++;
2015 break;
2019 if(total_wid > wid){
2020 ins = gf_line_test_new_ins(ins, line + strlen(line),
2021 color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
2022 VAR_HEADER_GENERAL_BACK_COLOR),
2023 (2 * RGBLEN) + 4);
2024 ins = gf_line_test_new_ins(ins, line+strlen(line),
2025 repeat_char(total_wid-wid, ' '), total_wid-wid);
2026 ins = gf_line_test_new_ins(ins, line + strlen(line),
2027 color_embed(VAR_NORM_FORE_COLOR,
2028 VAR_NORM_BACK_COLOR),
2029 (2 * RGBLEN) + 4);
2032 return(0);
2037 #define UES_LEN 12
2038 #define UES_MAX 32
2040 url_external_specific_handler(char *url, int len)
2042 static char list[UES_LEN * UES_MAX];
2044 if(url){
2045 char *p;
2046 int i;
2048 for(i = 0; i < UES_MAX && *(p = &list[i * UES_LEN]); i++)
2049 if(strlen(p) == len && !struncmp(p, url, len))
2050 return(1);
2052 else{ /* initialize! */
2053 char **l, *test, *cmd, *p, *p2;
2054 int i = 0, n;
2056 memset(list, 0, sizeof(list));
2057 for(l = ps_global->VAR_BROWSER ; l && *l; l++){
2058 get_pair(*l, &test, &cmd, 1, 1);
2060 if((p = srchstr(test, "_scheme(")) && (p2 = strstr(p+8, ")_"))){
2061 *p2 = '\0';
2063 for(p += 8; *p && i < UES_MAX; p += n)
2064 if((p2 = strchr(p, ',')) != NULL){
2065 if((n = p2 - p) < UES_LEN){
2066 strncpy(&list[i * UES_LEN], p, MIN(n, sizeof(list)-(i * UES_LEN)));
2067 i++;
2069 else
2070 dprint((1,
2071 "* * * HANLDER TOO LONG: %.*s\n", n,
2072 p ? p : "?"));
2074 n++;
2076 else{
2077 if(strlen(p) <= UES_LEN){
2078 strncpy(&list[i * UES_LEN], p, sizeof(list)-(i * UES_LEN));
2079 i++;
2082 break;
2086 if(test)
2087 fs_give((void **) &test);
2089 if(cmd)
2090 fs_give((void **) &cmd);
2094 return(0);
2099 url_imap_folder(char *true_url, char **folder, imapuid_t *uid_val,
2100 imapuid_t *uid, char **search, int silent)
2102 char *url, *scheme, *p, *cmd, *server = NULL,
2103 *user = NULL, *auth = NULL, *mailbox = NULL,
2104 *section = NULL;
2105 size_t l;
2106 int rv = URL_IMAP_ERROR;
2109 * Since we're planting nulls, operate on a temporary copy...
2111 scheme = silent ? NULL : "IMAP";
2112 url = cpystr(true_url + 7);
2114 /* Try to pick apart the "iserver" portion */
2115 if((cmd = strchr(url, '/')) != NULL){ /* iserver "/" [mailbox] ? */
2116 *cmd++ = '\0';
2118 else{
2119 dprint((2, "-- URL IMAP FOLDER: missing: %s\n",
2120 url ? url : "?"));
2121 cmd = &url[strlen(url)-1]; /* assume only iserver */
2124 if((p = strchr(url, '@')) != NULL){ /* user | auth | pass? */
2125 *p++ = '\0';
2126 server = rfc1738_str(p);
2128 /* only ";auth=*" supported (and also ";auth=anonymous") */
2129 if((p = srchstr(url, ";auth=")) != NULL){
2130 *p = '\0';
2131 auth = rfc1738_str(p + 6);
2134 if(*url)
2135 user = rfc1738_str(url);
2137 else
2138 server = rfc1738_str(url);
2140 if(!*server)
2141 return(url_bogus_imap(&url, scheme, "No server specified"));
2144 * "iserver" in hand, pick apart the "icommand"...
2146 p = NULL;
2147 if(!*cmd || (p = srchstr(cmd, ";type="))){
2148 char *criteria;
2151 * No "icommand" (all top-level folders) or "imailboxlist"...
2153 if(p){
2154 *p = '\0'; /* tie off criteria */
2155 criteria = rfc1738_str(cmd); /* get "enc_list_mailbox" */
2156 if(!strucmp(p = rfc1738_str(p+6), "lsub"))
2157 rv |= URL_IMAP_IMBXLSTLSUB;
2158 else if(strucmp(p, "list"))
2159 return(url_bogus_imap(&url, scheme,
2160 "Invalid list type specified"));
2162 else{
2163 rv |= URL_IMAP_ISERVERONLY;
2164 criteria = "";
2167 /* build folder list from specified server/criteria/list-method */
2168 l = strlen(server) + strlen(criteria) + 10 + (user ? (strlen(user)+2) : 9);
2169 *folder = (char *) fs_get((l+1) * sizeof(char));
2170 snprintf(*folder, l+1, "{%s/%s%s%s}%s%s%s", server,
2171 user ? "user=\"" : "Anonymous",
2172 user ? user : "",
2173 user ? "\"" : "",
2174 *criteria ? "[" : "", criteria, *criteria ? "[" : "");
2175 (*folder)[l] = '\0';
2176 rv |= URL_IMAP_IMAILBOXLIST;
2178 else{
2179 if((p = srchstr(cmd, "/;uid=")) != NULL){ /* "imessagepart" */
2180 *p = '\0'; /* tie off mailbox [uidvalidity] */
2181 if((section = srchstr(p += 6, "/;section=")) != NULL){
2182 *section = '\0'; /* tie off UID */
2183 section = rfc1738_str(section + 10);
2184 /* BUG: verify valid section spec ala rfc 2060 */
2185 dprint((2,
2186 "-- URL IMAP FOLDER: section not used: %s\n",
2187 section ? section : "?"));
2190 if(!(*uid = rfc1738_num(&p)) || *p) /* decode UID */
2191 return(url_bogus_imap(&url, scheme, "Invalid data in UID"));
2193 /* optional "uidvalidity"? */
2194 if((p = srchstr(cmd, ";uidvalidity=")) != NULL){
2195 *p = '\0';
2196 p += 13;
2197 if(!(*uid_val = rfc1738_num(&p)) || *p)
2198 return(url_bogus_imap(&url, scheme,
2199 "Invalid UIDVALIDITY"));
2202 mailbox = rfc1738_str(cmd);
2203 rv = URL_IMAP_IMESSAGEPART;
2205 else{ /* "imessagelist" */
2206 /* optional "uidvalidity"? */
2207 if((p = srchstr(cmd, ";uidvalidity=")) != NULL){
2208 *p = '\0';
2209 p += 13;
2210 if(!(*uid_val = rfc1738_num(&p)) || *p)
2211 return(url_bogus_imap(&url, scheme,
2212 "Invalid UIDVALIDITY"));
2215 /* optional "enc_search"? */
2216 if((p = strchr(cmd, '?')) != NULL){
2217 *p = '\0';
2218 if(search)
2219 *search = cpystr(rfc1738_str(p + 1));
2220 /* BUG: verify valid search spec ala rfc 2060 */
2223 mailbox = rfc1738_str(cmd);
2224 rv = URL_IMAP_IMESSAGELIST;
2227 if(auth && *auth != '*' && strucmp(auth, "anonymous"))
2228 q_status_message(SM_ORDER, 3, 3,
2229 "Unsupported authentication method. Using standard login.");
2232 * At this point our structure should contain the
2233 * digested url. Now put it together for c-client...
2235 l = strlen(server) + 8 + (mailbox ? strlen(mailbox) : 0)
2236 + (user ? (strlen(user)+2) : 9);
2237 *folder = (char *) fs_get((l+1) * sizeof(char));
2238 snprintf(*folder, l+1, "{%s%s%s%s%s}%s", server,
2239 (user || !(auth && strucmp(auth, "anonymous"))) ? "/" : "",
2240 user ? "user=\"" : ((auth && strucmp(auth, "anonymous")) ? "" : "Anonymous"),
2241 user ? user : "",
2242 user ? "\"" : "",
2243 mailbox);
2244 (*folder)[l] = '\0';
2247 fs_give((void **) &url);
2248 return(rv);
2253 url_bogus_imap(char **freeme, char *url, char *problem)
2255 fs_give((void **) freeme);
2256 (void) url_bogus(url, problem);
2257 return(URL_IMAP_ERROR);
2262 * url_bogus - report url syntax errors and such
2265 url_bogus(char *url, char *reason)
2267 dprint((2, "-- bogus url \"%s\": %s\n",
2268 url ? url : "<NULL URL>", reason ? reason : "?"));
2269 if(url)
2270 q_status_message3(SM_ORDER|SM_DING, 2, 3,
2271 "Malformed \"%.*s\" URL: %.200s",
2272 (void *) (strchr(url, ':') - url), url, reason);
2274 return(0);
2279 /*----------------------------------------------------------------------
2280 Format header text suitable for display
2282 Args: stream -- mail stream for various header text fetches
2283 msgno -- sequence number in stream of message we're interested in
2284 section -- which section of message
2285 env -- pointer to msg's envelope
2286 hdrs -- struct containing what's to get formatted
2287 prefix -- prefix to append to each output line
2288 handlesp -- address of pointer to the message's handles
2289 flags -- FM_ flags, see pith/mailview.h
2290 final_pc -- function to write header text with
2292 Result: 0 if all's well, -1 if write error, 1 if fetch error
2294 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2295 in the local convention.
2297 ----*/
2298 #define FHT_OK 0
2299 #define FHT_WRTERR -1
2300 #define FHT_FTCHERR 1
2302 format_header(MAILSTREAM *stream, long int msgno, char *section, ENVELOPE *env,
2303 HEADER_S *hdrs, char *prefix, HANDLE_S **handlesp, int flags,
2304 fmt_env_t fmt_env, gf_io_t final_pc)
2306 int rv = FHT_OK;
2307 int nfields, i;
2308 char *h = NULL, **fields = NULL, **v, *q, *start,
2309 *finish, *current;
2310 STORE_S *tmp_store;
2311 URL_HILITE_S uh;
2312 gf_io_t tmp_pc, tmp_gc;
2313 struct variable *vars = ps_global->vars;
2315 if((tmp_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL)
2316 gf_set_so_writec(&tmp_pc, tmp_store);
2317 else
2318 return(FHT_WRTERR);
2320 if(!fmt_env)
2321 fmt_env = format_envelope;
2323 if(ps_global->full_header == 2){
2324 rv = format_raw_header(stream, msgno, section, tmp_pc);
2326 else{
2328 * First, calculate how big a fields array we need.
2331 /* Custom header viewing list specified */
2332 if(hdrs->type == HD_LIST){
2333 /* view all these headers */
2334 if(!hdrs->except){
2335 for(nfields = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
2336 if(!is_an_env_hdr(q))
2337 nfields++;
2339 /* view all except these headers */
2340 else{
2341 for(nfields = 0, v = hdrs->h.l; *v != NULL; v++)
2342 nfields++;
2344 if(nfields > 1)
2345 nfields--; /* subtract one for ALL_EXCEPT field */
2348 else
2349 nfields = 6; /* default view */
2351 /* allocate pointer space */
2352 if(nfields){
2353 fields = (char **)fs_get((size_t)(nfields+1) * sizeof(char *));
2354 memset(fields, 0, (size_t)(nfields+1) * sizeof(char *));
2357 if(hdrs->type == HD_LIST){
2358 /* view all these headers */
2359 if(!hdrs->except){
2360 /* put the non-envelope headers in fields */
2361 if(nfields)
2362 for(i = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
2363 if(!is_an_env_hdr(q))
2364 fields[i++] = q;
2366 /* view all except these headers */
2367 else{
2368 /* put the list of headers not to view in fields */
2369 if(nfields)
2370 for(i = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
2371 if(strucmp(ALL_EXCEPT, q))
2372 fields[i++] = q;
2375 v = hdrs->h.l;
2377 else{
2378 if(nfields){
2379 fields[i = 0] = "Resent-Date";
2380 fields[++i] = "Resent-From";
2381 fields[++i] = "Resent-To";
2382 fields[++i] = "Resent-cc";
2383 fields[++i] = "Resent-Subject";
2386 v = fields;
2389 /* custom view with exception list */
2390 if(hdrs->type == HD_LIST && hdrs->except){
2392 * Go through each header in h and print it.
2393 * First we check to see if it is an envelope header so we
2394 * can print our envelope version of it instead of the raw version.
2397 /* fetch all the other headers */
2398 if(nfields)
2399 h = pine_fetchheader_lines_not(stream, msgno, section, fields);
2401 for(current = h;
2402 h && delineate_this_header(NULL, current, &start, &finish);
2403 current = finish){
2404 char tmp[MAILTMPLEN+1];
2405 char *colon_loc;
2407 colon_loc = strindex(start, ':');
2408 if(colon_loc && colon_loc < finish){
2409 strncpy(tmp, start, MIN(colon_loc-start, sizeof(tmp)-1));
2410 tmp[MIN(colon_loc-start, sizeof(tmp)-1)] = '\0';
2412 else
2413 colon_loc = NULL;
2415 if(colon_loc && is_an_env_hdr(tmp)){
2416 char *dummystart, *dummyfinish;
2419 * Pretty format for env hdrs.
2420 * If the same header appears more than once, only
2421 * print the last to avoid duplicates.
2422 * They should have been combined in the env when parsed.
2424 if(!delineate_this_header(tmp, current+1, &dummystart,
2425 &dummyfinish))
2426 format_env_hdr(stream, msgno, section, env,
2427 fmt_env, tmp_pc, tmp, hdrs->charset, flags);
2429 else{
2430 if((rv = format_raw_hdr_string(start, finish, tmp_pc,
2431 hdrs->charset, flags)) != 0)
2432 goto write_error;
2433 else
2434 start = finish;
2438 /* custom view or default */
2439 else{
2440 /* fetch the non-envelope headers */
2441 if(nfields)
2442 h = pine_fetchheader_lines(stream, msgno, section, fields);
2444 /* default envelope for default view */
2445 if(hdrs->type == HD_BFIELD)
2446 (*fmt_env)(stream, msgno, section, env, tmp_pc, hdrs->h.b, hdrs->charset, flags);
2448 /* go through each header in list, v initialized above */
2449 for(; (q = *v) != NULL; v++){
2450 if(is_an_env_hdr(q)){
2451 /* pretty format for env hdrs */
2452 format_env_hdr(stream, msgno, section, env,
2453 fmt_env, tmp_pc, q, hdrs->charset, flags);
2455 else{
2457 * Go through h finding all occurences of this header
2458 * and all continuation lines, and output.
2460 for(current = h;
2461 h && delineate_this_header(q,current,&start,&finish);
2462 current = finish){
2463 if((rv = format_raw_hdr_string(start, finish, tmp_pc,
2464 hdrs->charset, flags)) != 0)
2465 goto write_error;
2466 else
2467 start = finish;
2475 write_error:
2477 gf_clear_so_writec(tmp_store);
2479 if(!rv){ /* valid data? Do wrapping and filtering... */
2480 int column;
2481 char *errstr, *display_filter = NULL, trigger[MAILTMPLEN];
2482 STORE_S *df_store = NULL;
2484 so_seek(tmp_store, 0L, 0);
2486 column = (flags & FM_DISPLAY) ? ps_global->ttyo->screen_cols : 80;
2489 * Test for and act on any display filter
2490 * This barely makes sense. The display filter is going
2491 * to be getting UTF-8'ized headers here. In pre-alpine
2492 * pine the display filter was being fed already decoded
2493 * headers in whatever character set they were in.
2494 * The good news is that that didn't make much
2495 * sense either, so this shouldn't break anything.
2496 * It seems unlikely that anybody is doing anything useful
2497 * with the header part of display filters.
2499 if(ps_global->tools.display_filter
2500 && ps_global->tools.display_filter_trigger
2501 && (display_filter = (*ps_global->tools.display_filter_trigger)(NULL, trigger, sizeof(trigger)))){
2502 if((df_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
2504 gf_set_so_writec(&tmp_pc, df_store);
2505 gf_set_so_readc(&tmp_gc, df_store);
2506 if((errstr = (*ps_global->tools.display_filter)(display_filter, tmp_store, tmp_pc, NULL)) != NULL){
2507 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2508 _("Formatting error: %s"), errstr);
2509 rv = FHT_WRTERR;
2511 else
2512 so_seek(df_store, 0L, 0);
2514 gf_clear_so_writec(df_store);
2516 else{
2517 q_status_message(SM_ORDER | SM_DING, 3, 3,
2518 "No space for filtered text.");
2519 rv = FHT_WRTERR;
2522 else{
2523 so_seek(tmp_store, 0L, 0);
2524 gf_set_so_readc(&tmp_gc, tmp_store);
2527 if(!rv){
2528 int *margin, wrapflags = GFW_ONCOMMA;
2530 gf_filter_init();
2531 gf_link_filter(gf_local_nvtnl, NULL);
2533 if((F_ON(F_VIEW_SEL_URL, ps_global)
2534 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
2535 || F_ON(F_SCAN_ADDR, ps_global))
2536 && handlesp){
2537 gf_link_filter(gf_line_test,
2538 gf_line_test_opt(url_hilite_hdr,
2539 gf_url_hilite_opt(&uh,handlesp,1)));
2540 wrapflags |= GFW_HANDLES;
2543 if((flags & FM_DISPLAY)
2544 && !(flags & FM_NOCOLOR)
2545 && pico_usingcolor()
2546 && ((VAR_NORM_FORE_COLOR
2547 && VAR_HEADER_GENERAL_FORE_COLOR
2548 && colorcmp(VAR_NORM_FORE_COLOR, VAR_HEADER_GENERAL_FORE_COLOR))
2550 (VAR_NORM_BACK_COLOR
2551 && VAR_HEADER_GENERAL_BACK_COLOR
2552 && colorcmp(VAR_NORM_BACK_COLOR, VAR_HEADER_GENERAL_BACK_COLOR))))
2553 wrapflags |= GFW_HDRCOLOR;
2555 if((flags & FM_DISPLAY)
2556 && !(flags & FM_NOCOLOR)
2557 && pico_usingcolor()
2558 && ps_global->hdr_colors){
2559 gf_link_filter(gf_line_test,
2560 gf_line_test_opt(color_headers, NULL));
2561 wrapflags |= (GFW_HANDLES | GFW_HDRCOLOR);
2564 if(prefix && *prefix)
2565 column = MAX(column-strlen(prefix), 50);
2567 margin = format_view_margin();
2569 if(!(flags & FM_NOWRAP))
2570 gf_link_filter(gf_wrap,
2571 gf_wrap_filter_opt(column, column,
2572 (flags & FM_NOINDENT) ? NULL : margin,
2573 4, wrapflags));
2575 if(prefix && *prefix)
2576 gf_link_filter(gf_prefix, gf_prefix_opt(prefix));
2578 if((flags & FM_DISPLAY)
2579 && !(flags & FM_NOCOLOR)
2580 && pico_usingcolor()
2581 && ((VAR_NORM_FORE_COLOR
2582 && VAR_HEADER_GENERAL_FORE_COLOR
2583 && colorcmp(VAR_NORM_FORE_COLOR, VAR_HEADER_GENERAL_FORE_COLOR))
2585 (VAR_NORM_BACK_COLOR
2586 && VAR_HEADER_GENERAL_BACK_COLOR
2587 && colorcmp(VAR_NORM_BACK_COLOR, VAR_HEADER_GENERAL_BACK_COLOR)))){
2588 int right_margin;
2589 int total_wid;
2591 right_margin = margin ? margin[1] : 0;
2592 total_wid = column - right_margin;
2594 gf_link_filter(gf_line_test,
2595 gf_line_test_opt(pad_to_right_edge, (void *) &total_wid));
2596 wrapflags |= GFW_HANDLES;
2599 gf_link_filter(gf_nvtnl_local, NULL);
2601 if((errstr = gf_pipe(tmp_gc, final_pc)) != NULL){
2602 rv = FHT_WRTERR;
2603 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2604 "Can't build header : %.200s", errstr);
2608 if(df_store){
2609 gf_clear_so_readc(df_store);
2610 so_give(&df_store);
2612 else
2613 gf_clear_so_readc(tmp_store);
2616 so_give(&tmp_store);
2618 if(h)
2619 fs_give((void **)&h);
2621 if(fields)
2622 fs_give((void **)&fields);
2624 return(rv);
2628 /*----------------------------------------------------------------------
2629 Format RAW header text for display
2631 Args: stream -- mail stream for various header text fetches
2632 rawno -- sequence number in stream of message we're interested in
2633 section -- which section of message
2634 pc -- function to write header text with
2636 Result: 0 if all's well, -1 if write error, 1 if fetch error
2638 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2639 in the local convention.
2641 ----*/
2643 format_raw_header(MAILSTREAM *stream, long int msgno, char *section, gf_io_t pc)
2645 char *h = mail_fetch_header(stream, msgno, section, NULL, NULL, FT_PEEK);
2646 unsigned char c;
2648 if(h){
2649 while(*h){
2650 if(ISRFCEOL(h)){
2651 h += 2;
2652 if(!gf_puts(NEWLINE, pc))
2653 return(FHT_WRTERR);
2655 if(ISRFCEOL(h)) /* all done! */
2656 return(FHT_OK);
2658 else if((unsigned char)(*h) < 0x80 && FILTER_THIS(*h) &&
2659 !(*(h+1) && *h == ESCAPE && match_escapes(h+1))){
2660 c = (unsigned char) *h++;
2661 if(!((*pc)(c >= 0x80 ? '~' : '^')
2662 && (*pc)((c == 0x7f) ? '?' : (c & 0x1f) + '@')))
2663 return(FHT_WRTERR);
2665 else if(!(*pc)(*h++))
2666 return(FHT_WRTERR);
2669 else
2670 return(FHT_FTCHERR);
2672 return(FHT_OK);
2677 /*----------------------------------------------------------------------
2678 Format c-client envelope data suitable for display
2680 Args: s -- mail stream for various header text fetches
2681 n -- raw sequence number in stream of message we're interested in
2682 sect -- which section of message
2683 e -- pointer to msg's envelope
2684 pc -- function to write header text with
2685 which -- which header lines to write
2686 oacs --
2687 flags -- FM_ flags, see pith/mailview.h
2689 Result: 0 if all's well, -1 if write error, 1 if fetch error
2691 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2692 in the local convention.
2694 ----*/
2695 void
2696 format_envelope(MAILSTREAM *s, long int n, char *sect, ENVELOPE *e, gf_io_t pc,
2697 long int which, char *oacs, int flags)
2699 char *q, *p2, buftmp[MAILTMPLEN];
2701 if(!e)
2702 return;
2704 if((which & FE_DATE) && e->date) {
2705 q = "Date: ";
2706 snprintf(buftmp, sizeof(buftmp), "%s",
2707 F_ON(F_DATES_TO_LOCAL,ps_global)
2708 ? convert_date_to_local((char *) e->date) : (char *) e->date);
2709 buftmp[sizeof(buftmp)-1] = '\0';
2710 p2 = (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
2711 SIZEOF_20KBUF, buftmp);
2712 gf_puts(q, pc);
2713 format_env_puts(p2, pc);
2714 gf_puts(NEWLINE, pc);
2717 if((which & FE_FROM) && e->from)
2718 format_addr_string(s, n, sect, "From: ", e->from, flags, oacs, pc);
2720 if((which & FE_REPLYTO) && e->reply_to
2721 && (!e->from || !address_is_same(e->reply_to, e->from)))
2722 format_addr_string(s, n, sect, "Reply-To: ", e->reply_to, flags, oacs, pc);
2724 if((which & FE_TO) && e->to)
2725 format_addr_string(s, n, sect, "To: ", e->to, flags, oacs, pc);
2727 if((which & FE_CC) && e->cc)
2728 format_addr_string(s, n, sect, "Cc: ", e->cc, flags, oacs, pc);
2730 if((which & FE_BCC) && e->bcc)
2731 format_addr_string(s, n, sect, "Bcc: ", e->bcc, flags, oacs, pc);
2733 if((which & FE_RETURNPATH) && e->return_path)
2734 format_addr_string(s, n, sect, "Return-Path: ", e->return_path,
2735 flags, oacs, pc);
2737 if((which & FE_NEWSGROUPS) && e->newsgroups){
2738 int bogus = NIL;
2739 format_newsgroup_string("Newsgroups: ", e->newsgroups, flags, pc);
2740 if (!e->ngpathexists && e->message_id &&
2741 strncmp (e->message_id,"<alpine.",8) &&
2742 strncmp (e->message_id,"<Pine.",6) &&
2743 strncmp (e->message_id,"<MS-C.",6) &&
2744 strncmp (e->message_id,"<MailManager.",13) &&
2745 strncmp (e->message_id,"<EasyMail.",11) &&
2746 strncmp (e->message_id,"<ML-",4)) bogus = T;
2748 if(bogus)
2749 q_status_message(SM_ORDER, 0, 3,
2750 "Unverified Newsgroup header -- Message MAY or MAY NOT have been posted");
2753 if((which & FE_FOLLOWUPTO) && e->followup_to)
2754 format_newsgroup_string("Followup-To: ", e->followup_to, flags, pc);
2756 if((which & FE_SUBJECT) && e->subject && e->subject[0]){
2757 char *freeme = NULL;
2759 q = "Subject: ";
2760 gf_puts(q, pc);
2762 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->subject), SIZEOF_20KBUF-10000);
2764 if(flags & FM_DISPLAY
2765 && (ps_global->display_keywords_in_subject
2766 || ps_global->display_keywordinits_in_subject)){
2768 /* don't bother if no keywords are defined */
2769 if(some_user_flags_defined(s))
2770 p2 = freeme = prepend_keyword_subject(s, n, p2,
2771 ps_global->display_keywords_in_subject ? KW : KWInit,
2772 NULL, ps_global->VAR_KW_BRACES);
2775 format_env_puts(p2, pc);
2777 if(freeme)
2778 fs_give((void **) &freeme);
2780 gf_puts(NEWLINE, pc);
2783 if((which & FE_SENDER) && e->sender
2784 && (!e->from || !address_is_same(e->sender, e->from)))
2785 format_addr_string(s, n, sect, "Sender: ", e->sender, flags, oacs, pc);
2787 if((which & FE_MESSAGEID) && e->message_id){
2788 q = "Message-ID: ";
2789 gf_puts(q, pc);
2790 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->message_id), SIZEOF_20KBUF-10000);
2791 format_env_puts(p2, pc);
2792 gf_puts(NEWLINE, pc);
2795 if((which & FE_INREPLYTO) && e->in_reply_to){
2796 q = "In-Reply-To: ";
2797 gf_puts(q, pc);
2798 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);
2799 format_env_puts(p2, pc);
2800 gf_puts(NEWLINE, pc);
2803 if((which & FE_REFERENCES) && e->references) {
2804 q = "References: ";
2805 gf_puts(q, pc);
2806 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->references), SIZEOF_20KBUF-10000);
2807 format_env_puts(p2, pc);
2808 gf_puts(NEWLINE, pc);
2816 * The argument fieldname is something like "Subject:..." or "Subject".
2817 * Look through the specs in speccolor for a match of the fieldname,
2818 * and return the color that goes with any match, or NULL.
2819 * Caller should free the color.
2821 COLOR_PAIR *
2822 hdr_color(char *fieldname, char *value, SPEC_COLOR_S *speccolor)
2824 SPEC_COLOR_S *hc = NULL;
2825 COLOR_PAIR *color_pair = NULL;
2826 char *colon, *fname;
2827 char fbuf[FBUF_LEN+1];
2828 int gotit;
2829 PATTERN_S *pat;
2831 colon = strindex(fieldname, ':');
2832 if(colon){
2833 strncpy(fbuf, fieldname, MIN(colon-fieldname,FBUF_LEN));
2834 fbuf[MIN(colon-fieldname,FBUF_LEN)] = '\0';
2835 fname = fbuf;
2837 else
2838 fname = fieldname;
2840 if(fname && *fname)
2841 for(hc = speccolor; hc; hc = hc->next)
2842 if(hc->spec && !strucmp(fname, hc->spec)){
2843 if(!hc->val)
2844 break;
2846 gotit = 0;
2847 for(pat = hc->val; !gotit && pat; pat = pat->next)
2848 if(srchstr(value, pat->substring))
2849 gotit++;
2851 if(gotit)
2852 break;
2855 if(hc && hc->fg && hc->fg[0] && hc->bg && hc->bg[0])
2856 color_pair = new_color_pair(hc->fg, hc->bg);
2858 return(color_pair);
2863 * The argument fieldname is something like "Subject:..." or "Subject".
2864 * Look through the specs in hdr_colors for a match of the fieldname,
2865 * and return 1 if that fieldname is in one of the patterns, 0 otherwise.
2868 any_hdr_color(char *fieldname)
2870 SPEC_COLOR_S *hc = NULL;
2871 char *colon, *fname;
2872 char fbuf[FBUF_LEN+1];
2874 colon = strindex(fieldname, ':');
2875 if(colon){
2876 strncpy(fbuf, fieldname, MIN(colon-fieldname,FBUF_LEN));
2877 fbuf[MIN(colon-fieldname,FBUF_LEN)] = '\0';
2878 fname = fbuf;
2880 else
2881 fname = fieldname;
2883 if(fname && *fname)
2884 for(hc = ps_global->hdr_colors; hc; hc = hc->next)
2885 if(hc->spec && !strucmp(fname, hc->spec))
2886 break;
2888 return(hc ? 1 : 0);
2892 /*----------------------------------------------------------------------
2893 Format an address field, wrapping lines nicely at commas
2895 Args: field_name -- The name of the field we're formatting ("TO: ", ...)
2896 addr -- ADDRESS structure to format
2898 Result: A formatted, malloced string is returned.
2899 ----------------------------------------------------------------------*/
2900 void
2901 format_addr_string(MAILSTREAM *stream, long int msgno, char *section, char *field_name,
2902 struct mail_address *addr, int flags, char *oacs, gf_io_t pc)
2904 char *ptmp, *mtmp;
2905 int trailing = 0, group = 0;
2906 ADDRESS *atmp;
2908 if(!addr)
2909 return;
2912 * quickly run down address list to make sure none are patently bogus.
2913 * If so, just blat raw field out.
2915 for(atmp = addr; stream && atmp; atmp = atmp->next)
2916 if(atmp->host && atmp->host[0] == '.'){
2917 char *field, *fields[2];
2919 fields[1] = NULL;
2920 fields[0] = cpystr(field_name);
2921 if((ptmp = strchr(fields[0], ':')) != NULL)
2922 *ptmp = '\0';
2924 if((field = pine_fetchheader_lines(stream, msgno, section, fields)) != NULL){
2925 char *h, *t;
2927 for(t = h = field; *h ; t++)
2928 if(*t == '\015' && *(t+1) == '\012'){
2929 *t = '\0'; /* tie off line */
2930 format_env_puts(h, pc);
2931 if(*(h = (++t) + 1)) /* set new h and skip CRLF */
2932 gf_puts(NEWLINE, pc); /* more to write */
2933 else
2934 break;
2936 else if(!*t){ /* shouldn't happen much */
2937 if(h != t)
2938 format_env_puts(h, pc);
2940 break;
2943 fs_give((void **)&field);
2946 fs_give((void **)&fields[0]);
2947 gf_puts(NEWLINE, pc);
2948 dprint((2, "Error in \"%s\" field address\n",
2949 field_name ? field_name : "?"));
2950 return;
2953 gf_puts(field_name, pc);
2955 while(addr){
2956 atmp = addr->next; /* remember what's next */
2957 addr->next = NULL;
2958 if(!addr->host && addr->mailbox){
2959 mtmp = addr->mailbox;
2960 addr->mailbox = cpystr((char *)rfc1522_decode_to_utf8(
2961 (unsigned char *)tmp_20k_buf,
2962 SIZEOF_20KBUF, addr->mailbox));
2965 ptmp = addr->personal; /* RFC 1522 personal name? */
2966 addr->personal = iutf8ncpy((char *)tmp_20k_buf, (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+10000), SIZEOF_20KBUF-10000, addr->personal), 10000);
2967 tmp_20k_buf[10000-1] = '\0';
2969 if(!trailing) /* 1st pass, just address */
2970 trailing++;
2971 else{ /* else comma, unless */
2972 if(!((group == 1 && addr->host) /* 1st addr in group, */
2973 || (!addr->host && !addr->mailbox))){ /* or end of group */
2974 gf_puts(",", pc);
2975 #if 0
2976 gf_puts(NEWLINE, pc); /* ONE address/line please */
2977 gf_puts(" ", pc);
2978 #endif
2981 gf_puts(" ", pc);
2984 pine_rfc822_write_address_noquote(addr, pc, &group);
2986 addr->personal = ptmp; /* restore old personal ptr */
2987 if(!addr->host && addr->mailbox){
2988 fs_give((void **)&addr->mailbox);
2989 addr->mailbox = mtmp;
2992 addr->next = atmp;
2993 addr = atmp;
2996 gf_puts(NEWLINE, pc);
3002 const char *rspecials_minus_quote_and_dot = "()<>@,;:\\[]";
3003 /* RFC822 continuation, must start with CRLF */
3004 #define RFC822CONT "\015\012 "
3006 /* Write RFC822 address with some quoting turned off.
3007 * Accepts:
3008 * address to interpret
3010 * (This is a copy of c-client's rfc822_write_address except
3011 * we don't quote double quote and dot in personal names. It writes
3012 * to a gf_io_t instead of to a buffer so that we don't have to worry
3013 * about fixed sized buffer overflowing. It's also special cased to deal
3014 * with only a single address.)
3016 * The idea is that there are some places where we'd just like to display
3017 * the personal name as is before applying confusing quoting. However,
3018 * we do want to be careful not to break things that should be quoted so
3019 * we'll only use this where we are sure. Quoting may look ugly but it
3020 * doesn't usually break anything.
3022 void
3023 pine_rfc822_write_address_noquote(struct mail_address *adr, gf_io_t pc, int *group)
3025 extern const char *rspecials;
3027 if (adr->host) { /* ordinary address? */
3028 if (!(adr->personal || adr->adl)) pine_rfc822_address (adr, pc);
3029 else { /* no, must use phrase <route-addr> form */
3030 if (adr->personal)
3031 pine_rfc822_cat (adr->personal, rspecials_minus_quote_and_dot, pc);
3033 gf_puts(" <", pc); /* write address delimiter */
3034 pine_rfc822_address(adr, pc);
3035 gf_puts (">", pc); /* closing delimiter */
3038 if(*group)
3039 (*group)++;
3041 else if (adr->mailbox) { /* start of group? */
3042 /* yes, write group name */
3043 pine_rfc822_cat (adr->mailbox, rspecials, pc);
3045 gf_puts (": ", pc); /* write group identifier */
3046 *group = 1; /* in a group */
3048 else if (*group) { /* must be end of group (but be paranoid) */
3049 gf_puts (";", pc);
3050 *group = 0; /* no longer in that group */
3055 /* Write RFC822 route-address to string
3056 * Accepts:
3057 * address to interpret
3060 void
3061 pine_rfc822_address(struct mail_address *adr, gf_io_t pc)
3063 extern char *wspecials;
3065 if (adr && adr->host) { /* no-op if no address */
3066 if (adr->adl) { /* have an A-D-L? */
3067 gf_puts (adr->adl, pc);
3068 gf_puts (":", pc);
3070 /* write mailbox name */
3071 pine_rfc822_cat (adr->mailbox, wspecials, pc);
3072 if (*adr->host != '@') { /* unless null host (HIGHLY discouraged!) */
3073 gf_puts ("@", pc); /* host delimiter */
3074 gf_puts (adr->host, pc); /* write host name */
3080 /* Concatenate RFC822 string
3081 * Accepts:
3082 * pointer to string to concatenate
3083 * list of special characters
3086 void
3087 pine_rfc822_cat(char *src, const char *specials, gf_io_t pc)
3089 char *s;
3091 if (strpbrk (src,specials)) { /* any specials present? */
3092 gf_puts ("\"", pc); /* opening quote */
3093 /* truly bizarre characters in there? */
3094 while ((s = strpbrk (src,"\\\"")) != NULL) {
3095 char save[2];
3097 /* turn it into a null-terminated piece */
3098 save[0] = *s;
3099 save[1] = '\0';
3100 *s = '\0';
3101 gf_puts (src, pc); /* yes, output leader */
3102 *s = save[0];
3103 gf_puts ("\\", pc); /* quoting */
3104 gf_puts (save, pc); /* output the bizarre character */
3105 src = ++s; /* continue after the bizarre character */
3107 if (*src) gf_puts (src, pc);/* output non-bizarre string */
3108 gf_puts ("\"", pc); /* closing quote */
3110 else gf_puts (src, pc); /* otherwise it's the easy case */
3114 /*----------------------------------------------------------------------
3115 Format an address field, wrapping lines nicely at commas
3117 Args: field_name -- The name of the field we're formatting ("TO:", Cc:...)
3118 newsgrps -- ADDRESS structure to format
3120 Result: A formatted, malloced string is returned.
3122 The resuling lines formatted are 80 columns wide.
3123 ----------------------------------------------------------------------*/
3124 void
3125 format_newsgroup_string(char *field_name, char *newsgrps, int flags, gf_io_t pc)
3127 char buf[MAILTMPLEN];
3128 int trailing = 0, llen, alen;
3129 char *next_ng;
3131 if(!newsgrps || !*newsgrps)
3132 return;
3134 gf_puts(field_name, pc);
3136 llen = strlen(field_name);
3137 while(*newsgrps){
3138 for(next_ng = newsgrps; *next_ng && *next_ng != ','; next_ng++);
3139 strncpy(buf, newsgrps, MIN(next_ng - newsgrps, sizeof(buf)-1));
3140 buf[MIN(next_ng - newsgrps, sizeof(buf)-1)] = '\0';
3141 newsgrps = next_ng;
3142 if(*newsgrps)
3143 newsgrps++;
3144 alen = strlen(buf);
3145 if(!trailing){ /* first time thru, just address */
3146 llen += alen;
3147 trailing++;
3149 else{ /* else preceding comma */
3150 gf_puts(",", pc);
3151 llen++;
3153 if(alen + llen + 1 > 76){
3154 gf_puts(NEWLINE, pc);
3155 gf_puts(" ", pc);
3156 llen = alen + 5;
3158 else{
3159 gf_puts(" ", pc);
3160 llen += alen + 1;
3164 if(alen && llen > 76){ /* handle long addresses */
3165 register char *q, *p = &buf[alen-1];
3167 while(p > buf){
3168 if(isspace((unsigned char)*p)
3169 && (llen - (alen - (int)(p - buf))) < 76){
3170 for(q = buf; q < p; q++)
3171 (*pc)(*q); /* write character */
3173 gf_puts(NEWLINE, pc);
3174 gf_puts(" ", pc);
3175 gf_puts(p, pc);
3176 break;
3178 else
3179 p--;
3182 if(p == buf) /* no reasonable break point */
3183 gf_puts(buf, pc);
3185 else
3186 gf_puts(buf, pc);
3189 gf_puts(NEWLINE, pc);
3194 /*----------------------------------------------------------------------
3195 Format a text field that's part of some raw (non-envelope) message header
3197 Args: start --
3198 finish --
3199 pc --
3201 Result: Semi-digested text (RFC 1522 decoded, anyway) written with "pc"
3203 ----------------------------------------------------------------------*/
3205 format_raw_hdr_string(char *start, char *finish, gf_io_t pc, char *oacs, int flags)
3207 register char *current;
3208 unsigned char *p, *tmp = NULL, c;
3209 size_t n, len;
3210 char ch;
3211 int rv = FHT_OK;
3213 ch = *finish;
3214 *finish = '\0';
3216 if((n = 4*(finish-start)) > SIZEOF_20KBUF-1){
3217 len = n+1;
3218 p = tmp = (unsigned char *) fs_get(len * sizeof(unsigned char));
3220 else{
3221 len = SIZEOF_20KBUF;
3222 p = (unsigned char *) tmp_20k_buf;
3225 if(islower((unsigned char)(*start)))
3226 *start = toupper((unsigned char)(*start));
3228 current = (char *) rfc1522_decode_to_utf8(p, len, start);
3230 /* output from start to finish */
3231 while(*current && rv == FHT_OK)
3232 if(ISRFCEOL(current)){
3233 if(!gf_puts(NEWLINE, pc))
3234 rv = FHT_WRTERR;
3236 current += 2;
3238 else if((unsigned char)(*current) < 0x80 && FILTER_THIS(*current) &&
3239 !(*(current+1) && *current == ESCAPE && match_escapes(current+1))){
3240 c = (unsigned char) *current++;
3241 if(!((*pc)(c >= 0x80 ? '~' : '^')
3242 && (*pc)((c == 0x7f) ? '?' : (c & 0x1f) + '@')))
3243 rv = FHT_WRTERR;
3245 else if(!(*pc)(*current++))
3246 rv = FHT_WRTERR;
3248 if(tmp)
3249 fs_give((void **) &tmp);
3251 *finish = ch;
3253 return(rv);
3259 /*----------------------------------------------------------------------
3260 Format a text field that's part of some raw (non-envelope) message header
3262 Args: s --
3263 pc --
3265 Result: Output
3267 ----------------------------------------------------------------------*/
3269 format_env_puts(char *s, gf_io_t pc)
3271 if(ps_global->pass_ctrl_chars)
3272 return(gf_puts(s, pc));
3274 for(; *s; s++)
3275 if((unsigned char)(*s) < 0x80 && FILTER_THIS(*s) && !(*(s+1) && *s == ESCAPE && match_escapes(s+1))){
3276 if(!((*pc)((unsigned char) (*s) >= 0x80 ? '~' : '^')
3277 && (*pc)((*s == 0x7f) ? '?' : (*s & 0x1f) + '@')))
3278 return(0);
3280 else if(!(*pc)(*s))
3281 return(0);
3283 return(1);
3287 char *
3288 display_parameters(PARAMETER *params)
3290 int n, longest = 0;
3291 char *d, *printme;
3292 PARAMETER *p;
3293 PARMLIST_S *parmlist;
3295 for(p = params; p; p = p->next) /* ok if we include *'s */
3296 if(p->attribute && (n = strlen(p->attribute)) > longest)
3297 longest = MIN(32, n); /* shouldn't be any bigger than 32 */
3299 d = tmp_20k_buf;
3300 tmp_20k_buf[0] = '\0';
3301 if((parmlist = rfc2231_newparmlist(params)) != NULL){
3302 n = 0; /* n overloaded */
3303 while(rfc2231_list_params(parmlist) && d < tmp_20k_buf + 10000){
3304 if(n++){
3305 snprintf(d, 10000-(d-tmp_20k_buf), "\n");
3306 tmp_20k_buf[10000-1] = '\0';
3307 d += strlen(d);
3310 if(parmlist->value){
3311 if(parmlist->attrib && strucmp(parmlist->attrib, "url") == 0){
3312 snprintf(printme = tmp_20k_buf + 11000, 1000, "%s", parmlist->value);
3313 sqzspaces(printme);
3315 else
3316 printme = strsquish(tmp_20k_buf + 11000, 1000, parmlist->value, 100);
3318 else
3319 printme = "";
3321 snprintf(d, 10000-(d-tmp_20k_buf), "%-*s: %s", longest,
3322 parmlist->attrib ? parmlist->attrib : "", printme);
3324 tmp_20k_buf[10000-1] = '\0';
3325 d += strlen(d);
3328 rfc2231_free_parmlist(&parmlist);
3331 return(tmp_20k_buf);
3335 /*----------------------------------------------------------------------
3336 Fetch the requested header fields from the msgno specified
3338 Args: stream -- mail stream of open folder
3339 msgno -- number of message to get header lines from
3340 fields -- array of pointers to desired fields
3342 Returns: allocated string containing matched header lines,
3343 NULL on error.
3344 ----*/
3345 char *
3346 pine_fetch_header(MAILSTREAM *stream, long int msgno, char *section, char **fields, long int flags)
3348 STRINGLIST *sl;
3349 char *p, *m, *h = NULL, *match = NULL, *free_this, tmp[MAILTMPLEN];
3350 char **pflds = NULL, **pp = NULL, **qq;
3353 * If the user misconfigures it is possible to have one of the fields
3354 * set to the empty string instead of a header name. We want to catch
3355 * that here instead of asking the server the nonsensical question.
3357 for(pp = fields ? &fields[0] : NULL; pp && *pp; pp++)
3358 if(!**pp)
3359 break;
3361 if(pp && *pp){ /* found an empty header field, fix it */
3362 pflds = copy_list_array(fields);
3363 for(pp = pflds; pp && *pp; pp++){
3364 if(!**pp){ /* scoot rest of the lines up */
3365 free_this = *pp;
3366 for(qq = pp; *qq; qq++)
3367 *qq = *(qq+1);
3369 if(free_this)
3370 fs_give((void **) &free_this);
3374 /* no headers to look for, return NULL */
3375 if(pflds && !*pflds && !(flags & FT_NOT)){
3376 free_list_array(&pflds);
3377 return(NULL);
3380 else
3381 pflds = fields;
3383 sl = (pflds && *pflds) ? new_strlst(pflds) : NULL; /* package up fields */
3384 h = mail_fetch_header(stream, msgno, section, sl, NULL, flags | FT_PEEK);
3385 if (sl)
3386 free_strlst(&sl);
3388 if(!h){
3389 if(pflds && pflds != fields)
3390 free_list_array(&pflds);
3392 return(NULL);
3395 while(find_field(&h, tmp, sizeof(tmp))){
3396 for(pp = &pflds[0]; *pp && strucmp(tmp, *pp); pp++)
3399 /* interesting field? */
3400 if((p = (flags & FT_NOT) ? ((*pp) ? NULL : tmp) : *pp) != NULL){
3402 * Hold off allocating space for matching fields until
3403 * we at least find one to copy...
3405 if(!match)
3406 match = m = fs_get(strlen(h) + strlen(p) + 1);
3408 while(*p) /* copy field name */
3409 *m++ = *p++;
3411 while(*h && (*m++ = *h++)) /* header includes colon */
3412 if(*(m-1) == '\n' && (*h == '\r' || !isspace((unsigned char)*h)))
3413 break;
3415 *m = '\0'; /* tie off match string */
3417 else{ /* no match, pass this field */
3418 while(*h && !(*h++ == '\n'
3419 && (*h == '\r' || !isspace((unsigned char)*h))))
3424 if(pflds && pflds != fields)
3425 free_list_array(&pflds);
3427 return(match ? match : cpystr(""));
3432 find_field(char **h, char *tmp, size_t ntmp)
3434 char *otmp = tmp;
3436 if(!h || !*h || !**h || isspace((unsigned char)**h))
3437 return(0);
3439 while(tmp-otmp<ntmp-1 && **h && **h != ':' && !isspace((unsigned char)**h))
3440 *tmp++ = *(*h)++;
3442 *tmp = '\0';
3443 return(1);
3447 static char *_last_embedded_fg_color, *_last_embedded_bg_color;
3451 embed_color(COLOR_PAIR *cp, gf_io_t pc)
3453 if(cp && cp->fg){
3454 if(_last_embedded_fg_color)
3455 fs_give((void **)&_last_embedded_fg_color);
3457 _last_embedded_fg_color = cpystr(cp->fg);
3459 if(!(pc && (*pc)(TAG_EMBED) && (*pc)(TAG_FGCOLOR) &&
3460 gf_puts(color_to_asciirgb(cp->fg), pc)))
3461 return 0;
3464 if(cp && cp->bg){
3465 if(_last_embedded_bg_color)
3466 fs_give((void **)&_last_embedded_bg_color);
3468 _last_embedded_bg_color = cpystr(cp->bg);
3470 if(!(pc && (*pc)(TAG_EMBED) && (*pc)(TAG_BGCOLOR) &&
3471 gf_puts(color_to_asciirgb(cp->bg), pc)))
3472 return 0;
3475 return 1;
3479 COLOR_PAIR *
3480 get_cur_embedded_color(void)
3482 COLOR_PAIR *ret;
3484 if(_last_embedded_fg_color && _last_embedded_bg_color)
3485 ret = new_color_pair(_last_embedded_fg_color, _last_embedded_bg_color);
3486 else
3487 ret = pico_get_cur_color();
3489 return(ret);
3493 void
3494 clear_cur_embedded_color(void)
3496 if(_last_embedded_fg_color)
3497 fs_give((void **)&_last_embedded_fg_color);
3499 if(_last_embedded_bg_color)
3500 fs_give((void **)&_last_embedded_bg_color);
3505 scroll_handle_start_color(char *colorstring, size_t buflen, int *len)
3507 *len = 0;
3509 if(pico_usingcolor()){
3510 char *fg = NULL, *bg = NULL, *s;
3512 if(ps_global->VAR_SLCTBL_FORE_COLOR
3513 && colorcmp(ps_global->VAR_SLCTBL_FORE_COLOR,
3514 ps_global->VAR_NORM_FORE_COLOR))
3515 fg = ps_global->VAR_SLCTBL_FORE_COLOR;
3517 if(ps_global->VAR_SLCTBL_BACK_COLOR
3518 && colorcmp(ps_global->VAR_SLCTBL_BACK_COLOR,
3519 ps_global->VAR_NORM_BACK_COLOR))
3520 bg = ps_global->VAR_SLCTBL_BACK_COLOR;
3522 if(bg || fg){
3523 COLOR_PAIR *tmp;
3526 * The blacks are just known good colors for
3527 * testing whether the other color is good.
3529 if((tmp = new_color_pair(fg ? fg : colorx(COL_BLACK),
3530 bg ? bg : colorx(COL_BLACK))) != NULL){
3531 if(pico_is_good_colorpair(tmp))
3532 for(s = color_embed(fg, bg);
3533 (*len) < buflen && (colorstring[*len] = *s);
3534 s++, (*len)++)
3537 free_color_pair(&tmp);
3541 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
3542 strncpy(colorstring + (*len), url_embed(TAG_BOLDON), MIN(3,buflen-(*len)));
3543 *len += 2;
3547 colorstring[buflen-1] = '\0';
3549 return(*len != 0);
3554 scroll_handle_end_color(char *colorstring, size_t buflen, int *len, int use_hdr_color)
3556 *len = 0;
3557 if(pico_usingcolor()){
3558 char *fg = NULL, *bg = NULL, *s;
3559 char *basefg = NULL, *basebg = NULL;
3561 basefg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_FORE_COLOR
3562 : ps_global->VAR_NORM_FORE_COLOR;
3563 basebg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_BACK_COLOR
3564 : ps_global->VAR_NORM_BACK_COLOR;
3567 * We need to change the fg and bg colors back even if they
3568 * are the same as the Normal Colors so that color_a_quote
3569 * will have a chance to pick up those colors as the ones to
3570 * switch to. We don't do this before the handle above so that
3571 * the quote color will flow into the selectable item when
3572 * the selectable item color is partly the same as the
3573 * normal color. That is, suppose the normal color was black on
3574 * cyan and the selectable color was blue on cyan, only a fg color
3575 * change. We preserve the only-a-fg-color-change in a quote by
3576 * letting the quote background color flow into the selectable text.
3578 if(ps_global->VAR_SLCTBL_FORE_COLOR)
3579 fg = basefg;
3581 if(ps_global->VAR_SLCTBL_BACK_COLOR)
3582 bg = basebg;
3584 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
3585 strncpy(colorstring, url_embed(TAG_BOLDOFF), MIN(3,buflen));
3586 *len = 2;
3589 if(fg || bg)
3590 for(s = color_embed(fg, bg); (*len) < buflen && (colorstring[*len] = *s); s++, (*len)++)
3594 colorstring[buflen-1] = '\0';
3596 return(*len != 0);
3601 * Helper routine that is of limited use.
3602 * We need to tally up the screen width of
3603 * a UTF-8 string as we go through the string.
3604 * We just want the width of the character starting
3605 * at str (and no longer than remaining_octets).
3606 * If we're plopped into the middle of a UTF-8
3607 * character we just want to return width zero.
3610 width_at_this_position(unsigned char *str, unsigned long n)
3612 unsigned char *inputp = str;
3613 unsigned long remaining_octets = n;
3614 UCS ucs;
3615 int width = 0;
3617 ucs = (UCS) utf8_get(&inputp, &remaining_octets);
3618 if(!(ucs & U8G_ERROR || ucs == UBOGON)){
3619 width = wcellwidth(ucs);
3620 /* Writechar will print a '?' */
3621 if(width < 0)
3622 width = 1;
3625 return(width);