* Bug fix: Memory corruption when alpine searches for a string that is
[alpine.git] / pith / mailview.c
blobdc0c49b101bd4499be33621aa392285b975e211d
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-2021 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
21 mailview.c
22 Implements message data gathering and formatting
24 ====*/
27 #include "headers.h"
28 #include "../pith/ical.h"
29 #include "../pith/body.h"
30 #include "../pith/mailpart.h"
31 #include "../pith/mailview.h"
32 #include "../pith/conf.h"
33 #include "../pith/msgno.h"
34 #include "../pith/editorial.h"
35 #include "../pith/mimedesc.h"
36 #include "../pith/margin.h"
37 #include "../pith/color.h"
38 #include "../pith/strlst.h"
39 #include "../pith/charset.h"
40 #include "../pith/status.h"
41 #include "../pith/maillist.h"
42 #include "../pith/mailcmd.h"
43 #include "../pith/mailindx.h"
44 #include "../pith/imap.h"
45 #include "../pith/detach.h"
46 #include "../pith/text.h"
47 #include "../pith/url.h"
48 #include "../pith/rfc2231.h"
49 #include "../pith/list.h"
50 #include "../pith/stream.h"
51 #include "../pith/send.h"
52 #include "../pith/filter.h"
53 #include "../pith/string.h"
54 #include "../pith/ablookup.h"
55 #include "../pith/escapes.h"
56 #include "../pith/keyword.h"
57 #include "../pith/smime.h"
60 #define FBUF_LEN (50)
62 #define ISRFCEOL(S) (*(S) == '\015' && *((S)+1) == '\012')
66 * This is a list of header fields that are represented canonically
67 * by the c-client's ENVELOPE structure. The list is used by the
68 * two functions below to decide if a given field is included in this
69 * set.
71 static struct envelope_s {
72 char *name;
73 long val;
74 } envelope_hdrs[] = {
75 {"from", FE_FROM},
76 {"sender", FE_SENDER},
77 {"date", FE_DATE},
78 {"to", FE_TO},
79 {"cc", FE_CC},
80 {"bcc", FE_BCC},
81 {"newsgroups", FE_NEWSGROUPS},
82 {"subject", FE_SUBJECT},
83 {"message-id", FE_MESSAGEID},
84 {"reply-to", FE_REPLYTO},
85 {"followup-to", FE_FOLLOWUPTO},
86 {"in-reply-to", FE_INREPLYTO},
87 /* {"return-path", FE_RETURNPATH}, not usually filled in */
88 {"references", FE_REFERENCES},
89 {NULL, 0}
94 * Hook for optional display of rfc2369 content
96 int (*pith_opt_rfc2369_editorial)(long, HANDLE_S **, int, int, gf_io_t);
102 * Internal prototypes
104 int format_blip_seen(long);
105 int is_an_env_hdr(char *);
106 int is_an_addr_hdr(char *);
107 void format_env_hdr(MAILSTREAM *, long, char *, ENVELOPE *,
108 fmt_env_t, gf_io_t, char *, char *, int);
109 int delineate_this_header(char *, char *, char **, char **);
110 char *url_embed(int);
111 int color_headers(long, char *, LT_INS_S **, void *);
112 int url_hilite_hdr(long, char *, LT_INS_S **, void *);
113 int pad_to_right_edge(long, char *, LT_INS_S **, void *);
114 int url_bogus_imap(char **, char *, char *);
115 int format_raw_header(MAILSTREAM *, long, char *, gf_io_t);
116 void format_envelope(MAILSTREAM *, long, char *, ENVELOPE *,
117 gf_io_t, long, char *, int);
118 int any_hdr_color(char *);
119 void format_addr_string(MAILSTREAM *, long, char *, char *,
120 ADDRESS *, int, char *, gf_io_t);
121 void pine_rfc822_write_address_noquote(ADDRESS *, gf_io_t, int *);
122 void format_newsgroup_string(char *, char *, int, gf_io_t);
123 int format_raw_hdr_string(char *, char *, gf_io_t, char *, int);
124 int format_env_puts(char *, gf_io_t);
125 int find_field(char **, char *, size_t);
126 int embed_color(COLOR_PAIR *, gf_io_t);
127 COLOR_PAIR *get_cur_embedded_color(void);
128 void clear_cur_embedded_color(void);
129 void format_calendar_vevent(VCALENDAR_S *, ATTACH_S *, HANDLE_S **, int, int, gf_io_t, int);
133 /*----------------------------------------------------------------------
134 Format a message message for viewing
136 Args: msgno -- The number of the message to view
137 env -- pointer to the message's envelope
138 body -- pointer to the message's body
139 handlesp -- address of pointer to the message's handles
140 flgs -- possible flags listed in pith/mailview.h with
141 prefix FM_
142 pc -- write to this function
144 Result: Returns true if no problems encountered, else false.
146 First the envelope is formatted; next a list of all attachments is
147 formatted if there is more than one. Then all the body parts are
148 formatted, fetching them as needed. This includes headers of included
149 message. Richtext is also formatted. An entry is made in the text for
150 parts that are not displayed or can't be displayed.
152 ----*/
154 format_message(long int msgno, ENVELOPE *env, struct mail_bodystruct *body,
155 HANDLE_S **handlesp, int flgs, gf_io_t pc)
157 char *decode_err = NULL;
158 HEADER_S h;
159 int width, rv = 0;
161 clear_cur_embedded_color();
163 if(!(flgs & FM_DISPLAY))
164 flgs |= FM_NOINDENT;
166 width = (flgs & FM_DISPLAY) ? ps_global->ttyo->screen_cols : 80;
168 /*---- format and copy envelope ----*/
169 if(!(flgs & FM_NOEDITORIAL)){
170 if(ps_global->full_header == 1)
171 /* TRANSLATORS: User is viewing a message and all the quoted text is
172 being shown. */
173 q_status_message(SM_INFO, 0, 3, _("All quoted text being included"));
174 else if(ps_global->full_header == 2)
175 q_status_message(SM_INFO, 0, 3,
176 /* TRANSLATORS: User is viewing a message and all of
177 the header text is being shown. */
178 _("Full header mode ON. All header text being included"));
181 HD_INIT(&h, ps_global->VAR_VIEW_HEADERS, ps_global->view_all_except, FE_DEFAULT);
182 switch(format_header(ps_global->mail_stream, msgno, NULL,
183 env, &h, NULL, handlesp, flgs, NULL, pc)){
185 case -1 : /* write error */
186 goto write_error;
188 case 1 : /* fetch error */
189 if(!(gf_puts("[ Error fetching header ]", pc)
190 && !gf_puts(NEWLINE, pc)))
191 goto write_error;
193 break;
196 if(!(body == NULL
197 || (ps_global->full_header == 2
198 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global)))){
199 format_attachment_list(msgno, body, handlesp, flgs, width, pc);
200 format_calendar(msgno, body, handlesp, flgs, width, pc);
203 /* write delimiter and body */
204 if(gf_puts(NEWLINE, pc)){
205 if((decode_err = format_body(msgno, body, handlesp, &h, flgs, width, pc)) == NULL)
206 rv = 1;
207 else
208 rv = 0;
210 clear_cur_embedded_color();
211 return(rv);
214 write_error:
216 if(!(flgs & FM_DISPLAY))
217 q_status_message1(SM_ORDER, 3, 4, _("Error writing message: %s"),
218 decode_err ? decode_err : error_description(errno));
219 clear_cur_embedded_color();
220 return(0);
223 void
224 format_calendar_vevent(VCALENDAR_S *vcal, ATTACH_S *a, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc, int cflags)
226 int avail, m1, m2, hwid, i, partwid, padwid;
227 int s1, s2, dwid, minkey;
228 int *margin;
229 char padding[1024];
230 VEVENT_SUMMARY_S *vesy, *vesummary; /* vevent summary */
232 vesummary = vesy = ical_vevent_summary(vcal);
234 if(vesy == NULL) return;
236 if((cflags & FC_SUMMARY) && (cflags & FC_FULL))
237 cflags |= ~FC_FULL;
239 minkey = -1; /* initialize to something negative */
241 for(; vesy != NULL ; vesy = vesy->next){
242 avail = width;
243 margin = (cflags & FC_FULL) ? NULL
244 : (flgs & FM_NOINDENT) ? NULL : format_view_margin();
246 m1 = MAX(MIN(margin ? margin[0] : 0, avail), 0);
247 avail -= m1;
249 m2 = MAX(MIN(margin ? margin[1] : 0, avail), 0);
250 avail -= m2;
252 hwid = MAX(avail, 0);
253 padwid = 0;
255 if(ps_global->atmts[1].description == NULL){
256 avail = width - m1 -2;
258 dwid = MAX(MIN(40, avail), 0);
260 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s%s", m1, m1, "",
261 repeat_char(dwid, '-'));
263 if(!gf_puts(tmp_20k_buf, pc) || !gf_puts(NEWLINE, pc))
264 return;
268 if(cflags & FC_SUMMARY){
269 i = utf8_width(_("Calendar Entry:"));
270 partwid = MIN(i, hwid);
271 if(m1 > 0){
272 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", m1, m1, "");
273 if(!gf_puts(tmp_20k_buf, pc))
274 return;
277 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%-*.*w%*.*s",
278 partwid, partwid, _("Calendar Entry:"),
279 padwid, padwid, "");
281 if(!gf_puts(tmp_20k_buf, pc) || !gf_puts(NEWLINE, pc))
282 return;
284 else
285 partwid = 0;
287 if(m1 > 0){
288 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", m1, m1, "");
289 if(!gf_puts(tmp_20k_buf, pc))
290 return;
293 avail = width - m1 - m2;
295 s1 = MAX(MIN(1, avail), 0);
296 avail -= s1;
298 dwid = MAX(MIN(1, avail), 0);
299 avail -= dwid;
301 s2 = MAX(MIN(1, avail), 0);
302 avail -= s2;
304 if(cflags & FC_SUMMARY)
305 utf8_snprintf(padding, sizeof(padding), "%*.*s%*.*w%*.*s",
306 s1, s1, "", dwid, dwid, "", s2, s2, "");
307 else
308 padding[0] = '\0';
310 if(vesy->cancel){
311 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
312 padding, _("This event was cancelled!"));
313 if((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON)){
314 gf_puts(tmp_20k_buf, pc);
315 gf_puts(NEWLINE, pc);
316 (*pc)(TAG_EMBED);
317 (*pc)(TAG_BOLDOFF);
321 if(vesy->organizer){
322 if(vesy->sender != NULL){
323 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
324 padding, _("Sent-by: "), vesy->sender);
325 gf_puts(tmp_20k_buf, pc);
326 gf_puts(NEWLINE, pc);
329 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
330 padding, _("Organizer: "), vesy->organizer);
331 gf_puts(tmp_20k_buf, pc);
332 gf_puts(NEWLINE, pc);
333 } /* end of if(organizer) */
335 if(vesy->location){
336 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
337 padding, _("Location: "), vesy->location);
338 gf_puts(tmp_20k_buf, pc);
339 gf_puts(NEWLINE, pc);
340 } /* end of if location */
342 if(vesy->evstart){
343 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
344 padding, _("Start Date: "), vesy->evstart);
345 gf_puts(tmp_20k_buf, pc);
346 gf_puts(NEWLINE, pc);
347 } /* end of if dtstart */
349 if(vesy->duration){
350 int i;
352 for(i = 0; vesy->duration[i] != NULL; i++){
353 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
354 padding, _("Duration: "), vesy->duration[i]);
355 gf_puts(tmp_20k_buf, pc);
356 gf_puts(NEWLINE, pc);
358 } /* end of DURATION */
359 else if(vesy->evend){
360 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
361 padding, _("End Date: "), vesy->evend);
362 gf_puts(tmp_20k_buf, pc);
363 gf_puts(NEWLINE, pc);
364 } else { /* end of if dtend */
365 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
366 padding, _("No duration nor end time found for this event"));
367 gf_puts(tmp_20k_buf, pc);
368 gf_puts(NEWLINE, pc);
369 } /* end of else for if (duration) and if (dtend) */
371 if(vesy->attendee){
372 #define MAX_DISPLAYED 3
373 int i;
375 for(i = 0; vesy->attendee[i] != NULL; i++){
376 if((cflags & FC_SUMMARY) && i >= MAX_DISPLAYED)
377 break;
379 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
380 padding,
381 _("Attendee: "), vesy->attendee[i]);
382 gf_puts(tmp_20k_buf, pc);
383 gf_puts(NEWLINE, pc);
385 } /* end of ATTENDEES */
388 if(cflags & FC_SUMMARY){
389 COLOR_PAIR *lastc = NULL;
390 COLOR_PAIR *hdrcolor = NULL;
392 if((flgs & FM_DISPLAY)
393 && !(flgs & FM_NOCOLOR)
394 && pico_usingcolor()
395 && ps_global->VAR_HEADER_GENERAL_FORE_COLOR
396 && ps_global->VAR_HEADER_GENERAL_BACK_COLOR
397 && ps_global->VAR_NORM_FORE_COLOR
398 && ps_global->VAR_NORM_BACK_COLOR
399 && (colorcmp(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
400 ps_global->VAR_NORM_FORE_COLOR)
401 || colorcmp(ps_global->VAR_HEADER_GENERAL_BACK_COLOR,
402 ps_global->VAR_NORM_BACK_COLOR))){
404 if((hdrcolor = new_color_pair(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
405 ps_global->VAR_HEADER_GENERAL_BACK_COLOR)) != NULL){
406 if(!pico_is_good_colorpair(hdrcolor))
407 free_color_pair(&hdrcolor);
411 if(!(!hdrcolor || embed_color(hdrcolor, pc)))
412 return;
414 gf_puts(padding, pc);
415 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "[%s]", _("More Details"));
417 if(handlesp){
418 char buf[16], color[64];
419 int l;
420 HANDLE_S *h;
422 h = new_handle(handlesp);
423 if(minkey < 0) minkey = h->key;
424 h->type = iCal;
425 h->h.ical.attach = a;
426 h->h.ical.depth = h->key - minkey;
428 snprintf(buf, sizeof(buf), "%d", h->key);
429 buf[sizeof(buf)-1] = '\0';
431 if(!(flgs & FM_NOCOLOR)
432 && handle_start_color(color, sizeof(color), &l, 1)){
433 lastc = get_cur_embedded_color();
434 if(!gf_nputs(color, (long) l, pc))
435 return;
437 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)
438 && (!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON))))
439 return;
441 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE)
442 && (*pc)(strlen(buf)) && gf_puts(buf, pc)))
443 return;
444 } else
445 tmp_20k_buf[0] = '\0';
447 if(!format_env_puts(tmp_20k_buf, pc))
448 return;
450 if(handlesp){
451 if(lastc){
452 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
453 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
454 return;
457 if(!embed_color(lastc, pc))
458 return;
460 free_color_pair(&lastc);
462 else if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
463 return;
465 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)))
466 return;
469 if(padwid > 0){
470 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", padwid, padwid, "");
471 if(!gf_puts(tmp_20k_buf, pc))
472 return;
475 if(!gf_puts(NEWLINE, pc))
476 return;
478 avail = width - m1 -2;
480 dwid = MAX(MIN(40, avail), 0);
481 avail -= dwid;
483 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s%s", m1, m1, "",
484 repeat_char(dwid, '-'));
486 gf_puts(tmp_20k_buf, pc);
487 if(vesy->next)
488 gf_puts(NEWLINE, pc);
490 free_vevent_summary(&vesummary);
494 format_calendar(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc)
496 char *rawtext, *caltext;
497 unsigned long callen;
498 VCALENDAR_S *vcal = NULL;
499 ATTACH_S *a;
500 BODY *b;
502 if(flgs & FM_NEW_MESS) {
503 zero_atmts(ps_global->atmts);
504 describe_mime(body, "", 1, 1, 0, flgs);
507 for(a = ps_global->atmts; a->description != NULL; a++){
508 if(MIME_VCALENDAR(a->body->type, a->body->subtype)){
509 b = mail_body (ps_global->mail_stream, msgno, (unsigned char *) a->number);
510 if(b == NULL){
511 gf_puts(_("Error fetching calendar body part"), pc);
512 gf_puts(NEWLINE, pc);
513 continue;
515 if(b->sparep == NULL){
516 rawtext = mail_fetch_body(ps_global->mail_stream, msgno, a->number, &callen, 0);
517 if(rawtext == NULL || *rawtext == '\0'){
518 gf_puts(_("Error fetching calendar text"), pc);
519 gf_puts(NEWLINE, pc);
520 continue;
522 rawtext[callen] = '\0'; /* chop off cookie */
523 switch(b->encoding){
524 case ENCBASE64:
525 caltext = rfc822_base64((unsigned char *) rawtext, strlen(rawtext), &callen);
526 if(caltext == NULL){
527 gf_puts(_("Error in calendar base64 encoding"), pc);
528 gf_puts(NEWLINE, pc);
529 continue;
531 caltext[callen] = '\0';
532 break;
534 case ENCQUOTEDPRINTABLE:
535 caltext = rfc822_qprint ((unsigned char *) rawtext,strlen(rawtext),&callen);
536 if(caltext == NULL){
537 gf_puts(_("Error in calendar quoted printable encoding"), pc);
538 gf_puts(NEWLINE, pc);
539 continue;
541 caltext[callen] = '\0';
542 break;
544 default: caltext = cpystr(rawtext);
546 if(caltext != NULL){
547 vcal = ical_parse_text(caltext);
548 if(vcal != NULL) vcal->encoding = b->encoding;
549 b->sparep = create_body_sparep(iCalType, (void *) vcal);
550 fs_give((void **) &caltext);
553 else if(get_body_sparep_type(b->sparep) == iCalType)
554 vcal = (VCALENDAR_S *) get_body_sparep_data(b->sparep);
555 if(vcal != NULL && vcal->comp != NULL){
556 if(vcal->comp[VEvent] != NULL){
557 format_calendar_vevent(vcal, a, handlesp, flgs, width, pc, FC_SUMMARY);
558 } /* else another type of entry in the calendar */
560 gf_puts(NEWLINE, pc);
563 return 0;
567 char *
568 format_body(long int msgno, BODY *body, HANDLE_S **handlesp, HEADER_S *hp, int flgs, int width, gf_io_t pc)
570 int filt_only_c0 = 0, wrapflags, error_found = 0;
571 int is_in_sig = OUT_SIG_BLOCK;
572 char *charset, *decode_err = NULL, *tmp1, *description;
573 ATTACH_S *a;
574 URL_HILITE_S uh;
575 gf_io_t gc;
577 if(body == NULL
578 || (ps_global->full_header == 2
579 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))) {
581 /*--- Server is not an IMAP2bis, It can't parse MIME
582 so we just show the text here. Hopefully the
583 message isn't a MIME message
584 ---*/
585 void *text2;
587 if((text2 = (void *)pine_mail_fetch_text(ps_global->mail_stream,
588 msgno, NULL, NULL, NIL)) != NULL){
590 if(!gf_puts(NEWLINE, pc)) /* write delimiter */
591 return("Write Error");
593 gf_set_readc(&gc, text2, (unsigned long)strlen(text2), CharStar, 0);
594 gf_filter_init();
597 * We need to translate the message
598 * into UTF-8, but that's trouble in the full header case
599 * because we don't know what to translate from. We'll just
600 * take a guess by looking for the first text part and
601 * using its charset.
603 if(body && body->type == TYPETEXT)
604 charset = parameter_val(body->parameter, "charset");
605 else if(body && body->type == TYPEMULTIPART && body->nested.part
606 && body->nested.part->body.type == TYPETEXT)
607 charset = parameter_val(body->nested.part->body.parameter, "charset");
608 else
609 charset = cpystr(ps_global->display_charmap);
611 if(strucmp(charset, "us-ascii") && strucmp(charset, "utf-8")){
612 /* transliterate message text to UTF-8 */
613 gf_link_filter(gf_utf8, gf_utf8_opt(charset));
615 if (charset) fs_give((void **) &charset);
617 /* link in filters, similar to what is done in decode_text() */
618 if(!ps_global->pass_ctrl_chars){
619 gf_link_filter(gf_escape_filter, NULL);
620 filt_only_c0 = 1;
621 gf_link_filter(gf_control_filter,
622 gf_control_filter_opt(&filt_only_c0));
625 gf_link_filter(gf_tag_filter, NULL);
627 if((F_ON(F_VIEW_SEL_URL, ps_global)
628 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
629 || F_ON(F_SCAN_ADDR, ps_global))
630 && handlesp){
631 gf_link_filter(gf_line_test,
632 gf_line_test_opt(url_hilite,
633 gf_url_hilite_opt(&uh,handlesp,0)));
636 if((flgs & FM_DISPLAY)
637 && !(flgs & FM_NOCOLOR)
638 && pico_usingcolor()
639 && ps_global->VAR_SIGNATURE_FORE_COLOR
640 && ps_global->VAR_SIGNATURE_BACK_COLOR){
641 gf_link_filter(gf_line_test, gf_line_test_opt(color_signature, &is_in_sig));
644 if((flgs & FM_DISPLAY)
645 && !(flgs & FM_NOCOLOR)
646 && pico_usingcolor()
647 && ps_global->VAR_QUOTE1_FORE_COLOR
648 && ps_global->VAR_QUOTE1_BACK_COLOR){
649 gf_link_filter(gf_line_test, gf_line_test_opt(color_a_quote, NULL));
652 if(!(flgs & FM_NOWRAP)){
653 wrapflags = (flgs & FM_DISPLAY) ? (GFW_HANDLES|GFW_SOFTHYPHEN) : GFW_NONE;
654 if(flgs & FM_DISPLAY
655 && !(flgs & FM_NOCOLOR)
656 && pico_usingcolor())
657 wrapflags |= GFW_USECOLOR;
658 gf_link_filter(gf_wrap, gf_wrap_filter_opt(width, width,
659 (flgs & FM_NOINDENT)
660 ? NULL : format_view_margin(),
662 wrapflags));
665 gf_link_filter(gf_nvtnl_local, NULL);
666 if((decode_err = gf_pipe(gc, pc)) != NULL){
667 /* TRANSLATORS: There was an error putting together a message for
668 viewing. The arg is the description of the error. */
669 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Formatting error: %s"), decode_err);
670 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
671 if(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
672 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
673 && gf_puts(NEWLINE, pc))
674 decode_err = NULL;
675 else
676 return(decode_err);
680 if(!text2){
681 if(!gf_puts(NEWLINE, pc)
682 || !gf_puts(_(" [ERROR fetching text of message]"), pc)
683 || !gf_puts(NEWLINE, pc)
684 || !gf_puts(NEWLINE, pc))
685 return("Write Error");
688 else{
689 int show_parts = 0;
691 /*======== Now loop through formatting all the parts =======*/
692 for(a = ps_global->atmts; a->description != NULL; a++) {
693 if(MIME_VCALENDAR(a->body->type, a->body->subtype))
694 continue;
696 if(a->body->type == TYPEMULTIPART){
697 #ifdef SMIME
698 if(strucmp(a->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0){
699 if(a->description){
700 if(!(!format_editorial(a->description, width, flgs, handlesp, pc)
701 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
702 return("Write Error");
705 #endif /* SMIME */
706 continue;
709 if(!a->shown) {
710 if(a->suppress_editorial)
711 continue;
713 if(!(flgs & FM_NOEDITORIAL)
714 && (!gf_puts(NEWLINE, pc)
715 || (decode_err = part_desc(a->number, a->body,
716 (flgs & FM_DISPLAY)
717 ? (a->can_display != MCD_NONE)
718 ? 1 : 2
719 : 3, width, flgs, pc))))
720 return("Write Error");
722 continue;
725 switch(a->body->type){
727 case TYPETEXT:
729 * If a message is multipart *and* the first part of it
730 * is text *and that text is empty, there is a good chance that
731 * there was actually something there that c-client was
732 * unable to parse. Here we report the empty message body
733 * and insert the raw RFC822.TEXT (if full-headers are
734 * on).
736 if(body->type == TYPEMULTIPART
737 && a == ps_global->atmts
738 && a->body->size.bytes == 0
739 && F_ON(F_ENABLE_FULL_HDR, ps_global)){
740 char *err = NULL;
742 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
743 "Empty or malformed message%s.",
744 ps_global->full_header == 2
745 ? ". Displaying raw text"
746 : ". Use \"H\" to see raw text");
747 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
749 if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
750 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
751 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
752 return("Write Error");
754 if(ps_global->full_header == 2
755 && (err = detach_raw(ps_global->mail_stream, msgno,
756 a->number, pc, flgs))){
757 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
758 "%s%s [ Formatting error: %s ]%s%s",
759 NEWLINE, NEWLINE, err, NEWLINE, NEWLINE);
760 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
761 if(!gf_puts(tmp_20k_buf, pc))
762 return("Write Error");
765 break;
769 * Don't write our delimiter if this text part is
770 * the first part of a message/rfc822 segment...
772 if(show_parts && a != ps_global->atmts
773 && !((a[-1].body && a[-1].body->type == TYPEMESSAGE)
774 #ifdef SMIME
775 || (a[-1].body->type == TYPEMULTIPART
776 && a[-1].body->subtype
777 && (strucmp(a[-1].body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0)
778 && &a[-1] != ps_global->atmts
779 && a[-2].body && a[-2].body->type == TYPEMESSAGE)
780 #endif /* SMIME */
782 && !(flgs & FM_NOEDITORIAL)){
783 if(MIME_VCALENDAR(a->body->type, a->body->subtype))
784 tmp1 = "Calendar entry";
785 else
786 tmp1 = a->body->description ? a->body->description
787 : "Attached Text";
788 description = iutf8ncpy((char *)(tmp_20k_buf+10000),
789 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+15000), 5000, tmp1), 5000);
791 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Part %s: \"%.1024s\"", a->number,
792 description);
793 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
794 if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
795 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
796 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
797 return("Write Error");
799 /* skip calendar */
800 if(!MIME_VCALENDAR(a->body->type, a->body->subtype))
801 error_found += decode_text(a, msgno, pc, handlesp,
802 (flgs & FM_DISPLAY) ? InLine : QStatus,
803 flgs);
804 break;
806 case TYPEMESSAGE:
807 tmp1 = a->body->description ? a->body->description
808 : (strucmp(a->body->subtype, "delivery-status") == 0)
809 ? "Delivery Status"
810 : "Included Message";
811 description = iutf8ncpy((char *)(tmp_20k_buf+10000),
812 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+15000), 5000, tmp1), 5000);
814 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Part %s: \"%.1024s\"", a->number,
815 description);
816 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
818 if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
819 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
820 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
821 return("Write Error");
823 if(a->body->subtype && strucmp(a->body->subtype, "rfc822") == 0){
824 /* imapenvonly, we may not have all the headers we need */
825 if(a->body->nested.msg->env->imapenvonly)
826 mail_fetch_header(ps_global->mail_stream, msgno,
827 a->number, NULL, NULL, FT_PEEK);
828 switch(format_header(ps_global->mail_stream, msgno, a->number,
829 a->body->nested.msg->env, hp,
830 NULL, handlesp, flgs, NULL, pc)){
831 case -1 : /* write error */
832 return("Write Error");
834 case 1 : /* fetch error */
835 if(!(gf_puts("[ Error fetching header ]", pc)
836 && !gf_puts(NEWLINE, pc)))
837 return("Write Error");
839 break;
842 else if(a->body->subtype && strucmp(a->body->subtype, "external-body") == 0){
843 int *margin, avail, m1, m2;
845 avail = width;
846 margin = (flgs & FM_NOINDENT) ? NULL : format_view_margin();
848 m1 = MAX(MIN(margin ? margin[0] : 0, avail), 0);
849 avail -= m1;
851 m2 = MAX(MIN(margin ? margin[1] : 0, avail), 0);
852 avail -= m2;
854 if(format_editorial("This part is not included and can be fetched as follows:", avail, flgs, handlesp, pc)
855 || !gf_puts(NEWLINE, pc)
856 || format_editorial(display_parameters(a->body->parameter), avail, flgs, handlesp, pc))
857 return("Write Error");
859 else
860 error_found += decode_text(a, msgno, pc, handlesp,
861 (flgs&FM_DISPLAY) ? InLine : QStatus,
862 flgs);
864 if(!gf_puts(NEWLINE, pc))
865 return("Write Error");
867 break;
869 default:
870 if((decode_err = part_desc(a->number, a->body,
871 (flgs & FM_DISPLAY) ? 1 : 3,
872 width, flgs, pc)) != NULL)
873 return("Write Error");
876 show_parts++;
879 if(!(!error_found
880 && (pith_opt_rfc2369_editorial ? (*pith_opt_rfc2369_editorial)(msgno, handlesp, flgs, width, pc) : 1)
881 && format_blip_seen(msgno)))
882 return("Cannot format body.");
885 return(NULL);
890 format_attachment_list(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc)
892 ATTACH_S *a;
894 if(flgs & FM_NEW_MESS) {
895 zero_atmts(ps_global->atmts);
896 describe_mime(body, "", 1, 1, 0, flgs);
899 /*----- First do the list of parts/attachments if needed ----*/
900 if((flgs & FM_DISPLAY)
901 && (ps_global->atmts[1].description
902 || (ps_global->atmts[0].body
903 && ps_global->atmts[0].body->type != TYPETEXT))){
904 char tmp[6*MAX_SCREEN_COLS + 1], *tmpp;
905 int i, n, maxnumwid = 0, maxsizewid = 0, *margin;
906 int avail, m1, m2, hwid, s1, s2, s3, s4, s5, dwid, shownwid;
907 int sizewid, descwid, dashwid, partwid, padwid;
908 COLOR_PAIR *hdrcolor = NULL;
910 if((flgs & FM_DISPLAY)
911 && !(flgs & FM_NOCOLOR)
912 && pico_usingcolor()
913 && ps_global->VAR_HEADER_GENERAL_FORE_COLOR
914 && ps_global->VAR_HEADER_GENERAL_BACK_COLOR
915 && ps_global->VAR_NORM_FORE_COLOR
916 && ps_global->VAR_NORM_BACK_COLOR
917 && (colorcmp(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
918 ps_global->VAR_NORM_FORE_COLOR)
919 || colorcmp(ps_global->VAR_HEADER_GENERAL_BACK_COLOR,
920 ps_global->VAR_NORM_BACK_COLOR))){
922 if((hdrcolor = new_color_pair(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
923 ps_global->VAR_HEADER_GENERAL_BACK_COLOR)) != NULL){
924 if(!pico_is_good_colorpair(hdrcolor))
925 free_color_pair(&hdrcolor);
929 margin = (flgs & FM_NOINDENT) ? NULL : format_view_margin();
932 * Attachment list header
935 avail = width;
937 m1 = MAX(MIN(margin ? margin[0] : 0, avail), 0);
938 avail -= m1;
940 m2 = MAX(MIN(margin ? margin[1] : 0, avail), 0);
941 avail -= m2;
943 hwid = MAX(avail, 0);
945 i = utf8_width(_("Parts/Attachments:"));
946 partwid = MIN(i, hwid);
947 padwid = hdrcolor ? (hwid-partwid) : 0;
949 if(m1 > 0){
950 snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
951 if(!gf_puts(tmp, pc))
952 return(0);
955 utf8_snprintf(tmp, sizeof(tmp),
956 "%-*.*w%*.*s",
957 /* TRANSLATORS: A label */
958 partwid, partwid, _("Parts/Attachments:"),
959 padwid, padwid, "");
961 if(!((!hdrcolor || embed_color(hdrcolor, pc)) && gf_puts(tmp, pc) && gf_puts(NEWLINE, pc)))
962 return(0);
965 /*----- Figure max display widths -----*/
966 for(a = ps_global->atmts; a->description != NULL; a++){
967 if(MIME_VCALENDAR(a->body->type, a->body->subtype))
968 continue;
970 if((n = utf8_width(a->number)) > maxnumwid)
971 maxnumwid = n;
973 if((n = utf8_width(a->size)) > maxsizewid)
974 maxsizewid = n;
978 * ----- adjust max lengths for nice display -----
980 * marg _ D _ number _ Shown _ _ _ size _ _ description marg
984 avail = width - m1 - m2;
986 s1 = MAX(MIN(1, avail), 0);
987 avail -= s1;
989 dwid = MAX(MIN(1, avail), 0);
990 avail -= dwid;
992 s2 = MAX(MIN(1, avail), 0);
993 avail -= s2;
995 maxnumwid = MIN(maxnumwid, width/3);
996 maxnumwid = MAX(MIN(maxnumwid, avail), 0);
997 avail -= maxnumwid;
999 s3 = MAX(MIN(1, avail), 0);
1000 avail -= s3;
1002 shownwid = MAX(MIN(5, avail), 0);
1003 avail -= shownwid;
1005 s4 = MAX(MIN(3, avail), 0);
1006 avail -= s4;
1008 sizewid = MAX(MIN(maxsizewid, avail), 0);
1009 avail -= sizewid;
1011 s5 = MAX(MIN(2, avail), 0);
1012 avail -= s5;
1014 descwid = MAX(0, avail);
1016 /*----- Format the list of attachments -----*/
1017 for(a = ps_global->atmts; a->description != NULL; a++){
1018 COLOR_PAIR *lastc = NULL;
1019 char numbuf[50];
1020 int thisdescwid, padwid;
1022 if(MIME_VCALENDAR(a->body->type, a->body->subtype))
1023 continue;
1024 #ifdef SMIME
1025 if(a->body->type == TYPEMULTIPART
1026 && (strucmp(a->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0))
1027 continue;
1028 #endif /* SMIME */
1030 i = utf8_width((descwid > 2 && a->description) ? a->description : "");
1031 thisdescwid = MIN(i, descwid);
1032 padwid = hdrcolor ? (descwid-thisdescwid) : 0;
1034 if(m1 > 0){
1035 snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
1036 if(!gf_puts(tmp, pc))
1037 return(0);
1040 utf8_snprintf(tmp, sizeof(tmp),
1041 "%*.*s%*.*w%*.*s%-*.*w%*.*s%*.*w%*.*s%*.*w%*.*s%-*.*w",
1042 s1, s1, "",
1043 dwid, dwid,
1044 msgno_part_deleted(ps_global->mail_stream, msgno, a->number) ? "D" : "",
1045 s2, s2, "",
1046 maxnumwid, maxnumwid,
1047 a->number
1048 ? short_str(a->number, numbuf, sizeof(numbuf), maxnumwid, FrontDots)
1049 : "",
1050 s3, s3, "",
1051 shownwid, shownwid,
1052 a->shown ? "Shown" :
1053 (a->can_display != MCD_NONE && !(a->can_display & MCD_EXT_PROMPT))
1054 ? "OK " : "",
1055 s4, s4, "",
1056 sizewid, sizewid,
1057 a->size ? a->size : "",
1058 s5, s5, "",
1059 thisdescwid, thisdescwid,
1060 (descwid > 2 && a->description) ? a->description : "");
1062 if(!(!hdrcolor || embed_color(hdrcolor, pc)))
1063 return(0);
1065 if(F_ON(F_VIEW_SEL_ATTACH, ps_global) && handlesp){
1066 char buf[16], color[64];
1067 int l;
1068 HANDLE_S *h;
1070 for(tmpp = tmp; *tmpp && *tmpp == ' '; tmpp++)
1071 if(!(*pc)(' '))
1072 return(0);
1074 h = new_handle(handlesp);
1075 h->type = Attach;
1076 h->h.attach = a;
1078 snprintf(buf, sizeof(buf), "%d", h->key);
1079 buf[sizeof(buf)-1] = '\0';
1081 if(!(flgs & FM_NOCOLOR)
1082 && handle_start_color(color, sizeof(color), &l, 1)){
1083 lastc = get_cur_embedded_color();
1084 if(!gf_nputs(color, (long) l, pc))
1085 return(0);
1087 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)
1088 && (!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON))))
1089 return(0);
1091 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE)
1092 && (*pc)(strlen(buf)) && gf_puts(buf, pc)))
1093 return(0);
1095 else
1096 tmpp = tmp;
1098 if(!format_env_puts(tmpp, pc))
1099 return(0);
1101 if(F_ON(F_VIEW_SEL_ATTACH, ps_global) && handlesp){
1102 if(lastc){
1103 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
1104 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
1105 return(0);
1108 if(!embed_color(lastc, pc))
1109 return(0);
1111 free_color_pair(&lastc);
1113 else if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
1114 return(0);
1116 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)))
1117 return(0);
1120 if(padwid > 0){
1121 snprintf(tmp, sizeof(tmp), "%*.*s", padwid, padwid, "");
1122 if(!gf_puts(tmp, pc))
1123 return(0);
1126 if(!gf_puts(NEWLINE, pc))
1127 return(0);
1131 * Dashed line after list
1134 if(hdrcolor){
1135 avail = width - m1 - m2;
1136 hwid = MAX(avail, 0);
1138 dashwid = MAX(MIN(40, hwid-2), 0);
1139 padwid = hwid - dashwid;
1140 if(m1 > 0){
1141 snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
1142 if(!gf_puts(tmp, pc))
1143 return(0);
1146 snprintf(tmp, sizeof(tmp),
1147 "%s%*.*s",
1148 repeat_char(dashwid, '-'),
1149 padwid, padwid, "");
1151 else{
1152 avail = width - m1 -2;
1154 dashwid = MAX(MIN(40, avail), 0);
1155 avail -= dashwid;
1157 snprintf(tmp, sizeof(tmp),
1158 "%*.*s%s",
1159 m1, m1, "",
1160 repeat_char(dashwid, '-'));
1163 if(!((!hdrcolor || embed_color(hdrcolor, pc)) && gf_puts(tmp, pc) && gf_puts(NEWLINE, pc)))
1164 return(0);
1166 if(hdrcolor)
1167 free_color_pair(&hdrcolor);
1170 return(1);
1176 * format_blip_seen - if seen bit (which is usually cleared as a side-effect
1177 * of body part fetches as we're formatting) for the
1178 * given message isn't set (likely because there
1179 * weren't any parts suitable for display), then make
1180 * sure to set it here.
1183 format_blip_seen(long int msgno)
1185 MESSAGECACHE *mc;
1187 if(msgno > 0L && ps_global->mail_stream
1188 && msgno <= ps_global->mail_stream->nmsgs
1189 && (mc = mail_elt(ps_global->mail_stream, msgno))
1190 && !mc->seen
1191 && !ps_global->mail_stream->rdonly)
1192 mail_flag(ps_global->mail_stream, long2string(msgno), "\\SEEN", ST_SET);
1194 return(1);
1199 * is_an_env_hdr - is this name a header in the envelope structure?
1201 * name - the header name to check
1204 is_an_env_hdr(char *name)
1206 register int i;
1208 for(i = 0; envelope_hdrs[i].name; i++)
1209 if(!strucmp(name, envelope_hdrs[i].name))
1210 return(1);
1212 return(0);
1219 * is_an_addr_hdr - is this an address header?
1221 * name - the header name to check
1224 is_an_addr_hdr(char *fieldname)
1226 char fbuf[FBUF_LEN+1];
1227 char *colon, *fname;
1228 static char *addr_headers[] = {
1229 "from",
1230 "reply-to",
1231 "to",
1232 "cc",
1233 "bcc",
1234 "return-path",
1235 "sender",
1236 "x-sender",
1237 "x-x-sender",
1238 "resent-from",
1239 "resent-to",
1240 "resent-cc",
1241 NULL
1244 /* so it is pointing to NULL */
1245 char **p = addr_headers + sizeof(addr_headers)/sizeof(*addr_headers) - 1;
1247 if((colon = strindex(fieldname, ':')) != NULL){
1248 strncpy(fbuf, fieldname, MIN(colon-fieldname,sizeof(fbuf)));
1249 fbuf[MIN(colon-fieldname,sizeof(fbuf)-1)] = '\0';
1250 fname = fbuf;
1252 else
1253 fname = fieldname;
1255 if(fname && *fname){
1256 for(p = addr_headers; *p; p++)
1257 if(!strucmp(fname, *p))
1258 break;
1261 return((*p) ? 1 : 0);
1266 * Format a single field from the envelope
1268 void
1269 format_env_hdr(MAILSTREAM *stream, long int msgno, char *section, ENVELOPE *env,
1270 fmt_env_t fmt_env, gf_io_t pc, char *field, char *oacs, int flags)
1272 register int i;
1274 if(!fmt_env)
1275 fmt_env = format_envelope;
1277 for(i = 0; envelope_hdrs[i].name; i++)
1278 if(!strucmp(field, envelope_hdrs[i].name)){
1279 (*fmt_env)(stream, msgno, section, env, pc, envelope_hdrs[i].val, oacs, flags);
1280 return;
1286 * Look through header string beginning with "begin", for the next
1287 * occurrence of header "field". Set "start" to that. Set "end" to point one
1288 * position past all of the continuation lines that go with "field".
1289 * That is, if "end" is converted to a null
1290 * character then the string "start" will be the next occurrence of header
1291 * "field" including all of its continuation lines. Assume we
1292 * have CRLF's as end of lines.
1294 * If "field" is NULL, then we just leave "start" pointing to "begin" and
1295 * make "end" the end of that header.
1297 * Returns 1 if found, 0 if not.
1300 delineate_this_header(char *field, char *begin, char **start, char **end)
1302 char tmpfield[MAILTMPLEN+2]; /* copy of field with colon appended */
1303 char *p;
1304 char *begin_srch;
1306 if(field == NULL){
1307 if(!begin || !*begin || isspace((unsigned char)*begin))
1308 return 0;
1309 else
1310 *start = begin;
1312 else{
1313 strncpy(tmpfield, field, sizeof(tmpfield)-2);
1314 tmpfield[sizeof(tmpfield)-2] = '\0';
1315 strncat(tmpfield, ":", sizeof(tmpfield)-strlen(tmpfield)-1);
1316 tmpfield[sizeof(tmpfield)-1] = '\0';
1319 * We require that start is at the beginning of a line, so
1320 * either it equals begin (which we assume is the beginning of a
1321 * line) or it is preceded by a CRLF.
1323 begin_srch = begin;
1324 *start = srchstr(begin_srch, tmpfield);
1325 while(*start && *start != begin
1326 && !(*start - 2 >= begin && ISRFCEOL(*start - 2))){
1327 begin_srch = *start + 1;
1328 *start = srchstr(begin_srch, tmpfield);
1331 if(!*start)
1332 return 0;
1335 for(p = *start; *p; p++){
1336 if(ISRFCEOL(p)
1337 && (!isspace((unsigned char)*(p+2)) || *(p+2) == '\015')){
1339 * The final 015 in the test above is to test for the end
1340 * of the headers.
1342 *end = p+2;
1343 break;
1347 if(!*p)
1348 *end = p;
1350 return 1;
1356 handle_start_color(char *colorstring, size_t buflen, int *len, int use_hdr_color)
1358 *len = 0;
1360 if(pico_usingcolor()){
1361 char *fg = NULL, *bg = NULL, *s;
1362 char *basefg = NULL, *basebg = NULL;
1364 basefg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_FORE_COLOR
1365 : ps_global->VAR_NORM_FORE_COLOR;
1366 basebg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_BACK_COLOR
1367 : ps_global->VAR_NORM_BACK_COLOR;
1369 if(ps_global->VAR_SLCTBL_FORE_COLOR
1370 && colorcmp(ps_global->VAR_SLCTBL_FORE_COLOR, basefg))
1371 fg = ps_global->VAR_SLCTBL_FORE_COLOR;
1373 if(ps_global->VAR_SLCTBL_BACK_COLOR
1374 && colorcmp(ps_global->VAR_SLCTBL_BACK_COLOR, basebg))
1375 bg = ps_global->VAR_SLCTBL_BACK_COLOR;
1377 if(bg || fg){
1378 COLOR_PAIR *tmp;
1381 * The blacks are just known good colors for
1382 * testing whether the other color is good.
1384 if((tmp = new_color_pair(fg ? fg : colorx(COL_BLACK),
1385 bg ? bg : colorx(COL_BLACK))) != NULL){
1386 if(pico_is_good_colorpair(tmp))
1387 for(s = color_embed(fg, bg);
1388 (*len) < buflen && (colorstring[*len] = *s);
1389 s++, (*len)++)
1392 free_color_pair(&tmp);
1396 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
1397 strncpy(colorstring + (*len), url_embed(TAG_BOLDON), MIN(3,buflen-(*len)));
1398 *len += 2;
1402 colorstring[buflen-1] = '\0';
1404 return(*len != 0);
1409 handle_end_color(char *colorstring, size_t buflen, int *len)
1411 *len = 0;
1412 if(pico_usingcolor()){
1413 char *fg = NULL, *bg = NULL, *s;
1416 * We need to change the fg and bg colors back even if they
1417 * are the same as the Normal Colors so that color_a_quote
1418 * will have a chance to pick up those colors as the ones to
1419 * switch to. We don't do this before the handle above so that
1420 * the quote color will flow into the selectable item when
1421 * the selectable item color is partly the same as the
1422 * normal color. That is, suppose the normal color was black on
1423 * cyan and the selectable color was blue on cyan, only a fg color
1424 * change. We preserve the only-a-fg-color-change in a quote by
1425 * letting the quote background color flow into the selectable text.
1427 if(ps_global->VAR_SLCTBL_FORE_COLOR)
1428 fg = ps_global->VAR_NORM_FORE_COLOR;
1430 if(ps_global->VAR_SLCTBL_BACK_COLOR)
1431 bg = ps_global->VAR_NORM_BACK_COLOR;
1433 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
1434 strncpy(colorstring, url_embed(TAG_BOLDOFF), MIN(3,buflen));
1435 *len = 2;
1438 if(fg || bg)
1439 for(s = color_embed(fg, bg); (*len) < buflen && (colorstring[*len] = *s); s++, (*len)++)
1443 colorstring[buflen-1] = '\0';
1445 return(*len != 0);
1449 char *
1450 url_embed(int embed)
1452 static char buf[3] = {TAG_EMBED};
1453 buf[1] = embed;
1454 buf[2] = '\0';
1455 return(buf);
1460 * Paint the signature.
1463 color_signature(long int linenum, char *line, LT_INS_S **ins, void *is_in_sig)
1465 struct variable *vars = ps_global->vars;
1466 int *in_sig_block;
1467 COLOR_PAIR *col = NULL;
1469 if(is_in_sig == NULL)
1470 return 0;
1472 in_sig_block = (int *) is_in_sig;
1474 if(!strcmp(line, SIGDASHES))
1475 *in_sig_block = START_SIG_BLOCK;
1476 else if(*line == '\0')
1478 * Suggested by Eduardo: allow for a blank line right after
1479 * the sigdashes.
1481 *in_sig_block = (*in_sig_block == START_SIG_BLOCK)
1482 ? IN_SIG_BLOCK : OUT_SIG_BLOCK;
1483 else
1484 *in_sig_block = (*in_sig_block != OUT_SIG_BLOCK)
1485 ? IN_SIG_BLOCK : OUT_SIG_BLOCK;
1487 if(*in_sig_block != OUT_SIG_BLOCK
1488 && VAR_SIGNATURE_FORE_COLOR && VAR_SIGNATURE_BACK_COLOR
1489 && (col = new_color_pair(VAR_SIGNATURE_FORE_COLOR,
1490 VAR_SIGNATURE_BACK_COLOR))){
1491 if(!pico_is_good_colorpair(col))
1492 free_color_pair(&col);
1495 if(col){
1496 char *p, fg[RGBLEN + 1], bg[RGBLEN + 1], rgbbuf[RGBLEN + 1];
1498 ins = gf_line_test_new_ins(ins, line,
1499 color_embed(col->fg, col->bg),
1500 (2 * RGBLEN) + 4);
1502 strncpy(fg, color_to_asciirgb(VAR_NORM_FORE_COLOR), sizeof(fg));
1503 fg[sizeof(fg)-1] = '\0';
1504 strncpy(bg, color_to_asciirgb(VAR_NORM_BACK_COLOR), sizeof(bg));
1505 bg[sizeof(bg)-1] = '\0';
1508 * Loop watching colors, and override with
1509 * signature color whenever the normal foreground and background
1510 * colors are in force.
1513 for(p = line; *p; )
1514 if(*p++ == TAG_EMBED){
1516 switch(*p++){
1517 case TAG_HANDLE :
1518 p += *p + 1; /* skip handle key */
1519 break;
1521 case TAG_FGCOLOR :
1522 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1523 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1524 p += RGBLEN; /* advance past color value */
1526 if(!colorcmp(rgbbuf, VAR_NORM_FORE_COLOR)
1527 && !colorcmp(bg, VAR_NORM_BACK_COLOR))
1528 ins = gf_line_test_new_ins(ins, p,
1529 color_embed(col->fg,NULL),
1530 RGBLEN + 2);
1531 break;
1533 case TAG_BGCOLOR :
1534 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1535 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1536 p += RGBLEN; /* advance past color value */
1538 if(!colorcmp(rgbbuf, VAR_NORM_BACK_COLOR)
1539 && !colorcmp(fg, VAR_NORM_FORE_COLOR))
1540 ins = gf_line_test_new_ins(ins, p,
1541 color_embed(NULL,col->bg),
1542 RGBLEN + 2);
1544 break;
1546 default :
1547 break;
1551 ins = gf_line_test_new_ins(ins, line + strlen(line),
1552 color_embed(VAR_NORM_FORE_COLOR,
1553 VAR_NORM_BACK_COLOR),
1554 (2 * RGBLEN) + 4);
1555 free_color_pair(&col);
1558 return 0;
1563 * Line filter to add color to displayed headers.
1566 color_headers(long int linenum, char *line, LT_INS_S **ins, void *local)
1568 static char field[FBUF_LEN + 1];
1569 char fg[RGBLEN + 1], bg[RGBLEN + 1], rgbbuf[RGBLEN + 1];
1570 char *p, *q, *value, *beg;
1571 COLOR_PAIR *color;
1572 int in_quote = 0, in_comment = 0, did_color = 0;
1573 struct variable *vars = ps_global->vars;
1575 field[FBUF_LEN] = '\0';
1577 if(isspace((unsigned char)*line)) /* continuation line */
1578 value = line;
1579 else{
1580 if(!(value = strindex(line, ':')))
1581 return(0);
1583 memset(field, 0, sizeof(field));
1584 strncpy(field, line, MIN(value-line, sizeof(field)-1));
1587 for(value++; isspace((unsigned char)*value); value++)
1590 strncpy(fg, color_to_asciirgb(VAR_HEADER_GENERAL_FORE_COLOR), sizeof(fg));
1591 fg[sizeof(fg)-1] = '\0';
1592 strncpy(bg, color_to_asciirgb(VAR_HEADER_GENERAL_BACK_COLOR), sizeof(bg));
1593 bg[sizeof(bg)-1] = '\0';
1596 * Split into two cases depending on whether this is a header which
1597 * contains addresses or not. We may color addresses separately.
1599 if(is_an_addr_hdr(field)){
1602 * If none of the patterns are for this header, don't bother parsing
1603 * and checking each address.
1605 if(!any_hdr_color(field))
1606 return(0);
1609 * First check for patternless patterns which color whole line.
1611 if((color = hdr_color(field, NULL, ps_global->hdr_colors)) != NULL){
1612 if(pico_is_good_colorpair(color)){
1613 ins = gf_line_test_new_ins(ins, value,
1614 color_embed(color->fg, color->bg),
1615 (2 * RGBLEN) + 4);
1616 strncpy(fg, color_to_asciirgb(color->fg), sizeof(fg));
1617 fg[sizeof(fg)-1] = '\0';
1618 strncpy(bg, color_to_asciirgb(color->bg), sizeof(bg));
1619 bg[sizeof(bg)-1] = '\0';
1620 did_color++;
1622 else
1623 free_color_pair(&color);
1627 * Then go through checking address by address.
1628 * Keep track of quotes and watch for color changes, and override
1629 * with most recent header color whenever the normal foreground
1630 * and background colors are in force.
1632 beg = p = value;
1633 while(*p){
1634 switch(*p){
1635 case '\\':
1636 /* skip next character */
1637 if(*(p+1) && (in_comment || in_quote))
1638 p += 2;
1639 else
1640 p++;
1642 break;
1644 case '"':
1645 if(!in_comment)
1646 in_quote = 1 - in_quote;
1648 p++;
1649 break;
1651 case '(':
1652 in_comment++;
1653 p++;
1654 break;
1656 case ')':
1657 if(in_comment > 0)
1658 in_comment--;
1660 p++;
1661 break;
1663 case ',':
1664 if(!(in_quote || in_comment)){
1665 /* we reached the end of this address */
1666 *p = '\0';
1667 if(color)
1668 free_color_pair(&color);
1670 if((color = hdr_color(field, beg,
1671 ps_global->hdr_colors)) != NULL){
1672 if(pico_is_good_colorpair(color)){
1673 did_color++;
1674 ins = gf_line_test_new_ins(ins, beg,
1675 color_embed(color->fg,
1676 color->bg),
1677 (2 * RGBLEN) + 4);
1678 *p = ',';
1679 for(q = p; q > beg &&
1680 isspace((unsigned char)*(q-1)); q--)
1683 ins = gf_line_test_new_ins(ins, q,
1684 color_embed(fg, bg),
1685 (2 * RGBLEN) + 4);
1687 else
1688 free_color_pair(&color);
1690 else
1691 *p = ',';
1693 for(p++; isspace((unsigned char)*p); p++)
1696 beg = p;
1698 else
1699 p++;
1701 break;
1703 case TAG_EMBED:
1704 switch(*(++p)){
1705 case TAG_HANDLE:
1706 p++;
1707 p += *p + 1; /* skip handle key */
1708 break;
1710 case TAG_FGCOLOR:
1711 p++;
1712 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1713 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1714 p += RGBLEN; /* advance past color value */
1716 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_FORE_COLOR))
1717 ins = gf_line_test_new_ins(ins, p,
1718 color_embed(color->fg,NULL),
1719 RGBLEN + 2);
1720 break;
1722 case TAG_BGCOLOR:
1723 p++;
1724 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1725 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1726 p += RGBLEN; /* advance past color value */
1728 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_BACK_COLOR))
1729 ins = gf_line_test_new_ins(ins, p,
1730 color_embed(NULL,color->bg),
1731 RGBLEN + 2);
1733 break;
1735 default:
1736 break;
1739 break;
1741 default:
1742 p++;
1743 break;
1747 for(q = beg; *q && isspace((unsigned char)*q); q++)
1750 if(*q && !(in_quote || in_comment)){
1751 /* we reached the end of this address */
1752 if(color)
1753 free_color_pair(&color);
1755 if((color = hdr_color(field, beg, ps_global->hdr_colors)) != NULL){
1756 if(pico_is_good_colorpair(color)){
1757 did_color++;
1758 ins = gf_line_test_new_ins(ins, beg,
1759 color_embed(color->fg,
1760 color->bg),
1761 (2 * RGBLEN) + 4);
1762 for(q = p; q > beg && isspace((unsigned char)*(q-1)); q--)
1765 ins = gf_line_test_new_ins(ins, q,
1766 color_embed(fg, bg),
1767 (2 * RGBLEN) + 4);
1769 else
1770 free_color_pair(&color);
1774 if(color)
1775 free_color_pair(&color);
1777 if(did_color)
1778 ins = gf_line_test_new_ins(ins, line + strlen(line),
1779 color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
1780 VAR_HEADER_GENERAL_BACK_COLOR),
1781 (2 * RGBLEN) + 4);
1783 else{
1785 color = hdr_color(field, value, ps_global->hdr_colors);
1787 if(color){
1788 if(pico_is_good_colorpair(color)){
1789 ins = gf_line_test_new_ins(ins, value,
1790 color_embed(color->fg, color->bg),
1791 (2 * RGBLEN) + 4);
1794 * Loop watching colors, and override with header
1795 * color whenever the normal foreground and background
1796 * colors are in force.
1798 p = value;
1799 while(*p)
1800 if(*p++ == TAG_EMBED){
1802 switch(*p++){
1803 case TAG_HANDLE:
1804 p += *p + 1; /* skip handle key */
1805 break;
1807 case TAG_FGCOLOR:
1808 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1809 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1810 p += RGBLEN; /* advance past color value */
1812 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_FORE_COLOR)
1813 && !colorcmp(bg, VAR_HEADER_GENERAL_BACK_COLOR))
1814 ins = gf_line_test_new_ins(ins, p,
1815 color_embed(color->fg,NULL),
1816 RGBLEN + 2);
1817 break;
1819 case TAG_BGCOLOR:
1820 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1821 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1822 p += RGBLEN; /* advance past color value */
1824 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_BACK_COLOR)
1825 && !colorcmp(fg, VAR_HEADER_GENERAL_FORE_COLOR))
1826 ins = gf_line_test_new_ins(ins, p,
1827 color_embed(NULL,color->bg),
1828 RGBLEN + 2);
1830 break;
1832 default:
1833 break;
1837 ins = gf_line_test_new_ins(ins, line + strlen(line),
1838 color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
1839 VAR_HEADER_GENERAL_BACK_COLOR),
1840 (2 * RGBLEN) + 4);
1843 free_color_pair(&color);
1847 return(0);
1852 url_hilite(long int linenum, char *line, LT_INS_S **ins, void *local)
1854 register char *lp, *up = NULL, *urlp = NULL,
1855 *weburlp = NULL, *mailurlp = NULL;
1856 int n, n1, n2, n3, l;
1857 char buf[256], color[256];
1858 HANDLE_S *h;
1859 URL_HILITE_S *uh;
1861 for(lp = line; ; lp = up + n){
1862 /* scan for all of them so we can choose the first */
1863 if(F_ON(F_VIEW_SEL_URL,ps_global))
1864 urlp = rfc1738_scan(lp, &n1);
1865 if(F_ON(F_VIEW_SEL_URL_HOST,ps_global))
1866 weburlp = web_host_scan(lp, &n2);
1867 if(F_ON(F_SCAN_ADDR,ps_global))
1868 mailurlp = mail_addr_scan(lp, &n3);
1870 if(urlp || weburlp || mailurlp){
1871 up = urlp ? urlp :
1872 weburlp ? weburlp : mailurlp;
1873 if(up == urlp && weburlp && weburlp < up)
1874 up = weburlp;
1875 if(mailurlp && mailurlp < up)
1876 up = mailurlp;
1878 if(up == urlp){
1879 n = n1;
1880 weburlp = mailurlp = NULL;
1882 else if(up == weburlp){
1883 n = n2;
1884 mailurlp = NULL;
1886 else{
1887 n = n3;
1888 weburlp = NULL;
1891 else
1892 break;
1894 uh = (URL_HILITE_S *) local;
1896 h = new_handle(uh->handlesp);
1897 h->type = URL;
1898 h->h.url.path = (char *) fs_get((n + 10) * sizeof(char));
1899 snprintf(h->h.url.path, n+10, "%s%.*s",
1900 weburlp ? "http://" : (mailurlp ? "mailto:" : ""), n, up);
1901 h->h.url.path[n+10-1] = '\0';
1903 if(handle_start_color(color, sizeof(color), &l, uh->hdr_color))
1904 ins = gf_line_test_new_ins(ins, up, color, l);
1905 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global))
1906 ins = gf_line_test_new_ins(ins, up, url_embed(TAG_BOLDON), 2);
1908 buf[0] = TAG_EMBED;
1909 buf[1] = TAG_HANDLE;
1910 snprintf(&buf[3], sizeof(buf)-3, "%d", h->key);
1911 buf[sizeof(buf)-1] = '\0';
1912 buf[2] = strlen(&buf[3]);
1913 ins = gf_line_test_new_ins(ins, up, buf, (int) buf[2] + 3);
1915 /* in case it was the current selection */
1916 ins = gf_line_test_new_ins(ins, up + n, url_embed(TAG_INVOFF), 2);
1918 if(scroll_handle_end_color(color, sizeof(color), &l, uh->hdr_color))
1919 ins = gf_line_test_new_ins(ins, up + n, color, l);
1920 else
1921 ins = gf_line_test_new_ins(ins, up + n, url_embed(TAG_BOLDOFF), 2);
1923 urlp = weburlp = mailurlp = NULL;
1926 return(0);
1931 url_hilite_hdr(long int linenum, char *line, LT_INS_S **ins, void *local)
1933 static int check_for_urls = 0;
1934 register char *lp;
1936 if(isspace((unsigned char)*line)) /* continuation, check or not
1937 depending on last line */
1938 lp = line;
1939 else{
1940 check_for_urls = 0;
1941 if((lp = strchr(line, ':')) != NULL){ /* there ought to always be a colon */
1942 FieldType ft;
1944 *lp = '\0';
1946 if(((ft = pine_header_standard(line)) == FreeText
1947 || ft == Subject
1948 || ft == TypeUnknown)
1949 && strucmp(line, "message-id")
1950 && strucmp(line, "newsgroups")
1951 && strucmp(line, "references")
1952 && strucmp(line, "in-reply-to")
1953 && strucmp(line, "received")
1954 && strucmp(line, "date")){
1955 check_for_urls = 1;
1958 *lp = ':';
1962 if(check_for_urls)
1963 (void) url_hilite(linenum, lp + 1, ins, local);
1965 return(0);
1970 pad_to_right_edge(long int linenum, char *line, LT_INS_S **ins, void *local)
1972 char *p;
1973 int wid = 0;
1974 int total_wid;
1975 struct variable *vars = ps_global->vars;
1977 if(!line[0])
1978 return 0;
1980 total_wid = *((int *) local);
1982 /* calculate width of line */
1983 p = line;
1984 while(*p){
1986 switch(*p){
1987 case TAG_EMBED:
1988 p++;
1989 switch(*p){
1990 case TAG_HANDLE:
1991 p++;
1992 p += *p + 1; /* skip handle key */
1993 break;
1995 case TAG_FGCOLOR :
1996 case TAG_BGCOLOR :
1997 p += (RGBLEN + 1); /* 1 for TAG, RGBLEN for color */
1998 break;
2000 case TAG_INVON:
2001 case TAG_INVOFF:
2002 case TAG_BOLDON:
2003 case TAG_BOLDOFF:
2004 case TAG_ULINEON:
2005 case TAG_ULINEOFF:
2006 p++;
2007 break;
2009 default: /* literal embed char */
2010 break;
2013 break;
2015 case TAB:
2016 p++;
2017 while(((++wid) & 0x07) != 0) /* add tab's spaces */
2020 break;
2022 default:
2023 wid += width_at_this_position((unsigned char *) p, strlen(p));
2024 p++;
2025 break;
2029 if(total_wid > wid){
2030 ins = gf_line_test_new_ins(ins, line + strlen(line),
2031 color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
2032 VAR_HEADER_GENERAL_BACK_COLOR),
2033 (2 * RGBLEN) + 4);
2034 ins = gf_line_test_new_ins(ins, line+strlen(line),
2035 repeat_char(total_wid-wid, ' '), total_wid-wid);
2036 ins = gf_line_test_new_ins(ins, line + strlen(line),
2037 color_embed(VAR_NORM_FORE_COLOR,
2038 VAR_NORM_BACK_COLOR),
2039 (2 * RGBLEN) + 4);
2042 return(0);
2047 #define UES_LEN 12
2048 #define UES_MAX 32
2050 url_external_specific_handler(char *url, int len)
2052 static char list[UES_LEN * UES_MAX];
2054 if(url){
2055 char *p;
2056 int i;
2058 for(i = 0; i < UES_MAX && *(p = &list[i * UES_LEN]); i++)
2059 if(strlen(p) == len && !struncmp(p, url, len))
2060 return(1);
2062 else{ /* initialize! */
2063 char **l, *test, *cmd, *p, *p2;
2064 int i = 0, n;
2066 memset(list, 0, sizeof(list));
2067 for(l = ps_global->VAR_BROWSER ; l && *l; l++){
2068 get_pair(*l, &test, &cmd, 1, 1);
2070 if((p = srchstr(test, "_scheme(")) && (p2 = strstr(p+8, ")_"))){
2071 *p2 = '\0';
2073 for(p += 8; *p && i < UES_MAX; p += n)
2074 if((p2 = strchr(p, ',')) != NULL){
2075 if((n = p2 - p) < UES_LEN){
2076 strncpy(&list[i * UES_LEN], p, MIN(n, sizeof(list)-(i * UES_LEN)));
2077 i++;
2079 else
2080 dprint((1,
2081 "* * * HANDLER TOO LONG: %.*s\n", n,
2082 p ? p : "?"));
2084 n++;
2086 else{
2087 if(strlen(p) <= UES_LEN){
2088 strncpy(&list[i * UES_LEN], p, sizeof(list)-(i * UES_LEN));
2089 i++;
2092 break;
2096 if(test)
2097 fs_give((void **) &test);
2099 if(cmd)
2100 fs_give((void **) &cmd);
2104 return(0);
2109 url_imap_folder(char *true_url, char **folder, imapuid_t *uid_val,
2110 imapuid_t *uid, char **search, int silent)
2112 char *url, *scheme, *p, *cmd, *server = NULL,
2113 *user = NULL, *auth = NULL, *mailbox = NULL,
2114 *section = NULL;
2115 size_t l;
2116 int rv = URL_IMAP_ERROR;
2119 * Since we're planting nulls, operate on a temporary copy...
2121 scheme = silent ? NULL : "IMAP";
2122 url = cpystr(true_url + 7);
2124 /* Try to pick apart the "iserver" portion */
2125 if((cmd = strchr(url, '/')) != NULL){ /* iserver "/" [mailbox] ? */
2126 *cmd++ = '\0';
2128 else{
2129 dprint((2, "-- URL IMAP FOLDER: missing: %s\n",
2130 url ? url : "?"));
2131 cmd = &url[strlen(url)-1]; /* assume only iserver */
2134 if((p = strchr(url, '@')) != NULL){ /* user | auth | pass? */
2135 *p++ = '\0';
2136 server = rfc1738_str(p);
2138 /* only ";auth=*" supported (and also ";auth=anonymous") */
2139 if((p = srchstr(url, ";auth=")) != NULL){
2140 *p = '\0';
2141 auth = rfc1738_str(p + 6);
2144 if(*url)
2145 user = rfc1738_str(url);
2147 else
2148 server = rfc1738_str(url);
2150 if(!*server)
2151 return(url_bogus_imap(&url, scheme, "No server specified"));
2154 * "iserver" in hand, pick apart the "icommand"...
2156 p = NULL;
2157 if(!*cmd || (p = srchstr(cmd, ";type="))){
2158 char *criteria;
2161 * No "icommand" (all top-level folders) or "imailboxlist"...
2163 if(p){
2164 *p = '\0'; /* tie off criteria */
2165 criteria = rfc1738_str(cmd); /* get "enc_list_mailbox" */
2166 if(!strucmp(p = rfc1738_str(p+6), "lsub"))
2167 rv |= URL_IMAP_IMBXLSTLSUB;
2168 else if(strucmp(p, "list"))
2169 return(url_bogus_imap(&url, scheme,
2170 "Invalid list type specified"));
2172 else{
2173 rv |= URL_IMAP_ISERVERONLY;
2174 criteria = "";
2177 /* build folder list from specified server/criteria/list-method */
2178 l = strlen(server) + strlen(criteria) + 10 + (user ? (strlen(user)+2) : 9);
2179 *folder = (char *) fs_get((l+1) * sizeof(char));
2180 snprintf(*folder, l+1, "{%s/%s%s%s}%s%s%s", server,
2181 user ? "user=\"" : "Anonymous",
2182 user ? user : "",
2183 user ? "\"" : "",
2184 *criteria ? "[" : "", criteria, *criteria ? "[" : "");
2185 (*folder)[l] = '\0';
2186 rv |= URL_IMAP_IMAILBOXLIST;
2188 else{
2189 if((p = srchstr(cmd, "/;uid=")) != NULL){ /* "imessagepart" */
2190 *p = '\0'; /* tie off mailbox [uidvalidity] */
2191 if((section = srchstr(p += 6, "/;section=")) != NULL){
2192 *section = '\0'; /* tie off UID */
2193 section = rfc1738_str(section + 10);
2194 /* BUG: verify valid section spec ala rfc 2060 */
2195 dprint((2,
2196 "-- URL IMAP FOLDER: section not used: %s\n",
2197 section ? section : "?"));
2200 if(!(*uid = rfc1738_num(&p)) || *p) /* decode UID */
2201 return(url_bogus_imap(&url, scheme, "Invalid data in UID"));
2203 /* optional "uidvalidity"? */
2204 if((p = srchstr(cmd, ";uidvalidity=")) != NULL){
2205 *p = '\0';
2206 p += 13;
2207 if(!(*uid_val = rfc1738_num(&p)) || *p)
2208 return(url_bogus_imap(&url, scheme,
2209 "Invalid UIDVALIDITY"));
2212 mailbox = rfc1738_str(cmd);
2213 rv = URL_IMAP_IMESSAGEPART;
2215 else{ /* "imessagelist" */
2216 /* optional "uidvalidity"? */
2217 if((p = srchstr(cmd, ";uidvalidity=")) != NULL){
2218 *p = '\0';
2219 p += 13;
2220 if(!(*uid_val = rfc1738_num(&p)) || *p)
2221 return(url_bogus_imap(&url, scheme,
2222 "Invalid UIDVALIDITY"));
2225 /* optional "enc_search"? */
2226 if((p = strchr(cmd, '?')) != NULL){
2227 *p = '\0';
2228 if(search)
2229 *search = cpystr(rfc1738_str(p + 1));
2230 /* BUG: verify valid search spec ala rfc 2060 */
2233 mailbox = rfc1738_str(cmd);
2234 rv = URL_IMAP_IMESSAGELIST;
2237 if(auth && *auth != '*' && strucmp(auth, "anonymous"))
2238 q_status_message(SM_ORDER, 3, 3,
2239 "Unsupported authentication method. Using standard login.");
2242 * At this point our structure should contain the
2243 * digested url. Now put it together for c-client...
2245 l = strlen(server) + 8 + (mailbox ? strlen(mailbox) : 0)
2246 + (user ? (strlen(user)+2) : 9);
2247 *folder = (char *) fs_get((l+1) * sizeof(char));
2248 snprintf(*folder, l+1, "{%s%s%s%s%s}%s", server,
2249 (user || !(auth && strucmp(auth, "anonymous"))) ? "/" : "",
2250 user ? "user=\"" : ((auth && strucmp(auth, "anonymous")) ? "" : "Anonymous"),
2251 user ? user : "",
2252 user ? "\"" : "",
2253 mailbox);
2254 (*folder)[l] = '\0';
2257 fs_give((void **) &url);
2258 return(rv);
2263 url_bogus_imap(char **freeme, char *url, char *problem)
2265 fs_give((void **) freeme);
2266 (void) url_bogus(url, problem);
2267 return(URL_IMAP_ERROR);
2272 * url_bogus - report url syntax errors and such
2275 url_bogus(char *url, char *reason)
2277 dprint((2, "-- bogus url \"%s\": %s\n",
2278 url ? url : "<NULL URL>", reason ? reason : "?"));
2279 if(url)
2280 q_status_message3(SM_ORDER|SM_DING, 2, 3,
2281 "Malformed \"%.*s\" URL: %.200s",
2282 (void *) (strchr(url, ':') - url), url, reason);
2284 return(0);
2289 /*----------------------------------------------------------------------
2290 Format header text suitable for display
2292 Args: stream -- mail stream for various header text fetches
2293 msgno -- sequence number in stream of message we're interested in
2294 section -- which section of message
2295 env -- pointer to msg's envelope
2296 hdrs -- struct containing what's to get formatted
2297 prefix -- prefix to append to each output line
2298 handlesp -- address of pointer to the message's handles
2299 flags -- FM_ flags, see pith/mailview.h
2300 final_pc -- function to write header text with
2302 Result: 0 if all's well, -1 if write error, 1 if fetch error
2304 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2305 in the local convention.
2307 ----*/
2308 #define FHT_OK 0
2309 #define FHT_WRTERR -1
2310 #define FHT_FTCHERR 1
2312 format_header(MAILSTREAM *stream, long int msgno, char *section, ENVELOPE *env,
2313 HEADER_S *hdrs, char *prefix, HANDLE_S **handlesp, int flags,
2314 fmt_env_t fmt_env, gf_io_t final_pc)
2316 int rv = FHT_OK;
2317 int nfields, i;
2318 char *h = NULL, **fields = NULL, **v, *q, *start,
2319 *finish, *current;
2320 STORE_S *tmp_store;
2321 URL_HILITE_S uh;
2322 gf_io_t tmp_pc, tmp_gc;
2323 struct variable *vars = ps_global->vars;
2325 if((tmp_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL)
2326 gf_set_so_writec(&tmp_pc, tmp_store);
2327 else
2328 return(FHT_WRTERR);
2330 if(!fmt_env)
2331 fmt_env = format_envelope;
2333 if(ps_global->full_header == 2){
2334 rv = format_raw_header(stream, msgno, section, tmp_pc);
2336 else{
2338 * First, calculate how big a fields array we need.
2341 /* Custom header viewing list specified */
2342 if(hdrs->type == HD_LIST){
2343 /* view all these headers */
2344 if(!hdrs->except){
2345 for(nfields = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
2346 if(!is_an_env_hdr(q))
2347 nfields++;
2349 /* view all except these headers */
2350 else{
2351 for(nfields = 0, v = hdrs->h.l; *v != NULL; v++)
2352 nfields++;
2354 if(nfields > 1)
2355 nfields--; /* subtract one for ALL_EXCEPT field */
2358 else
2359 nfields = 6; /* default view */
2361 /* allocate pointer space */
2362 if(nfields){
2363 fields = (char **)fs_get((size_t)(nfields+1) * sizeof(char *));
2364 memset(fields, 0, (size_t)(nfields+1) * sizeof(char *));
2367 if(hdrs->type == HD_LIST){
2368 /* view all these headers */
2369 if(!hdrs->except){
2370 /* put the non-envelope headers in fields */
2371 if(nfields)
2372 for(i = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
2373 if(!is_an_env_hdr(q))
2374 fields[i++] = q;
2376 /* view all except these headers */
2377 else{
2378 /* put the list of headers not to view in fields */
2379 if(nfields)
2380 for(i = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
2381 if(strucmp(ALL_EXCEPT, q))
2382 fields[i++] = q;
2385 v = hdrs->h.l;
2387 else{
2388 if(nfields){
2389 fields[i = 0] = "Resent-Date";
2390 fields[++i] = "Resent-From";
2391 fields[++i] = "Resent-To";
2392 fields[++i] = "Resent-cc";
2393 fields[++i] = "Resent-Subject";
2396 v = fields;
2399 /* custom view with exception list */
2400 if(hdrs->type == HD_LIST && hdrs->except){
2402 * Go through each header in h and print it.
2403 * First we check to see if it is an envelope header so we
2404 * can print our envelope version of it instead of the raw version.
2407 /* fetch all the other headers */
2408 if(nfields)
2409 h = pine_fetchheader_lines_not(stream, msgno, section, fields);
2411 for(current = h;
2412 h && delineate_this_header(NULL, current, &start, &finish);
2413 current = finish){
2414 char tmp[MAILTMPLEN+1];
2415 char *colon_loc;
2417 colon_loc = strindex(start, ':');
2418 if(colon_loc && colon_loc < finish){
2419 strncpy(tmp, start, MIN(colon_loc-start, sizeof(tmp)-1));
2420 tmp[MIN(colon_loc-start, sizeof(tmp)-1)] = '\0';
2422 else
2423 colon_loc = NULL;
2425 if(colon_loc && is_an_env_hdr(tmp)){
2426 char *dummystart, *dummyfinish;
2429 * Pretty format for env hdrs.
2430 * If the same header appears more than once, only
2431 * print the last to avoid duplicates.
2432 * They should have been combined in the env when parsed.
2434 if(!delineate_this_header(tmp, current+1, &dummystart,
2435 &dummyfinish))
2436 format_env_hdr(stream, msgno, section, env,
2437 fmt_env, tmp_pc, tmp, hdrs->charset, flags);
2439 else{
2440 if((rv = format_raw_hdr_string(start, finish, tmp_pc,
2441 hdrs->charset, flags)) != 0)
2442 goto write_error;
2443 else
2444 start = finish;
2448 /* custom view or default */
2449 else{
2450 /* fetch the non-envelope headers */
2451 if(nfields)
2452 h = pine_fetchheader_lines(stream, msgno, section, fields);
2454 /* default envelope for default view */
2455 if(hdrs->type == HD_BFIELD)
2456 (*fmt_env)(stream, msgno, section, env, tmp_pc, hdrs->h.b, hdrs->charset, flags);
2458 /* go through each header in list, v initialized above */
2459 for(; (q = *v) != NULL; v++){
2460 if(is_an_env_hdr(q)){
2461 /* pretty format for env hdrs */
2462 format_env_hdr(stream, msgno, section, env,
2463 fmt_env, tmp_pc, q, hdrs->charset, flags);
2465 else{
2467 * Go through h finding all occurrences of this header
2468 * and all continuation lines, and output.
2470 for(current = h;
2471 h && delineate_this_header(q,current,&start,&finish);
2472 current = finish){
2473 if((rv = format_raw_hdr_string(start, finish, tmp_pc,
2474 hdrs->charset, flags)) != 0)
2475 goto write_error;
2476 else
2477 start = finish;
2485 write_error:
2487 gf_clear_so_writec(tmp_store);
2489 if(!rv){ /* valid data? Do wrapping and filtering... */
2490 int column;
2491 char *errstr, *display_filter = NULL, trigger[MAILTMPLEN];
2492 STORE_S *df_store = NULL;
2494 so_seek(tmp_store, 0L, 0);
2496 column = (flags & FM_DISPLAY) ? ps_global->ttyo->screen_cols : 80;
2499 * Test for and act on any display filter
2500 * This barely makes sense. The display filter is going
2501 * to be getting UTF-8'ized headers here. In pre-alpine
2502 * pine the display filter was being fed already decoded
2503 * headers in whatever character set they were in.
2504 * The good news is that that didn't make much
2505 * sense either, so this shouldn't break anything.
2506 * It seems unlikely that anybody is doing anything useful
2507 * with the header part of display filters.
2509 if(ps_global->tools.display_filter
2510 && ps_global->tools.display_filter_trigger
2511 && (display_filter = (*ps_global->tools.display_filter_trigger)(NULL, trigger, sizeof(trigger)))){
2512 if((df_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
2514 gf_set_so_writec(&tmp_pc, df_store);
2515 gf_set_so_readc(&tmp_gc, df_store);
2516 if((errstr = (*ps_global->tools.display_filter)(display_filter, tmp_store, tmp_pc, NULL)) != NULL){
2517 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2518 _("Formatting error: %s"), errstr);
2519 rv = FHT_WRTERR;
2521 else
2522 so_seek(df_store, 0L, 0);
2524 gf_clear_so_writec(df_store);
2526 else{
2527 q_status_message(SM_ORDER | SM_DING, 3, 3,
2528 "No space for filtered text.");
2529 rv = FHT_WRTERR;
2532 else{
2533 so_seek(tmp_store, 0L, 0);
2534 gf_set_so_readc(&tmp_gc, tmp_store);
2537 if(!rv){
2538 int *margin, wrapflags = GFW_ONCOMMA;
2540 gf_filter_init();
2541 gf_link_filter(gf_local_nvtnl, NULL);
2543 if((F_ON(F_VIEW_SEL_URL, ps_global)
2544 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
2545 || F_ON(F_SCAN_ADDR, ps_global))
2546 && handlesp){
2547 gf_link_filter(gf_line_test,
2548 gf_line_test_opt(url_hilite_hdr,
2549 gf_url_hilite_opt(&uh,handlesp,1)));
2550 wrapflags |= GFW_HANDLES;
2553 if((flags & FM_DISPLAY)
2554 && !(flags & FM_NOCOLOR)
2555 && pico_usingcolor()
2556 && ((VAR_NORM_FORE_COLOR
2557 && VAR_HEADER_GENERAL_FORE_COLOR
2558 && colorcmp(VAR_NORM_FORE_COLOR, VAR_HEADER_GENERAL_FORE_COLOR))
2560 (VAR_NORM_BACK_COLOR
2561 && VAR_HEADER_GENERAL_BACK_COLOR
2562 && colorcmp(VAR_NORM_BACK_COLOR, VAR_HEADER_GENERAL_BACK_COLOR))))
2563 wrapflags |= GFW_HDRCOLOR;
2565 if((flags & FM_DISPLAY)
2566 && !(flags & FM_NOCOLOR)
2567 && pico_usingcolor()
2568 && ps_global->hdr_colors){
2569 gf_link_filter(gf_line_test,
2570 gf_line_test_opt(color_headers, NULL));
2571 wrapflags |= (GFW_HANDLES | GFW_HDRCOLOR);
2574 if(prefix && *prefix)
2575 column = MAX(column-strlen(prefix), 50);
2577 margin = format_view_margin();
2579 if(!(flags & FM_NOWRAP))
2580 gf_link_filter(gf_wrap,
2581 gf_wrap_filter_opt(column, column,
2582 (flags & FM_NOINDENT) ? NULL : margin,
2583 4, wrapflags));
2585 if(prefix && *prefix)
2586 gf_link_filter(gf_prefix, gf_prefix_opt(prefix));
2588 if((flags & FM_DISPLAY)
2589 && !(flags & FM_NOCOLOR)
2590 && pico_usingcolor()
2591 && ((VAR_NORM_FORE_COLOR
2592 && VAR_HEADER_GENERAL_FORE_COLOR
2593 && colorcmp(VAR_NORM_FORE_COLOR, VAR_HEADER_GENERAL_FORE_COLOR))
2595 (VAR_NORM_BACK_COLOR
2596 && VAR_HEADER_GENERAL_BACK_COLOR
2597 && colorcmp(VAR_NORM_BACK_COLOR, VAR_HEADER_GENERAL_BACK_COLOR)))){
2598 int right_margin;
2599 int total_wid;
2601 right_margin = margin ? margin[1] : 0;
2602 total_wid = column - right_margin;
2604 gf_link_filter(gf_line_test,
2605 gf_line_test_opt(pad_to_right_edge, (void *) &total_wid));
2606 wrapflags |= GFW_HANDLES;
2609 gf_link_filter(gf_nvtnl_local, NULL);
2611 if((errstr = gf_pipe(tmp_gc, final_pc)) != NULL){
2612 rv = FHT_WRTERR;
2613 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2614 "Can't build header : %.200s", errstr);
2618 if(df_store){
2619 gf_clear_so_readc(df_store);
2620 so_give(&df_store);
2622 else
2623 gf_clear_so_readc(tmp_store);
2626 so_give(&tmp_store);
2628 if(h)
2629 fs_give((void **)&h);
2631 if(fields)
2632 fs_give((void **)&fields);
2634 return(rv);
2638 /*----------------------------------------------------------------------
2639 Format RAW header text for display
2641 Args: stream -- mail stream for various header text fetches
2642 rawno -- sequence number in stream of message we're interested in
2643 section -- which section of message
2644 pc -- function to write header text with
2646 Result: 0 if all's well, -1 if write error, 1 if fetch error
2648 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2649 in the local convention.
2651 ----*/
2653 format_raw_header(MAILSTREAM *stream, long int msgno, char *section, gf_io_t pc)
2655 char *h = mail_fetch_header(stream, msgno, section, NULL, NULL, FT_PEEK);
2656 unsigned char c;
2658 if(h){
2659 while(*h){
2660 if(ISRFCEOL(h)){
2661 h += 2;
2662 if(!gf_puts(NEWLINE, pc))
2663 return(FHT_WRTERR);
2665 if(ISRFCEOL(h)) /* all done! */
2666 return(FHT_OK);
2668 else if((unsigned char)(*h) < 0x80 && FILTER_THIS(*h) &&
2669 !(*(h+1) && *h == ESCAPE && match_escapes(h+1))){
2670 c = (unsigned char) *h++;
2671 if(!((*pc)(c >= 0x80 ? '~' : '^')
2672 && (*pc)((c == 0x7f) ? '?' : (c & 0x1f) + '@')))
2673 return(FHT_WRTERR);
2675 else if(!(*pc)(*h++))
2676 return(FHT_WRTERR);
2679 else
2680 return(FHT_FTCHERR);
2682 return(FHT_OK);
2687 /*----------------------------------------------------------------------
2688 Format c-client envelope data suitable for display
2690 Args: s -- mail stream for various header text fetches
2691 n -- raw sequence number in stream of message we're interested in
2692 sect -- which section of message
2693 e -- pointer to msg's envelope
2694 pc -- function to write header text with
2695 which -- which header lines to write
2696 oacs --
2697 flags -- FM_ flags, see pith/mailview.h
2699 Result: 0 if all's well, -1 if write error, 1 if fetch error
2701 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2702 in the local convention.
2704 ----*/
2705 void
2706 format_envelope(MAILSTREAM *s, long int n, char *sect, ENVELOPE *e, gf_io_t pc,
2707 long int which, char *oacs, int flags)
2709 char *q, *p2, buftmp[MAILTMPLEN];
2711 if(!e)
2712 return;
2714 if((which & FE_DATE) && e->date) {
2715 q = "Date: ";
2716 snprintf(buftmp, sizeof(buftmp), "%s",
2717 F_ON(F_DATES_TO_LOCAL,ps_global)
2718 ? convert_date_to_local((char *) e->date) : (char *) e->date);
2719 buftmp[sizeof(buftmp)-1] = '\0';
2720 p2 = (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
2721 SIZEOF_20KBUF, buftmp);
2722 gf_puts(q, pc);
2723 format_env_puts(p2, pc);
2724 gf_puts(NEWLINE, pc);
2727 if((which & FE_FROM) && e->from)
2728 format_addr_string(s, n, sect, "From: ", e->from, flags, oacs, pc);
2730 if((which & FE_REPLYTO) && e->reply_to
2731 && (!e->from || !address_is_same(e->reply_to, e->from)))
2732 format_addr_string(s, n, sect, "Reply-To: ", e->reply_to, flags, oacs, pc);
2734 if((which & FE_TO) && e->to)
2735 format_addr_string(s, n, sect, "To: ", e->to, flags, oacs, pc);
2737 if((which & FE_CC) && e->cc)
2738 format_addr_string(s, n, sect, "Cc: ", e->cc, flags, oacs, pc);
2740 if((which & FE_BCC) && e->bcc)
2741 format_addr_string(s, n, sect, "Bcc: ", e->bcc, flags, oacs, pc);
2743 if((which & FE_RETURNPATH) && e->return_path)
2744 format_addr_string(s, n, sect, "Return-Path: ", e->return_path,
2745 flags, oacs, pc);
2747 if((which & FE_NEWSGROUPS) && e->newsgroups){
2748 int bogus = NIL;
2749 format_newsgroup_string("Newsgroups: ", e->newsgroups, flags, pc);
2750 if (!e->ngpathexists && e->message_id &&
2751 strncmp (e->message_id,"<alpine.",8) &&
2752 strncmp (e->message_id,"<Pine.",6) &&
2753 strncmp (e->message_id,"<MS-C.",6) &&
2754 strncmp (e->message_id,"<MailManager.",13) &&
2755 strncmp (e->message_id,"<EasyMail.",11) &&
2756 strncmp (e->message_id,"<ML-",4)) bogus = T;
2758 if(bogus)
2759 q_status_message(SM_ORDER, 0, 3,
2760 "Unverified Newsgroup header -- Message MAY or MAY NOT have been posted");
2763 if((which & FE_FOLLOWUPTO) && e->followup_to)
2764 format_newsgroup_string("Followup-To: ", e->followup_to, flags, pc);
2766 if((which & FE_SUBJECT) && e->subject && e->subject[0]){
2767 char *freeme = NULL;
2769 q = "Subject: ";
2770 gf_puts(q, pc);
2772 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->subject), SIZEOF_20KBUF-10000);
2774 if(flags & FM_DISPLAY
2775 && (ps_global->display_keywords_in_subject
2776 || ps_global->display_keywordinits_in_subject)){
2778 /* don't bother if no keywords are defined */
2779 if(some_user_flags_defined(s))
2780 p2 = freeme = prepend_keyword_subject(s, n, p2,
2781 ps_global->display_keywords_in_subject ? KW : KWInit,
2782 NULL, ps_global->VAR_KW_BRACES);
2785 format_env_puts(p2, pc);
2787 if(freeme)
2788 fs_give((void **) &freeme);
2790 gf_puts(NEWLINE, pc);
2793 if((which & FE_SENDER) && e->sender
2794 && (!e->from || !address_is_same(e->sender, e->from)))
2795 format_addr_string(s, n, sect, "Sender: ", e->sender, flags, oacs, pc);
2797 if((which & FE_MESSAGEID) && e->message_id){
2798 q = "Message-ID: ";
2799 gf_puts(q, pc);
2800 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->message_id), SIZEOF_20KBUF-10000);
2801 format_env_puts(p2, pc);
2802 gf_puts(NEWLINE, pc);
2805 if((which & FE_INREPLYTO) && e->in_reply_to){
2806 q = "In-Reply-To: ";
2807 gf_puts(q, pc);
2808 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);
2809 format_env_puts(p2, pc);
2810 gf_puts(NEWLINE, pc);
2813 if((which & FE_REFERENCES) && e->references) {
2814 q = "References: ";
2815 gf_puts(q, pc);
2816 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->references), SIZEOF_20KBUF-10000);
2817 format_env_puts(p2, pc);
2818 gf_puts(NEWLINE, pc);
2826 * The argument fieldname is something like "Subject:..." or "Subject".
2827 * Look through the specs in speccolor for a match of the fieldname,
2828 * and return the color that goes with any match, or NULL.
2829 * Caller should free the color.
2831 COLOR_PAIR *
2832 hdr_color(char *fieldname, char *value, SPEC_COLOR_S *speccolor)
2834 SPEC_COLOR_S *hc = NULL;
2835 COLOR_PAIR *color_pair = NULL;
2836 char *colon, *fname;
2837 char fbuf[FBUF_LEN+1];
2838 int gotit;
2839 PATTERN_S *pat;
2841 colon = strindex(fieldname, ':');
2842 if(colon){
2843 strncpy(fbuf, fieldname, MIN(colon-fieldname,FBUF_LEN));
2844 fbuf[MIN(colon-fieldname,FBUF_LEN)] = '\0';
2845 fname = fbuf;
2847 else
2848 fname = fieldname;
2850 if(fname && *fname)
2851 for(hc = speccolor; hc; hc = hc->next)
2852 if(hc->spec && !strucmp(fname, hc->spec)){
2853 if(!hc->val)
2854 break;
2856 gotit = 0;
2857 for(pat = hc->val; !gotit && pat; pat = pat->next)
2858 if(srchstr(value, pat->substring))
2859 gotit++;
2861 if(gotit)
2862 break;
2865 if(hc && hc->fg && hc->fg[0] && hc->bg && hc->bg[0])
2866 color_pair = new_color_pair(hc->fg, hc->bg);
2868 return(color_pair);
2873 * The argument fieldname is something like "Subject:..." or "Subject".
2874 * Look through the specs in hdr_colors for a match of the fieldname,
2875 * and return 1 if that fieldname is in one of the patterns, 0 otherwise.
2878 any_hdr_color(char *fieldname)
2880 SPEC_COLOR_S *hc = NULL;
2881 char *colon, *fname;
2882 char fbuf[FBUF_LEN+1];
2884 colon = strindex(fieldname, ':');
2885 if(colon){
2886 strncpy(fbuf, fieldname, MIN(colon-fieldname,FBUF_LEN));
2887 fbuf[MIN(colon-fieldname,FBUF_LEN)] = '\0';
2888 fname = fbuf;
2890 else
2891 fname = fieldname;
2893 if(fname && *fname)
2894 for(hc = ps_global->hdr_colors; hc; hc = hc->next)
2895 if(hc->spec && !strucmp(fname, hc->spec))
2896 break;
2898 return(hc ? 1 : 0);
2902 /*----------------------------------------------------------------------
2903 Format an address field, wrapping lines nicely at commas
2905 Args: field_name -- The name of the field we're formatting ("TO: ", ...)
2906 addr -- ADDRESS structure to format
2908 Result: A formatted, malloced string is returned.
2909 ----------------------------------------------------------------------*/
2910 void
2911 format_addr_string(MAILSTREAM *stream, long int msgno, char *section, char *field_name,
2912 struct mail_address *addr, int flags, char *oacs, gf_io_t pc)
2914 char *ptmp, *mtmp = NULL;
2915 int trailing = 0, group = 0;
2916 ADDRESS *atmp;
2918 if(!addr)
2919 return;
2922 * quickly run down address list to make sure none are patently bogus.
2923 * If so, just blat raw field out.
2925 for(atmp = addr; stream && atmp; atmp = atmp->next)
2926 if(atmp->host && atmp->host[0] == '.'){
2927 char *field, *fields[2];
2929 fields[1] = NULL;
2930 fields[0] = cpystr(field_name);
2931 if((ptmp = strchr(fields[0], ':')) != NULL)
2932 *ptmp = '\0';
2934 if((field = pine_fetchheader_lines(stream, msgno, section, fields)) != NULL){
2935 char *h, *t;
2937 for(t = h = field; *h ; t++)
2938 if(*t == '\015' && *(t+1) == '\012'){
2939 *t = '\0'; /* tie off line */
2940 format_env_puts(h, pc);
2941 if(*(h = (++t) + 1)) /* set new h and skip CRLF */
2942 gf_puts(NEWLINE, pc); /* more to write */
2943 else
2944 break;
2946 else if(!*t){ /* shouldn't happen much */
2947 if(h != t)
2948 format_env_puts(h, pc);
2950 break;
2953 fs_give((void **)&field);
2956 fs_give((void **)&fields[0]);
2957 gf_puts(NEWLINE, pc);
2958 dprint((2, "Error in \"%s\" field address\n",
2959 field_name ? field_name : "?"));
2960 return;
2963 gf_puts(field_name, pc);
2965 while(addr){
2966 atmp = addr->next; /* remember what's next */
2967 addr->next = NULL;
2968 if(!addr->host && addr->mailbox){
2969 mtmp = addr->mailbox;
2970 addr->mailbox = cpystr((char *)rfc1522_decode_to_utf8(
2971 (unsigned char *)tmp_20k_buf,
2972 SIZEOF_20KBUF, addr->mailbox));
2975 ptmp = addr->personal; /* RFC 1522 personal name? */
2976 addr->personal = iutf8ncpy((char *)tmp_20k_buf, (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+10000), SIZEOF_20KBUF-10000, addr->personal), 10000);
2977 tmp_20k_buf[10000-1] = '\0';
2979 if(!trailing) /* 1st pass, just address */
2980 trailing++;
2981 else{ /* else comma, unless */
2982 if(!((group == 1 && addr->host) /* 1st addr in group, */
2983 || (!addr->host && !addr->mailbox))){ /* or end of group */
2984 gf_puts(",", pc);
2985 #if 0
2986 gf_puts(NEWLINE, pc); /* ONE address/line please */
2987 gf_puts(" ", pc);
2988 #endif
2991 gf_puts(" ", pc);
2994 pine_rfc822_write_address_noquote(addr, pc, &group);
2996 addr->personal = ptmp; /* restore old personal ptr */
2997 if(!addr->host && addr->mailbox){
2998 fs_give((void **)&addr->mailbox);
2999 addr->mailbox = mtmp;
3002 addr->next = atmp;
3003 addr = atmp;
3006 gf_puts(NEWLINE, pc);
3012 const char *rspecials_minus_quote_and_dot = "()<>@,;:\\[]";
3013 /* RFC822 continuation, must start with CRLF */
3014 #define RFC822CONT "\015\012 "
3016 /* Write RFC822 address with some quoting turned off.
3017 * Accepts:
3018 * address to interpret
3020 * (This is a copy of c-client's rfc822_write_address except
3021 * we don't quote double quote and dot in personal names. It writes
3022 * to a gf_io_t instead of to a buffer so that we don't have to worry
3023 * about fixed sized buffer overflowing. It's also special cased to deal
3024 * with only a single address.)
3026 * The idea is that there are some places where we'd just like to display
3027 * the personal name as is before applying confusing quoting. However,
3028 * we do want to be careful not to break things that should be quoted so
3029 * we'll only use this where we are sure. Quoting may look ugly but it
3030 * doesn't usually break anything.
3032 void
3033 pine_rfc822_write_address_noquote(struct mail_address *adr, gf_io_t pc, int *group)
3035 extern const char *rspecials;
3037 if (adr->host) { /* ordinary address? */
3038 if (!(adr->personal || adr->adl)) pine_rfc822_address (adr, pc);
3039 else { /* no, must use phrase <route-addr> form */
3040 if (adr->personal)
3041 pine_rfc822_cat (adr->personal, rspecials_minus_quote_and_dot, pc);
3043 gf_puts(" <", pc); /* write address delimiter */
3044 pine_rfc822_address(adr, pc);
3045 gf_puts (">", pc); /* closing delimiter */
3048 if(*group)
3049 (*group)++;
3051 else if (adr->mailbox) { /* start of group? */
3052 /* yes, write group name */
3053 pine_rfc822_cat (adr->mailbox, rspecials, pc);
3055 gf_puts (": ", pc); /* write group identifier */
3056 *group = 1; /* in a group */
3058 else if (*group) { /* must be end of group (but be paranoid) */
3059 gf_puts (";", pc);
3060 *group = 0; /* no longer in that group */
3065 /* Write RFC822 route-address to string
3066 * Accepts:
3067 * address to interpret
3070 void
3071 pine_rfc822_address(struct mail_address *adr, gf_io_t pc)
3073 extern char *wspecials;
3075 if (adr && adr->host) { /* no-op if no address */
3076 if (adr->adl) { /* have an A-D-L? */
3077 gf_puts (adr->adl, pc);
3078 gf_puts (":", pc);
3080 /* write mailbox name */
3081 pine_rfc822_cat (adr->mailbox, wspecials, pc);
3082 if (*adr->host != '@') { /* unless null host (HIGHLY discouraged!) */
3083 gf_puts ("@", pc); /* host delimiter */
3084 gf_puts (adr->host, pc); /* write host name */
3090 /* Concatenate RFC822 string
3091 * Accepts:
3092 * pointer to string to concatenate
3093 * list of special characters
3096 void
3097 pine_rfc822_cat(char *src, const char *specials, gf_io_t pc)
3099 char *s;
3101 if (strpbrk (src,specials)) { /* any specials present? */
3102 gf_puts ("\"", pc); /* opening quote */
3103 /* truly bizarre characters in there? */
3104 while ((s = strpbrk (src,"\\\"")) != NULL) {
3105 char save[2];
3107 /* turn it into a null-terminated piece */
3108 save[0] = *s;
3109 save[1] = '\0';
3110 *s = '\0';
3111 gf_puts (src, pc); /* yes, output leader */
3112 *s = save[0];
3113 gf_puts ("\\", pc); /* quoting */
3114 gf_puts (save, pc); /* output the bizarre character */
3115 src = ++s; /* continue after the bizarre character */
3117 if (*src) gf_puts (src, pc);/* output non-bizarre string */
3118 gf_puts ("\"", pc); /* closing quote */
3120 else gf_puts (src, pc); /* otherwise it's the easy case */
3124 /*----------------------------------------------------------------------
3125 Format an address field, wrapping lines nicely at commas
3127 Args: field_name -- The name of the field we're formatting ("TO:", Cc:...)
3128 newsgrps -- ADDRESS structure to format
3130 Result: A formatted, malloced string is returned.
3132 The resulting lines formatted are 80 columns wide.
3133 ----------------------------------------------------------------------*/
3134 void
3135 format_newsgroup_string(char *field_name, char *newsgrps, int flags, gf_io_t pc)
3137 char buf[MAILTMPLEN];
3138 int trailing = 0, llen, alen;
3139 char *next_ng;
3141 if(!newsgrps || !*newsgrps)
3142 return;
3144 gf_puts(field_name, pc);
3146 llen = strlen(field_name);
3147 while(*newsgrps){
3148 for(next_ng = newsgrps; *next_ng && *next_ng != ','; next_ng++);
3149 strncpy(buf, newsgrps, MIN(next_ng - newsgrps, sizeof(buf)-1));
3150 buf[MIN(next_ng - newsgrps, sizeof(buf)-1)] = '\0';
3151 newsgrps = next_ng;
3152 if(*newsgrps)
3153 newsgrps++;
3154 alen = strlen(buf);
3155 if(!trailing){ /* first time thru, just address */
3156 llen += alen;
3157 trailing++;
3159 else{ /* else preceding comma */
3160 gf_puts(",", pc);
3161 llen++;
3163 if(alen + llen + 1 > 76){
3164 gf_puts(NEWLINE, pc);
3165 gf_puts(" ", pc);
3166 llen = alen + 5;
3168 else{
3169 gf_puts(" ", pc);
3170 llen += alen + 1;
3174 if(alen && llen > 76){ /* handle long addresses */
3175 register char *q, *p = &buf[alen-1];
3177 while(p > buf){
3178 if(isspace((unsigned char)*p)
3179 && (llen - (alen - (int)(p - buf))) < 76){
3180 for(q = buf; q < p; q++)
3181 (*pc)(*q); /* write character */
3183 gf_puts(NEWLINE, pc);
3184 gf_puts(" ", pc);
3185 gf_puts(p, pc);
3186 break;
3188 else
3189 p--;
3192 if(p == buf) /* no reasonable break point */
3193 gf_puts(buf, pc);
3195 else
3196 gf_puts(buf, pc);
3199 gf_puts(NEWLINE, pc);
3204 /*----------------------------------------------------------------------
3205 Format a text field that's part of some raw (non-envelope) message header
3207 Args: start --
3208 finish --
3209 pc --
3211 Result: Semi-digested text (RFC 1522 decoded, anyway) written with "pc"
3213 ----------------------------------------------------------------------*/
3215 format_raw_hdr_string(char *start, char *finish, gf_io_t pc, char *oacs, int flags)
3217 register char *current;
3218 unsigned char *p, *tmp = NULL, c;
3219 size_t n, len;
3220 char ch;
3221 int rv = FHT_OK;
3223 ch = *finish;
3224 *finish = '\0';
3226 if((n = 4*(finish-start)) > SIZEOF_20KBUF-1){
3227 len = n+1;
3228 p = tmp = (unsigned char *) fs_get(len * sizeof(unsigned char));
3230 else{
3231 len = SIZEOF_20KBUF;
3232 p = (unsigned char *) tmp_20k_buf;
3235 if(islower((unsigned char)(*start)))
3236 *start = toupper((unsigned char)(*start));
3238 current = (char *) rfc1522_decode_to_utf8(p, len, start);
3240 /* output from start to finish */
3241 while(*current && rv == FHT_OK)
3242 if(ISRFCEOL(current)){
3243 if(!gf_puts(NEWLINE, pc))
3244 rv = FHT_WRTERR;
3246 current += 2;
3248 else if((unsigned char)(*current) < 0x80 && FILTER_THIS(*current) &&
3249 !(*(current+1) && *current == ESCAPE && match_escapes(current+1))){
3250 c = (unsigned char) *current++;
3251 if(!((*pc)(c >= 0x80 ? '~' : '^')
3252 && (*pc)((c == 0x7f) ? '?' : (c & 0x1f) + '@')))
3253 rv = FHT_WRTERR;
3255 else if(!(*pc)(*current++))
3256 rv = FHT_WRTERR;
3258 if(tmp)
3259 fs_give((void **) &tmp);
3261 *finish = ch;
3263 return(rv);
3269 /*----------------------------------------------------------------------
3270 Format a text field that's part of some raw (non-envelope) message header
3272 Args: s --
3273 pc --
3275 Result: Output
3277 ----------------------------------------------------------------------*/
3279 format_env_puts(char *s, gf_io_t pc)
3281 if(ps_global->pass_ctrl_chars)
3282 return(gf_puts(s, pc));
3284 for(; *s; s++)
3285 if((unsigned char)(*s) < 0x80 && FILTER_THIS(*s) && !(*(s+1) && *s == ESCAPE && match_escapes(s+1))){
3286 if(!((*pc)((unsigned char) (*s) >= 0x80 ? '~' : '^')
3287 && (*pc)((*s == 0x7f) ? '?' : (*s & 0x1f) + '@')))
3288 return(0);
3290 else if(!(*pc)(*s))
3291 return(0);
3293 return(1);
3297 char *
3298 display_parameters(PARAMETER *params)
3300 int n, longest = 0;
3301 char *d, *printme;
3302 PARAMETER *p;
3303 PARMLIST_S *parmlist;
3305 for(p = params; p; p = p->next) /* ok if we include *'s */
3306 if(p->attribute && (n = strlen(p->attribute)) > longest)
3307 longest = MIN(32, n); /* shouldn't be any bigger than 32 */
3309 d = tmp_20k_buf;
3310 tmp_20k_buf[0] = '\0';
3311 if((parmlist = rfc2231_newparmlist(params)) != NULL){
3312 n = 0; /* n overloaded */
3313 while(rfc2231_list_params(parmlist) && d < tmp_20k_buf + 10000){
3314 if(n++){
3315 snprintf(d, 10000-(d-tmp_20k_buf), "\n");
3316 tmp_20k_buf[10000-1] = '\0';
3317 d += strlen(d);
3320 if(parmlist->value){
3321 if(parmlist->attrib && strucmp(parmlist->attrib, "url") == 0){
3322 snprintf(printme = tmp_20k_buf + 11000, 1000, "%s", parmlist->value);
3323 sqzspaces(printme);
3325 else
3326 printme = strsquish(tmp_20k_buf + 11000, 1000, parmlist->value, 100);
3328 else
3329 printme = "";
3331 snprintf(d, 10000-(d-tmp_20k_buf), "%-*s: %s", longest,
3332 parmlist->attrib ? parmlist->attrib : "", printme);
3334 tmp_20k_buf[10000-1] = '\0';
3335 d += strlen(d);
3338 rfc2231_free_parmlist(&parmlist);
3341 return(tmp_20k_buf);
3345 /*----------------------------------------------------------------------
3346 Fetch the requested header fields from the msgno specified
3348 Args: stream -- mail stream of open folder
3349 msgno -- number of message to get header lines from
3350 fields -- array of pointers to desired fields
3352 Returns: allocated string containing matched header lines,
3353 NULL on error.
3354 ----*/
3355 char *
3356 pine_fetch_header(MAILSTREAM *stream, long int msgno, char *section, char **fields, long int flags)
3358 STRINGLIST *sl;
3359 char *p, *m, *h = NULL, *match = NULL, *free_this, tmp[MAILTMPLEN];
3360 char **pflds = NULL, **pp = NULL, **qq;
3363 * If the user misconfigures it is possible to have one of the fields
3364 * set to the empty string instead of a header name. We want to catch
3365 * that here instead of asking the server the nonsensical question.
3367 for(pp = fields ? &fields[0] : NULL; pp && *pp; pp++)
3368 if(!**pp)
3369 break;
3371 if(pp && *pp){ /* found an empty header field, fix it */
3372 pflds = copy_list_array(fields);
3373 for(pp = pflds; pp && *pp; pp++){
3374 if(!**pp){ /* scoot rest of the lines up */
3375 free_this = *pp;
3376 for(qq = pp; *qq; qq++)
3377 *qq = *(qq+1);
3379 if(free_this)
3380 fs_give((void **) &free_this);
3384 /* no headers to look for, return NULL */
3385 if(pflds && !*pflds && !(flags & FT_NOT)){
3386 free_list_array(&pflds);
3387 return(NULL);
3390 else
3391 pflds = fields;
3393 sl = (pflds && *pflds) ? new_strlst(pflds) : NULL; /* package up fields */
3394 h = mail_fetch_header(stream, msgno, section, sl, NULL, flags | FT_PEEK);
3395 if (sl)
3396 free_strlst(&sl);
3398 if(!h){
3399 if(pflds && pflds != fields)
3400 free_list_array(&pflds);
3402 return(NULL);
3405 while(find_field(&h, tmp, sizeof(tmp))){
3406 for(pp = &pflds[0]; *pp && strucmp(tmp, *pp); pp++)
3409 /* interesting field? */
3410 if((p = (flags & FT_NOT) ? ((*pp) ? NULL : tmp) : *pp) != NULL){
3412 * Hold off allocating space for matching fields until
3413 * we at least find one to copy...
3415 if(!match)
3416 match = m = fs_get(strlen(h) + strlen(p) + 1);
3418 while(*p) /* copy field name */
3419 *m++ = *p++;
3421 while(*h && (*m++ = *h++)) /* header includes colon */
3422 if(*(m-1) == '\n' && (*h == '\r' || !isspace((unsigned char)*h)))
3423 break;
3425 *m = '\0'; /* tie off match string */
3427 else{ /* no match, pass this field */
3428 while(*h && !(*h++ == '\n'
3429 && (*h == '\r' || !isspace((unsigned char)*h))))
3434 if(pflds && pflds != fields)
3435 free_list_array(&pflds);
3437 return(match ? match : cpystr(""));
3442 find_field(char **h, char *tmp, size_t ntmp)
3444 char *otmp = tmp;
3446 if(!h || !*h || !**h || isspace((unsigned char)**h))
3447 return(0);
3449 while(tmp-otmp<ntmp-1 && **h && **h != ':' && !isspace((unsigned char)**h))
3450 *tmp++ = *(*h)++;
3452 *tmp = '\0';
3453 return(1);
3457 static char *_last_embedded_fg_color, *_last_embedded_bg_color;
3461 embed_color(COLOR_PAIR *cp, gf_io_t pc)
3463 if(cp && cp->fg){
3464 if(_last_embedded_fg_color)
3465 fs_give((void **)&_last_embedded_fg_color);
3467 _last_embedded_fg_color = cpystr(cp->fg);
3469 if(!(pc && (*pc)(TAG_EMBED) && (*pc)(TAG_FGCOLOR) &&
3470 gf_puts(color_to_asciirgb(cp->fg), pc)))
3471 return 0;
3474 if(cp && cp->bg){
3475 if(_last_embedded_bg_color)
3476 fs_give((void **)&_last_embedded_bg_color);
3478 _last_embedded_bg_color = cpystr(cp->bg);
3480 if(!(pc && (*pc)(TAG_EMBED) && (*pc)(TAG_BGCOLOR) &&
3481 gf_puts(color_to_asciirgb(cp->bg), pc)))
3482 return 0;
3485 return 1;
3489 COLOR_PAIR *
3490 get_cur_embedded_color(void)
3492 COLOR_PAIR *ret;
3494 if(_last_embedded_fg_color && _last_embedded_bg_color)
3495 ret = new_color_pair(_last_embedded_fg_color, _last_embedded_bg_color);
3496 else
3497 ret = pico_get_cur_color();
3499 return(ret);
3503 void
3504 clear_cur_embedded_color(void)
3506 if(_last_embedded_fg_color)
3507 fs_give((void **)&_last_embedded_fg_color);
3509 if(_last_embedded_bg_color)
3510 fs_give((void **)&_last_embedded_bg_color);
3515 scroll_handle_start_color(char *colorstring, size_t buflen, int *len)
3517 *len = 0;
3519 if(pico_usingcolor()){
3520 char *fg = NULL, *bg = NULL, *s;
3522 if(ps_global->VAR_SLCTBL_FORE_COLOR
3523 && colorcmp(ps_global->VAR_SLCTBL_FORE_COLOR,
3524 ps_global->VAR_NORM_FORE_COLOR))
3525 fg = ps_global->VAR_SLCTBL_FORE_COLOR;
3527 if(ps_global->VAR_SLCTBL_BACK_COLOR
3528 && colorcmp(ps_global->VAR_SLCTBL_BACK_COLOR,
3529 ps_global->VAR_NORM_BACK_COLOR))
3530 bg = ps_global->VAR_SLCTBL_BACK_COLOR;
3532 if(bg || fg){
3533 COLOR_PAIR *tmp;
3536 * The blacks are just known good colors for
3537 * testing whether the other color is good.
3539 if((tmp = new_color_pair(fg ? fg : colorx(COL_BLACK),
3540 bg ? bg : colorx(COL_BLACK))) != NULL){
3541 if(pico_is_good_colorpair(tmp))
3542 for(s = color_embed(fg, bg);
3543 (*len) < buflen && (colorstring[*len] = *s);
3544 s++, (*len)++)
3547 free_color_pair(&tmp);
3551 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
3552 strncpy(colorstring + (*len), url_embed(TAG_BOLDON), MIN(3,buflen-(*len)));
3553 *len += 2;
3557 colorstring[buflen-1] = '\0';
3559 return(*len != 0);
3564 scroll_handle_end_color(char *colorstring, size_t buflen, int *len, int use_hdr_color)
3566 *len = 0;
3567 if(pico_usingcolor()){
3568 char *fg = NULL, *bg = NULL, *s;
3569 char *basefg = NULL, *basebg = NULL;
3571 basefg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_FORE_COLOR
3572 : ps_global->VAR_NORM_FORE_COLOR;
3573 basebg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_BACK_COLOR
3574 : ps_global->VAR_NORM_BACK_COLOR;
3577 * We need to change the fg and bg colors back even if they
3578 * are the same as the Normal Colors so that color_a_quote
3579 * will have a chance to pick up those colors as the ones to
3580 * switch to. We don't do this before the handle above so that
3581 * the quote color will flow into the selectable item when
3582 * the selectable item color is partly the same as the
3583 * normal color. That is, suppose the normal color was black on
3584 * cyan and the selectable color was blue on cyan, only a fg color
3585 * change. We preserve the only-a-fg-color-change in a quote by
3586 * letting the quote background color flow into the selectable text.
3588 if(ps_global->VAR_SLCTBL_FORE_COLOR)
3589 fg = basefg;
3591 if(ps_global->VAR_SLCTBL_BACK_COLOR)
3592 bg = basebg;
3594 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
3595 strncpy(colorstring, url_embed(TAG_BOLDOFF), MIN(3,buflen));
3596 *len = 2;
3599 if(fg || bg)
3600 for(s = color_embed(fg, bg); (*len) < buflen && (colorstring[*len] = *s); s++, (*len)++)
3604 colorstring[buflen-1] = '\0';
3606 return(*len != 0);
3611 * Helper routine that is of limited use.
3612 * We need to tally up the screen width of
3613 * a UTF-8 string as we go through the string.
3614 * We just want the width of the character starting
3615 * at str (and no longer than remaining_octets).
3616 * If we're plopped into the middle of a UTF-8
3617 * character we just want to return width zero.
3620 width_at_this_position(unsigned char *str, unsigned long n)
3622 unsigned char *inputp = str;
3623 unsigned long remaining_octets = n;
3624 UCS ucs;
3625 int width = 0;
3627 ucs = (UCS) utf8_get(&inputp, &remaining_octets);
3628 if(!(ucs & U8G_ERROR || ucs == UBOGON)){
3629 width = wcellwidth(ucs);
3630 /* Writechar will print a '?' */
3631 if(width < 0)
3632 width = 1;
3635 return(width);