* For a calendar entry with method PUBLISH, we show all entries in the calendar.
[alpine.git] / pith / mailview.c
blobc438371c6eea4aa9e414032eb2c8ef5f8b86fd21
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: mailview.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2009 University of Washington
8 * Copyright 2013-2018 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
21 mailview.c
22 Implements message data gathering and formatting
24 ====*/
27 #include "headers.h"
28 #include "../pith/ical.h"
29 #include "../pith/body.h"
30 #include "../pith/mailpart.h"
31 #include "../pith/mailview.h"
32 #include "../pith/conf.h"
33 #include "../pith/msgno.h"
34 #include "../pith/editorial.h"
35 #include "../pith/mimedesc.h"
36 #include "../pith/margin.h"
37 #include "../pith/color.h"
38 #include "../pith/strlst.h"
39 #include "../pith/charset.h"
40 #include "../pith/status.h"
41 #include "../pith/maillist.h"
42 #include "../pith/mailcmd.h"
43 #include "../pith/mailindx.h"
44 #include "../pith/imap.h"
45 #include "../pith/detach.h"
46 #include "../pith/text.h"
47 #include "../pith/url.h"
48 #include "../pith/rfc2231.h"
49 #include "../pith/list.h"
50 #include "../pith/stream.h"
51 #include "../pith/send.h"
52 #include "../pith/filter.h"
53 #include "../pith/string.h"
54 #include "../pith/ablookup.h"
55 #include "../pith/escapes.h"
56 #include "../pith/keyword.h"
57 #include "../pith/smime.h"
60 #define FBUF_LEN (50)
62 #define ISRFCEOL(S) (*(S) == '\015' && *((S)+1) == '\012')
66 * This is a list of header fields that are represented canonically
67 * by the c-client's ENVELOPE structure. The list is used by the
68 * two functions below to decide if a given field is included in this
69 * set.
71 static struct envelope_s {
72 char *name;
73 long val;
74 } envelope_hdrs[] = {
75 {"from", FE_FROM},
76 {"sender", FE_SENDER},
77 {"date", FE_DATE},
78 {"to", FE_TO},
79 {"cc", FE_CC},
80 {"bcc", FE_BCC},
81 {"newsgroups", FE_NEWSGROUPS},
82 {"subject", FE_SUBJECT},
83 {"message-id", FE_MESSAGEID},
84 {"reply-to", FE_REPLYTO},
85 {"followup-to", FE_FOLLOWUPTO},
86 {"in-reply-to", FE_INREPLYTO},
87 /* {"return-path", FE_RETURNPATH}, not usually filled in */
88 {"references", FE_REFERENCES},
89 {NULL, 0}
94 * Hook for optional display of rfc2369 content
96 int (*pith_opt_rfc2369_editorial)(long, HANDLE_S **, int, int, gf_io_t);
102 * Internal prototypes
104 int format_blip_seen(long);
105 int is_an_env_hdr(char *);
106 int is_an_addr_hdr(char *);
107 void format_env_hdr(MAILSTREAM *, long, char *, ENVELOPE *,
108 fmt_env_t, gf_io_t, char *, char *, int);
109 int delineate_this_header(char *, char *, char **, char **);
110 char *url_embed(int);
111 int color_headers(long, char *, LT_INS_S **, void *);
112 int url_hilite_hdr(long, char *, LT_INS_S **, void *);
113 int pad_to_right_edge(long, char *, LT_INS_S **, void *);
114 int url_bogus_imap(char **, char *, char *);
115 int format_raw_header(MAILSTREAM *, long, char *, gf_io_t);
116 void format_envelope(MAILSTREAM *, long, char *, ENVELOPE *,
117 gf_io_t, long, char *, int);
118 int any_hdr_color(char *);
119 void format_addr_string(MAILSTREAM *, long, char *, char *,
120 ADDRESS *, int, char *, gf_io_t);
121 void pine_rfc822_write_address_noquote(ADDRESS *, gf_io_t, int *);
122 void format_newsgroup_string(char *, char *, int, gf_io_t);
123 int format_raw_hdr_string(char *, char *, gf_io_t, char *, int);
124 int format_env_puts(char *, gf_io_t);
125 int find_field(char **, char *, size_t);
126 int embed_color(COLOR_PAIR *, gf_io_t);
127 COLOR_PAIR *get_cur_embedded_color(void);
128 void clear_cur_embedded_color(void);
129 void format_calendar_vevent(VCALENDAR_S *, ATTACH_S *, HANDLE_S **, int, int, gf_io_t, int);
133 /*----------------------------------------------------------------------
134 Format a message message for viewing
136 Args: msgno -- The number of the message to view
137 env -- pointer to the message's envelope
138 body -- pointer to the message's body
139 handlesp -- address of pointer to the message's handles
140 flgs -- possible flags listed in pith/mailview.h with
141 prefix FM_
142 pc -- write to this function
144 Result: Returns true if no problems encountered, else false.
146 First the envelope is formatted; next a list of all attachments is
147 formatted if there is more than one. Then all the body parts are
148 formatted, fetching them as needed. This includes headers of included
149 message. Richtext is also formatted. An entry is made in the text for
150 parts that are not displayed or can't be displayed.
152 ----*/
154 format_message(long int msgno, ENVELOPE *env, struct mail_bodystruct *body,
155 HANDLE_S **handlesp, int flgs, gf_io_t pc)
157 char *decode_err = NULL;
158 HEADER_S h;
159 int width, rv;
161 clear_cur_embedded_color();
163 if(!(flgs & FM_DISPLAY))
164 flgs |= FM_NOINDENT;
166 width = (flgs & FM_DISPLAY) ? ps_global->ttyo->screen_cols : 80;
168 /*---- format and copy envelope ----*/
169 if(!(flgs & FM_NOEDITORIAL)){
170 if(ps_global->full_header == 1)
171 /* TRANSLATORS: User is viewing a message and all the quoted text is
172 being shown. */
173 q_status_message(SM_INFO, 0, 3, _("All quoted text being included"));
174 else if(ps_global->full_header == 2)
175 q_status_message(SM_INFO, 0, 3,
176 /* TRANSLATORS: User is viewing a message and all of
177 the header text is being shown. */
178 _("Full header mode ON. All header text being included"));
181 HD_INIT(&h, ps_global->VAR_VIEW_HEADERS, ps_global->view_all_except, FE_DEFAULT);
182 switch(format_header(ps_global->mail_stream, msgno, NULL,
183 env, &h, NULL, handlesp, flgs, NULL, pc)){
185 case -1 : /* write error */
186 goto write_error;
188 case 1 : /* fetch error */
189 if(!(gf_puts("[ Error fetching header ]", pc)
190 && !gf_puts(NEWLINE, pc)))
191 goto write_error;
193 break;
196 if(!(body == NULL
197 || (ps_global->full_header == 2
198 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global)))){
199 format_attachment_list(msgno, body, handlesp, flgs, width, pc);
200 format_calendar(msgno, body, handlesp, flgs, width, pc);
203 /* write delimiter and body */
204 if(gf_puts(NEWLINE, pc)){
205 if((decode_err = format_body(msgno, body, handlesp, &h, flgs, width, pc)) == NULL)
206 rv = 1;
207 else
208 rv = 0;
210 clear_cur_embedded_color();
211 return(rv);
214 write_error:
216 if(!(flgs & FM_DISPLAY))
217 q_status_message1(SM_ORDER, 3, 4, _("Error writing message: %s"),
218 decode_err ? decode_err : error_description(errno));
219 clear_cur_embedded_color();
220 return(0);
223 void
224 format_calendar_vevent(VCALENDAR_S *vcal, ATTACH_S *a, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc, int cflags)
226 int avail, m1, m2, hwid, i, partwid, padwid;
227 int s1, s2, dwid, minkey;
228 int *margin;
229 char padding[1024];
230 VEVENT_SUMMARY_S *vesy, *vesummary; /* vevent summary */
232 vesummary = vesy = ical_vevent_summary(vcal);
234 if(vesy == NULL) return;
236 if((cflags & FC_SUMMARY) && (cflags & FC_FULL))
237 cflags |= ~FC_FULL;
239 minkey = -1; /* initialize to something negative */
241 for(; vesy != NULL ; vesy = vesy->next){
242 avail = width;
243 margin = (cflags & FC_FULL) ? NULL
244 : (flgs & FM_NOINDENT) ? NULL : format_view_margin();
246 m1 = MAX(MIN(margin ? margin[0] : 0, avail), 0);
247 avail -= m1;
249 m2 = MAX(MIN(margin ? margin[1] : 0, avail), 0);
250 avail -= m2;
252 hwid = MAX(avail, 0);
253 padwid = 0;
255 if(ps_global->atmts[1].description == NULL){
256 avail = width - m1 -2;
258 dwid = MAX(MIN(40, avail), 0);
260 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s%s", m1, m1, "",
261 repeat_char(dwid, '-'));
263 if(!gf_puts(tmp_20k_buf, pc) || !gf_puts(NEWLINE, pc))
264 return;
268 if(cflags & FC_SUMMARY){
269 i = utf8_width(_("Calendar Entry:"));
270 partwid = MIN(i, hwid);
271 if(m1 > 0){
272 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", m1, m1, "");
273 if(!gf_puts(tmp_20k_buf, pc))
274 return;
277 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%-*.*w%*.*s",
278 partwid, partwid, _("Calendar Entry:"),
279 padwid, padwid, "");
281 if(!gf_puts(tmp_20k_buf, pc) || !gf_puts(NEWLINE, pc))
282 return;
284 else
285 partwid = 0;
287 if(m1 > 0){
288 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", m1, m1, "");
289 if(!gf_puts(tmp_20k_buf, pc))
290 return;
293 avail = width - m1 - m2;
295 s1 = MAX(MIN(1, avail), 0);
296 avail -= s1;
298 dwid = MAX(MIN(1, avail), 0);
299 avail -= dwid;
301 s2 = MAX(MIN(1, avail), 0);
302 avail -= s2;
304 if(cflags & FC_SUMMARY)
305 utf8_snprintf(padding, sizeof(padding), "%*.*s%*.*w%*.*s",
306 s1, s1, "", dwid, dwid, "", s2, s2, "");
307 else
308 padding[0] = '\0';
310 if(vesy->cancel){
311 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
312 padding, _("This event was cancelled!"));
313 if((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON)){
314 gf_puts(tmp_20k_buf, pc);
315 gf_puts(NEWLINE, pc);
316 (*pc)(TAG_EMBED);
317 (*pc)(TAG_BOLDOFF);
321 if(vesy->organizer){
322 if(vesy->sender != NULL){
323 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
324 padding, _("Sent-by: "), vesy->sender);
325 gf_puts(tmp_20k_buf, pc);
326 gf_puts(NEWLINE, pc);
329 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
330 padding, _("Organizer: "), vesy->organizer);
331 gf_puts(tmp_20k_buf, pc);
332 gf_puts(NEWLINE, pc);
333 } /* end of if(organizer) */
335 if(vesy->location){
336 ical_remove_escapes(&vesy->location);
337 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
338 padding, _("Location: "), vesy->location);
339 gf_puts(tmp_20k_buf, pc);
340 gf_puts(NEWLINE, pc);
341 } /* end of if location */
343 if(vesy->evstart){
344 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
345 padding, _("Start Date: "), vesy->evstart);
346 gf_puts(tmp_20k_buf, pc);
347 gf_puts(NEWLINE, pc);
348 } /* end of if dtstart */
350 if(vesy->duration){
351 int i;
353 for(i = 0; vesy->duration[i] != NULL; i++){
354 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
355 padding, _("Duration: "), vesy->duration[i]);
356 gf_puts(tmp_20k_buf, pc);
357 gf_puts(NEWLINE, pc);
359 } /* end of DURATION */
360 else if(vesy->evend){
361 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
362 padding, _("End Date: "), vesy->evend);
363 gf_puts(tmp_20k_buf, pc);
364 gf_puts(NEWLINE, pc);
365 } else { /* end of if dtend */
366 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
367 padding, _("No duration nor end time found for this event"));
368 gf_puts(tmp_20k_buf, pc);
369 gf_puts(NEWLINE, pc);
370 } /* end of else for if (duration) and if (dtend) */
372 if(vesy->attendee){
373 #define MAX_DISPLAYED 3
374 int i;
376 for(i = 0; vesy->attendee[i] != NULL; i++){
377 if((cflags & FC_SUMMARY) && i >= MAX_DISPLAYED)
378 break;
380 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
381 padding,
382 _("Attendee: "), vesy->attendee[i]);
383 gf_puts(tmp_20k_buf, pc);
384 gf_puts(NEWLINE, pc);
386 } /* end of ATTENDEES */
389 if(cflags & FC_SUMMARY){
390 COLOR_PAIR *lastc = NULL;
391 char numbuf[50];
392 int thisdescwid;
393 COLOR_PAIR *hdrcolor = NULL;
395 if((flgs & FM_DISPLAY)
396 && !(flgs & FM_NOCOLOR)
397 && pico_usingcolor()
398 && ps_global->VAR_HEADER_GENERAL_FORE_COLOR
399 && ps_global->VAR_HEADER_GENERAL_BACK_COLOR
400 && ps_global->VAR_NORM_FORE_COLOR
401 && ps_global->VAR_NORM_BACK_COLOR
402 && (colorcmp(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
403 ps_global->VAR_NORM_FORE_COLOR)
404 || colorcmp(ps_global->VAR_HEADER_GENERAL_BACK_COLOR,
405 ps_global->VAR_NORM_BACK_COLOR))){
407 if((hdrcolor = new_color_pair(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
408 ps_global->VAR_HEADER_GENERAL_BACK_COLOR)) != NULL){
409 if(!pico_is_good_colorpair(hdrcolor))
410 free_color_pair(&hdrcolor);
414 if(!(!hdrcolor || embed_color(hdrcolor, pc)))
415 return;
417 gf_puts(padding, pc);
418 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "[%s]", _("More Details"));
420 if(handlesp){
421 char buf[16], color[64];
422 int l;
423 HANDLE_S *h;
425 h = new_handle(handlesp);
426 if(minkey < 0) minkey = h->key;
427 h->type = iCal;
428 h->h.ical.attach = a;
429 h->h.ical.depth = h->key - minkey;
431 snprintf(buf, sizeof(buf), "%d", h->key);
432 buf[sizeof(buf)-1] = '\0';
434 if(!(flgs & FM_NOCOLOR)
435 && handle_start_color(color, sizeof(color), &l, 1)){
436 lastc = get_cur_embedded_color();
437 if(!gf_nputs(color, (long) l, pc))
438 return;
440 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)
441 && (!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON))))
442 return;
444 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE)
445 && (*pc)(strlen(buf)) && gf_puts(buf, pc)))
446 return;
447 } else
448 tmp_20k_buf[0] = '\0';
450 if(!format_env_puts(tmp_20k_buf, pc))
451 return;
453 if(handlesp){
454 if(lastc){
455 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
456 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
457 return;
460 if(!embed_color(lastc, pc))
461 return;
463 free_color_pair(&lastc);
465 else if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
466 return;
468 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)))
469 return;
472 if(padwid > 0){
473 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", padwid, padwid, "");
474 if(!gf_puts(tmp_20k_buf, pc))
475 return;
478 if(!gf_puts(NEWLINE, pc))
479 return;
481 avail = width - m1 -2;
483 dwid = MAX(MIN(40, avail), 0);
484 avail -= dwid;
486 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s%s", m1, m1, "",
487 repeat_char(dwid, '-'));
489 gf_puts(tmp_20k_buf, pc);
490 if(vesy->next)
491 gf_puts(NEWLINE, pc);
493 free_vevent_summary(&vesummary);
497 format_calendar(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc)
499 char *rawtext, *caltext;
500 unsigned long callen;
501 VCALENDAR_S *vcal = NULL;
502 ATTACH_S *a;
503 BODY *b;
505 if(flgs & FM_NEW_MESS) {
506 zero_atmts(ps_global->atmts);
507 describe_mime(body, "", 1, 1, 0, flgs);
510 for(a = ps_global->atmts; a->description != NULL; a++){
511 if(MIME_VCALENDAR(a->body->type, a->body->subtype)){
512 b = mail_body (ps_global->mail_stream, msgno, a->number);
513 if(b == NULL){
514 gf_puts(_("Error fetching calendar body part"), pc);
515 gf_puts(NEWLINE, pc);
516 continue;
518 if(b->sparep == NULL){
519 rawtext = mail_fetch_body(ps_global->mail_stream, msgno, a->number, &callen, 0);
520 if(rawtext == NULL || *rawtext == '\0'){
521 gf_puts(_("Error fetching calendar text"), pc);
522 gf_puts(NEWLINE, pc);
523 continue;
525 rawtext[callen] = '\0'; /* chop off cookie */
526 switch(b->encoding){
527 case ENCBASE64:
528 caltext = rfc822_base64(rawtext, strlen(rawtext), &callen);
529 if(caltext == NULL){
530 gf_puts(_("Error in calendar base64 encoding"), pc);
531 gf_puts(NEWLINE, pc);
532 continue;
534 caltext[callen] = '\0';
535 break;
537 case ENCQUOTEDPRINTABLE:
538 caltext = rfc822_qprint ((unsigned char *) rawtext,strlen(rawtext),&callen);
539 if(caltext == NULL){
540 gf_puts(_("Error in calendar quoted printable encoding"), pc);
541 gf_puts(NEWLINE, pc);
542 continue;
544 caltext[callen] = '\0';
545 break;
547 default: caltext = cpystr(rawtext);
549 if(caltext != NULL){
550 vcal = ical_parse_text(caltext);
551 if(vcal != NULL) vcal->encoding = b->encoding;
552 b->sparep = create_body_sparep(iCalType, (void *) vcal);
553 fs_give((void **) &caltext);
556 else if(get_body_sparep_type(b->sparep) == iCalType)
557 vcal = (VCALENDAR_S *) get_body_sparep_data(b->sparep);
558 if(vcal != NULL && vcal->comp != NULL){
559 if(vcal->comp[VEvent] != NULL){
560 format_calendar_vevent(vcal, a, handlesp, flgs, width, pc, FC_SUMMARY);
561 } /* else another type of entry in the calendar */
563 gf_puts(NEWLINE, pc);
566 return 0;
570 char *
571 format_body(long int msgno, BODY *body, HANDLE_S **handlesp, HEADER_S *hp, int flgs, int width, gf_io_t pc)
573 int filt_only_c0 = 0, wrapflags, error_found = 0;
574 int is_in_sig = OUT_SIG_BLOCK;
575 char *charset, *decode_err = NULL, *tmp1, *description;
576 ATTACH_S *a;
577 URL_HILITE_S uh;
578 gf_io_t gc;
580 if(body == NULL
581 || (ps_global->full_header == 2
582 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))) {
584 /*--- Server is not an IMAP2bis, It can't parse MIME
585 so we just show the text here. Hopefully the
586 message isn't a MIME message
587 ---*/
588 void *text2;
590 if((text2 = (void *)pine_mail_fetch_text(ps_global->mail_stream,
591 msgno, NULL, NULL, NIL)) != NULL){
593 if(!gf_puts(NEWLINE, pc)) /* write delimiter */
594 return("Write Error");
596 gf_set_readc(&gc, text2, (unsigned long)strlen(text2), CharStar, 0);
597 gf_filter_init();
600 * We need to translate the message
601 * into UTF-8, but that's trouble in the full header case
602 * because we don't know what to translate from. We'll just
603 * take a guess by looking for the first text part and
604 * using its charset.
606 if(body && body->type == TYPETEXT)
607 charset = parameter_val(body->parameter, "charset");
608 else if(body && body->type == TYPEMULTIPART && body->nested.part
609 && body->nested.part->body.type == TYPETEXT)
610 charset = parameter_val(body->nested.part->body.parameter, "charset");
611 else
612 charset = ps_global->display_charmap;
614 if(strucmp(charset, "us-ascii") && strucmp(charset, "utf-8")){
615 /* transliterate message text to UTF-8 */
616 gf_link_filter(gf_utf8, gf_utf8_opt(charset));
619 /* link in filters, similar to what is done in decode_text() */
620 if(!ps_global->pass_ctrl_chars){
621 gf_link_filter(gf_escape_filter, NULL);
622 filt_only_c0 = 1;
623 gf_link_filter(gf_control_filter,
624 gf_control_filter_opt(&filt_only_c0));
627 gf_link_filter(gf_tag_filter, NULL);
629 if((F_ON(F_VIEW_SEL_URL, ps_global)
630 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
631 || F_ON(F_SCAN_ADDR, ps_global))
632 && handlesp){
633 gf_link_filter(gf_line_test,
634 gf_line_test_opt(url_hilite,
635 gf_url_hilite_opt(&uh,handlesp,0)));
638 if((flgs & FM_DISPLAY)
639 && !(flgs & FM_NOCOLOR)
640 && pico_usingcolor()
641 && ps_global->VAR_SIGNATURE_FORE_COLOR
642 && ps_global->VAR_SIGNATURE_BACK_COLOR){
643 gf_link_filter(gf_line_test, gf_line_test_opt(color_signature, &is_in_sig));
646 if((flgs & FM_DISPLAY)
647 && !(flgs & FM_NOCOLOR)
648 && pico_usingcolor()
649 && ps_global->VAR_QUOTE1_FORE_COLOR
650 && ps_global->VAR_QUOTE1_BACK_COLOR){
651 gf_link_filter(gf_line_test, gf_line_test_opt(color_a_quote, NULL));
654 if(!(flgs & FM_NOWRAP)){
655 wrapflags = (flgs & FM_DISPLAY) ? (GFW_HANDLES|GFW_SOFTHYPHEN) : GFW_NONE;
656 if(flgs & FM_DISPLAY
657 && !(flgs & FM_NOCOLOR)
658 && pico_usingcolor())
659 wrapflags |= GFW_USECOLOR;
660 gf_link_filter(gf_wrap, gf_wrap_filter_opt(width, width,
661 (flgs & FM_NOINDENT)
662 ? NULL : format_view_margin(),
664 wrapflags));
667 gf_link_filter(gf_nvtnl_local, NULL);
668 if((decode_err = gf_pipe(gc, pc)) != NULL){
669 /* TRANSLATORS: There was an error putting together a message for
670 viewing. The arg is the description of the error. */
671 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Formatting error: %s"), decode_err);
672 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
673 if(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
674 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
675 && gf_puts(NEWLINE, pc))
676 decode_err = NULL;
677 else
678 return(decode_err);
682 if(!text2){
683 if(!gf_puts(NEWLINE, pc)
684 || !gf_puts(_(" [ERROR fetching text of message]"), pc)
685 || !gf_puts(NEWLINE, pc)
686 || !gf_puts(NEWLINE, pc))
687 return("Write Error");
690 else{
691 int show_parts = 0;
693 /*======== Now loop through formatting all the parts =======*/
694 for(a = ps_global->atmts; a->description != NULL; a++) {
695 if(MIME_VCALENDAR(a->body->type, a->body->subtype))
696 continue;
698 if(a->body->type == TYPEMULTIPART){
699 #ifdef SMIME
700 if(strucmp(a->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0){
701 if(a->description){
702 if(!(!format_editorial(a->description, width, flgs, handlesp, pc)
703 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
704 return("Write Error");
707 #endif /* SMIME */
708 continue;
711 if(!a->shown) {
712 if(a->suppress_editorial)
713 continue;
715 if(!(flgs & FM_NOEDITORIAL)
716 && (!gf_puts(NEWLINE, pc)
717 || (decode_err = part_desc(a->number, a->body,
718 (flgs & FM_DISPLAY)
719 ? (a->can_display != MCD_NONE)
720 ? 1 : 2
721 : 3, width, flgs, pc))))
722 return("Write Error");
724 continue;
727 switch(a->body->type){
729 case TYPETEXT:
731 * If a message is multipart *and* the first part of it
732 * is text *and that text is empty, there is a good chance that
733 * there was actually something there that c-client was
734 * unable to parse. Here we report the empty message body
735 * and insert the raw RFC822.TEXT (if full-headers are
736 * on).
738 if(body->type == TYPEMULTIPART
739 && a == ps_global->atmts
740 && a->body->size.bytes == 0
741 && F_ON(F_ENABLE_FULL_HDR, ps_global)){
742 char *err = NULL;
744 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
745 "Empty or malformed message%s.",
746 ps_global->full_header == 2
747 ? ". Displaying raw text"
748 : ". Use \"H\" to see raw text");
749 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
751 if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
752 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
753 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
754 return("Write Error");
756 if(ps_global->full_header == 2
757 && (err = detach_raw(ps_global->mail_stream, msgno,
758 a->number, pc, flgs))){
759 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
760 "%s%s [ Formatting error: %s ]%s%s",
761 NEWLINE, NEWLINE, err, NEWLINE, NEWLINE);
762 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
763 if(!gf_puts(tmp_20k_buf, pc))
764 return("Write Error");
767 break;
771 * Don't write our delimiter if this text part is
772 * the first part of a message/rfc822 segment...
774 if(show_parts && a != ps_global->atmts
775 && !((a[-1].body && a[-1].body->type == TYPEMESSAGE)
776 #ifdef SMIME
777 || (a[-1].body->type == TYPEMULTIPART
778 && a[-1].body->subtype
779 && (strucmp(a[-1].body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0)
780 && &a[-1] != ps_global->atmts
781 && a[-2].body && a[-2].body->type == TYPEMESSAGE)
782 #endif /* SMIME */
784 && !(flgs & FM_NOEDITORIAL)){
785 if(MIME_VCALENDAR(a->body->type, a->body->subtype))
786 tmp1 = "Calendar entry";
787 else
788 tmp1 = a->body->description ? a->body->description
789 : "Attached Text";
790 description = iutf8ncpy((char *)(tmp_20k_buf+10000),
791 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+15000), 5000, tmp1), 5000);
793 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Part %s: \"%.1024s\"", a->number,
794 description);
795 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
796 if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
797 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
798 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
799 return("Write Error");
801 /* skip calendar */
802 if(!MIME_VCALENDAR(a->body->type, a->body->subtype))
803 error_found += decode_text(a, msgno, pc, handlesp,
804 (flgs & FM_DISPLAY) ? InLine : QStatus,
805 flgs);
806 break;
808 case TYPEMESSAGE:
809 tmp1 = a->body->description ? a->body->description
810 : (strucmp(a->body->subtype, "delivery-status") == 0)
811 ? "Delivery Status"
812 : "Included Message";
813 description = iutf8ncpy((char *)(tmp_20k_buf+10000),
814 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+15000), 5000, tmp1), 5000);
816 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Part %s: \"%.1024s\"", a->number,
817 description);
818 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
820 if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
821 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
822 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
823 return("Write Error");
825 if(a->body->subtype && strucmp(a->body->subtype, "rfc822") == 0){
826 /* imapenvonly, we may not have all the headers we need */
827 if(a->body->nested.msg->env->imapenvonly)
828 mail_fetch_header(ps_global->mail_stream, msgno,
829 a->number, NULL, NULL, FT_PEEK);
830 switch(format_header(ps_global->mail_stream, msgno, a->number,
831 a->body->nested.msg->env, hp,
832 NULL, handlesp, flgs, NULL, pc)){
833 case -1 : /* write error */
834 return("Write Error");
836 case 1 : /* fetch error */
837 if(!(gf_puts("[ Error fetching header ]", pc)
838 && !gf_puts(NEWLINE, pc)))
839 return("Write Error");
841 break;
844 else if(a->body->subtype && strucmp(a->body->subtype, "external-body") == 0){
845 int *margin, avail, m1, m2;
847 avail = width;
848 margin = (flgs & FM_NOINDENT) ? NULL : format_view_margin();
850 m1 = MAX(MIN(margin ? margin[0] : 0, avail), 0);
851 avail -= m1;
853 m2 = MAX(MIN(margin ? margin[1] : 0, avail), 0);
854 avail -= m2;
856 if(format_editorial("This part is not included and can be fetched as follows:", avail, flgs, handlesp, pc)
857 || !gf_puts(NEWLINE, pc)
858 || format_editorial(display_parameters(a->body->parameter), avail, flgs, handlesp, pc))
859 return("Write Error");
861 else
862 error_found += decode_text(a, msgno, pc, handlesp,
863 (flgs&FM_DISPLAY) ? InLine : QStatus,
864 flgs);
866 if(!gf_puts(NEWLINE, pc))
867 return("Write Error");
869 break;
871 default:
872 if((decode_err = part_desc(a->number, a->body,
873 (flgs & FM_DISPLAY) ? 1 : 3,
874 width, flgs, pc)) != NULL)
875 return("Write Error");
878 show_parts++;
881 if(!(!error_found
882 && (pith_opt_rfc2369_editorial ? (*pith_opt_rfc2369_editorial)(msgno, handlesp, flgs, width, pc) : 1)
883 && format_blip_seen(msgno)))
884 return("Cannot format body.");
887 return(NULL);
892 format_attachment_list(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc)
894 ATTACH_S *a;
896 if(flgs & FM_NEW_MESS) {
897 zero_atmts(ps_global->atmts);
898 describe_mime(body, "", 1, 1, 0, flgs);
901 /*----- First do the list of parts/attachments if needed ----*/
902 if((flgs & FM_DISPLAY)
903 && (ps_global->atmts[1].description
904 || (ps_global->atmts[0].body
905 && ps_global->atmts[0].body->type != TYPETEXT))){
906 char tmp[6*MAX_SCREEN_COLS + 1], *tmpp;
907 int i, n, maxnumwid = 0, maxsizewid = 0, *margin;
908 int avail, m1, m2, hwid, s1, s2, s3, s4, s5, dwid, shownwid;
909 int sizewid, descwid, dashwid, partwid, padwid;
910 COLOR_PAIR *hdrcolor = NULL;
912 if((flgs & FM_DISPLAY)
913 && !(flgs & FM_NOCOLOR)
914 && pico_usingcolor()
915 && ps_global->VAR_HEADER_GENERAL_FORE_COLOR
916 && ps_global->VAR_HEADER_GENERAL_BACK_COLOR
917 && ps_global->VAR_NORM_FORE_COLOR
918 && ps_global->VAR_NORM_BACK_COLOR
919 && (colorcmp(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
920 ps_global->VAR_NORM_FORE_COLOR)
921 || colorcmp(ps_global->VAR_HEADER_GENERAL_BACK_COLOR,
922 ps_global->VAR_NORM_BACK_COLOR))){
924 if((hdrcolor = new_color_pair(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
925 ps_global->VAR_HEADER_GENERAL_BACK_COLOR)) != NULL){
926 if(!pico_is_good_colorpair(hdrcolor))
927 free_color_pair(&hdrcolor);
931 margin = (flgs & FM_NOINDENT) ? NULL : format_view_margin();
934 * Attachment list header
937 avail = width;
939 m1 = MAX(MIN(margin ? margin[0] : 0, avail), 0);
940 avail -= m1;
942 m2 = MAX(MIN(margin ? margin[1] : 0, avail), 0);
943 avail -= m2;
945 hwid = MAX(avail, 0);
947 i = utf8_width(_("Parts/Attachments:"));
948 partwid = MIN(i, hwid);
949 padwid = hdrcolor ? (hwid-partwid) : 0;
951 if(m1 > 0){
952 snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
953 if(!gf_puts(tmp, pc))
954 return(0);
957 utf8_snprintf(tmp, sizeof(tmp),
958 "%-*.*w%*.*s",
959 /* TRANSLATORS: A label */
960 partwid, partwid, _("Parts/Attachments:"),
961 padwid, padwid, "");
963 if(!((!hdrcolor || embed_color(hdrcolor, pc)) && gf_puts(tmp, pc) && gf_puts(NEWLINE, pc)))
964 return(0);
967 /*----- Figure max display widths -----*/
968 for(a = ps_global->atmts; a->description != NULL; a++){
969 if(MIME_VCALENDAR(a->body->type, a->body->subtype))
970 continue;
972 if((n = utf8_width(a->number)) > maxnumwid)
973 maxnumwid = n;
975 if((n = utf8_width(a->size)) > maxsizewid)
976 maxsizewid = n;
980 * ----- adjust max lengths for nice display -----
982 * marg _ D _ number _ Shown _ _ _ size _ _ description marg
986 avail = width - m1 - m2;
988 s1 = MAX(MIN(1, avail), 0);
989 avail -= s1;
991 dwid = MAX(MIN(1, avail), 0);
992 avail -= dwid;
994 s2 = MAX(MIN(1, avail), 0);
995 avail -= s2;
997 maxnumwid = MIN(maxnumwid, width/3);
998 maxnumwid = MAX(MIN(maxnumwid, avail), 0);
999 avail -= maxnumwid;
1001 s3 = MAX(MIN(1, avail), 0);
1002 avail -= s3;
1004 shownwid = MAX(MIN(5, avail), 0);
1005 avail -= shownwid;
1007 s4 = MAX(MIN(3, avail), 0);
1008 avail -= s4;
1010 sizewid = MAX(MIN(maxsizewid, avail), 0);
1011 avail -= sizewid;
1013 s5 = MAX(MIN(2, avail), 0);
1014 avail -= s5;
1016 descwid = MAX(0, avail);
1018 /*----- Format the list of attachments -----*/
1019 for(a = ps_global->atmts; a->description != NULL; a++){
1020 COLOR_PAIR *lastc = NULL;
1021 char numbuf[50];
1022 int thisdescwid, padwid;
1024 if(MIME_VCALENDAR(a->body->type, a->body->subtype))
1025 continue;
1026 #ifdef SMIME
1027 if(a->body->type == TYPEMULTIPART
1028 && (strucmp(a->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0))
1029 continue;
1030 #endif /* SMIME */
1032 i = utf8_width((descwid > 2 && a->description) ? a->description : "");
1033 thisdescwid = MIN(i, descwid);
1034 padwid = hdrcolor ? (descwid-thisdescwid) : 0;
1036 if(m1 > 0){
1037 snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
1038 if(!gf_puts(tmp, pc))
1039 return(0);
1042 utf8_snprintf(tmp, sizeof(tmp),
1043 "%*.*s%*.*w%*.*s%-*.*w%*.*s%*.*w%*.*s%*.*w%*.*s%-*.*w",
1044 s1, s1, "",
1045 dwid, dwid,
1046 msgno_part_deleted(ps_global->mail_stream, msgno, a->number) ? "D" : "",
1047 s2, s2, "",
1048 maxnumwid, maxnumwid,
1049 a->number
1050 ? short_str(a->number, numbuf, sizeof(numbuf), maxnumwid, FrontDots)
1051 : "",
1052 s3, s3, "",
1053 shownwid, shownwid,
1054 a->shown ? "Shown" :
1055 (a->can_display != MCD_NONE && !(a->can_display & MCD_EXT_PROMPT))
1056 ? "OK " : "",
1057 s4, s4, "",
1058 sizewid, sizewid,
1059 a->size ? a->size : "",
1060 s5, s5, "",
1061 thisdescwid, thisdescwid,
1062 (descwid > 2 && a->description) ? a->description : "");
1064 if(!(!hdrcolor || embed_color(hdrcolor, pc)))
1065 return(0);
1067 if(F_ON(F_VIEW_SEL_ATTACH, ps_global) && handlesp){
1068 char buf[16], color[64];
1069 int l;
1070 HANDLE_S *h;
1072 for(tmpp = tmp; *tmpp && *tmpp == ' '; tmpp++)
1073 if(!(*pc)(' '))
1074 return(0);
1076 h = new_handle(handlesp);
1077 h->type = Attach;
1078 h->h.attach = a;
1080 snprintf(buf, sizeof(buf), "%d", h->key);
1081 buf[sizeof(buf)-1] = '\0';
1083 if(!(flgs & FM_NOCOLOR)
1084 && handle_start_color(color, sizeof(color), &l, 1)){
1085 lastc = get_cur_embedded_color();
1086 if(!gf_nputs(color, (long) l, pc))
1087 return(0);
1089 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)
1090 && (!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON))))
1091 return(0);
1093 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE)
1094 && (*pc)(strlen(buf)) && gf_puts(buf, pc)))
1095 return(0);
1097 else
1098 tmpp = tmp;
1100 if(!format_env_puts(tmpp, pc))
1101 return(0);
1103 if(F_ON(F_VIEW_SEL_ATTACH, ps_global) && handlesp){
1104 if(lastc){
1105 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
1106 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
1107 return(0);
1110 if(!embed_color(lastc, pc))
1111 return(0);
1113 free_color_pair(&lastc);
1115 else if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
1116 return(0);
1118 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)))
1119 return(0);
1122 if(padwid > 0){
1123 snprintf(tmp, sizeof(tmp), "%*.*s", padwid, padwid, "");
1124 if(!gf_puts(tmp, pc))
1125 return(0);
1128 if(!gf_puts(NEWLINE, pc))
1129 return(0);
1133 * Dashed line after list
1136 if(hdrcolor){
1137 avail = width - m1 - m2;
1138 hwid = MAX(avail, 0);
1140 dashwid = MAX(MIN(40, hwid-2), 0);
1141 padwid = hwid - dashwid;
1142 if(m1 > 0){
1143 snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
1144 if(!gf_puts(tmp, pc))
1145 return(0);
1148 snprintf(tmp, sizeof(tmp),
1149 "%s%*.*s",
1150 repeat_char(dashwid, '-'),
1151 padwid, padwid, "");
1153 else{
1154 avail = width - m1 -2;
1156 dashwid = MAX(MIN(40, avail), 0);
1157 avail -= dashwid;
1159 snprintf(tmp, sizeof(tmp),
1160 "%*.*s%s",
1161 m1, m1, "",
1162 repeat_char(dashwid, '-'));
1165 if(!((!hdrcolor || embed_color(hdrcolor, pc)) && gf_puts(tmp, pc) && gf_puts(NEWLINE, pc)))
1166 return(0);
1168 if(hdrcolor)
1169 free_color_pair(&hdrcolor);
1172 return(1);
1178 * format_blip_seen - if seen bit (which is usually cleared as a side-effect
1179 * of body part fetches as we're formatting) for the
1180 * given message isn't set (likely because there
1181 * weren't any parts suitable for display), then make
1182 * sure to set it here.
1185 format_blip_seen(long int msgno)
1187 MESSAGECACHE *mc;
1189 if(msgno > 0L && ps_global->mail_stream
1190 && msgno <= ps_global->mail_stream->nmsgs
1191 && (mc = mail_elt(ps_global->mail_stream, msgno))
1192 && !mc->seen
1193 && !ps_global->mail_stream->rdonly)
1194 mail_flag(ps_global->mail_stream, long2string(msgno), "\\SEEN", ST_SET);
1196 return(1);
1201 * is_an_env_hdr - is this name a header in the envelope structure?
1203 * name - the header name to check
1206 is_an_env_hdr(char *name)
1208 register int i;
1210 for(i = 0; envelope_hdrs[i].name; i++)
1211 if(!strucmp(name, envelope_hdrs[i].name))
1212 return(1);
1214 return(0);
1221 * is_an_addr_hdr - is this an address header?
1223 * name - the header name to check
1226 is_an_addr_hdr(char *fieldname)
1228 char fbuf[FBUF_LEN+1];
1229 char *colon, *fname;
1230 static char *addr_headers[] = {
1231 "from",
1232 "reply-to",
1233 "to",
1234 "cc",
1235 "bcc",
1236 "return-path",
1237 "sender",
1238 "x-sender",
1239 "x-x-sender",
1240 "resent-from",
1241 "resent-to",
1242 "resent-cc",
1243 NULL
1246 /* so it is pointing to NULL */
1247 char **p = addr_headers + sizeof(addr_headers)/sizeof(*addr_headers) - 1;
1249 if((colon = strindex(fieldname, ':')) != NULL){
1250 strncpy(fbuf, fieldname, MIN(colon-fieldname,sizeof(fbuf)));
1251 fbuf[MIN(colon-fieldname,sizeof(fbuf)-1)] = '\0';
1252 fname = fbuf;
1254 else
1255 fname = fieldname;
1257 if(fname && *fname){
1258 for(p = addr_headers; *p; p++)
1259 if(!strucmp(fname, *p))
1260 break;
1263 return((*p) ? 1 : 0);
1268 * Format a single field from the envelope
1270 void
1271 format_env_hdr(MAILSTREAM *stream, long int msgno, char *section, ENVELOPE *env,
1272 fmt_env_t fmt_env, gf_io_t pc, char *field, char *oacs, int flags)
1274 register int i;
1276 if(!fmt_env)
1277 fmt_env = format_envelope;
1279 for(i = 0; envelope_hdrs[i].name; i++)
1280 if(!strucmp(field, envelope_hdrs[i].name)){
1281 (*fmt_env)(stream, msgno, section, env, pc, envelope_hdrs[i].val, oacs, flags);
1282 return;
1288 * Look through header string beginning with "begin", for the next
1289 * occurrence of header "field". Set "start" to that. Set "end" to point one
1290 * position past all of the continuation lines that go with "field".
1291 * That is, if "end" is converted to a null
1292 * character then the string "start" will be the next occurence of header
1293 * "field" including all of its continuation lines. Assume we
1294 * have CRLF's as end of lines.
1296 * If "field" is NULL, then we just leave "start" pointing to "begin" and
1297 * make "end" the end of that header.
1299 * Returns 1 if found, 0 if not.
1302 delineate_this_header(char *field, char *begin, char **start, char **end)
1304 char tmpfield[MAILTMPLEN+2]; /* copy of field with colon appended */
1305 char *p;
1306 char *begin_srch;
1308 if(field == NULL){
1309 if(!begin || !*begin || isspace((unsigned char)*begin))
1310 return 0;
1311 else
1312 *start = begin;
1314 else{
1315 strncpy(tmpfield, field, sizeof(tmpfield)-2);
1316 tmpfield[sizeof(tmpfield)-2] = '\0';
1317 strncat(tmpfield, ":", sizeof(tmpfield)-strlen(tmpfield)-1);
1318 tmpfield[sizeof(tmpfield)-1] = '\0';
1321 * We require that start is at the beginning of a line, so
1322 * either it equals begin (which we assume is the beginning of a
1323 * line) or it is preceded by a CRLF.
1325 begin_srch = begin;
1326 *start = srchstr(begin_srch, tmpfield);
1327 while(*start && *start != begin
1328 && !(*start - 2 >= begin && ISRFCEOL(*start - 2))){
1329 begin_srch = *start + 1;
1330 *start = srchstr(begin_srch, tmpfield);
1333 if(!*start)
1334 return 0;
1337 for(p = *start; *p; p++){
1338 if(ISRFCEOL(p)
1339 && (!isspace((unsigned char)*(p+2)) || *(p+2) == '\015')){
1341 * The final 015 in the test above is to test for the end
1342 * of the headers.
1344 *end = p+2;
1345 break;
1349 if(!*p)
1350 *end = p;
1352 return 1;
1358 handle_start_color(char *colorstring, size_t buflen, int *len, int use_hdr_color)
1360 *len = 0;
1362 if(pico_usingcolor()){
1363 char *fg = NULL, *bg = NULL, *s;
1364 char *basefg = NULL, *basebg = NULL;
1366 basefg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_FORE_COLOR
1367 : ps_global->VAR_NORM_FORE_COLOR;
1368 basebg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_BACK_COLOR
1369 : ps_global->VAR_NORM_BACK_COLOR;
1371 if(ps_global->VAR_SLCTBL_FORE_COLOR
1372 && colorcmp(ps_global->VAR_SLCTBL_FORE_COLOR, basefg))
1373 fg = ps_global->VAR_SLCTBL_FORE_COLOR;
1375 if(ps_global->VAR_SLCTBL_BACK_COLOR
1376 && colorcmp(ps_global->VAR_SLCTBL_BACK_COLOR, basebg))
1377 bg = ps_global->VAR_SLCTBL_BACK_COLOR;
1379 if(bg || fg){
1380 COLOR_PAIR *tmp;
1383 * The blacks are just known good colors for
1384 * testing whether the other color is good.
1386 if((tmp = new_color_pair(fg ? fg : colorx(COL_BLACK),
1387 bg ? bg : colorx(COL_BLACK))) != NULL){
1388 if(pico_is_good_colorpair(tmp))
1389 for(s = color_embed(fg, bg);
1390 (*len) < buflen && (colorstring[*len] = *s);
1391 s++, (*len)++)
1394 free_color_pair(&tmp);
1398 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
1399 strncpy(colorstring + (*len), url_embed(TAG_BOLDON), MIN(3,buflen-(*len)));
1400 *len += 2;
1404 colorstring[buflen-1] = '\0';
1406 return(*len != 0);
1411 handle_end_color(char *colorstring, size_t buflen, int *len)
1413 *len = 0;
1414 if(pico_usingcolor()){
1415 char *fg = NULL, *bg = NULL, *s;
1418 * We need to change the fg and bg colors back even if they
1419 * are the same as the Normal Colors so that color_a_quote
1420 * will have a chance to pick up those colors as the ones to
1421 * switch to. We don't do this before the handle above so that
1422 * the quote color will flow into the selectable item when
1423 * the selectable item color is partly the same as the
1424 * normal color. That is, suppose the normal color was black on
1425 * cyan and the selectable color was blue on cyan, only a fg color
1426 * change. We preserve the only-a-fg-color-change in a quote by
1427 * letting the quote background color flow into the selectable text.
1429 if(ps_global->VAR_SLCTBL_FORE_COLOR)
1430 fg = ps_global->VAR_NORM_FORE_COLOR;
1432 if(ps_global->VAR_SLCTBL_BACK_COLOR)
1433 bg = ps_global->VAR_NORM_BACK_COLOR;
1435 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
1436 strncpy(colorstring, url_embed(TAG_BOLDOFF), MIN(3,buflen));
1437 *len = 2;
1440 if(fg || bg)
1441 for(s = color_embed(fg, bg); (*len) < buflen && (colorstring[*len] = *s); s++, (*len)++)
1445 colorstring[buflen-1] = '\0';
1447 return(*len != 0);
1451 char *
1452 url_embed(int embed)
1454 static char buf[3] = {TAG_EMBED};
1455 buf[1] = embed;
1456 buf[2] = '\0';
1457 return(buf);
1462 * Paint the signature.
1465 color_signature(long int linenum, char *line, LT_INS_S **ins, void *is_in_sig)
1467 struct variable *vars = ps_global->vars;
1468 int *in_sig_block;
1469 COLOR_PAIR *col = NULL;
1471 if(is_in_sig == NULL)
1472 return 0;
1474 in_sig_block = (int *) is_in_sig;
1476 if(!strcmp(line, SIGDASHES))
1477 *in_sig_block = START_SIG_BLOCK;
1478 else if(*line == '\0')
1480 * Suggested by Eduardo: allow for a blank line right after
1481 * the sigdashes.
1483 *in_sig_block = (*in_sig_block == START_SIG_BLOCK)
1484 ? IN_SIG_BLOCK : OUT_SIG_BLOCK;
1485 else
1486 *in_sig_block = (*in_sig_block != OUT_SIG_BLOCK)
1487 ? IN_SIG_BLOCK : OUT_SIG_BLOCK;
1489 if(*in_sig_block != OUT_SIG_BLOCK
1490 && VAR_SIGNATURE_FORE_COLOR && VAR_SIGNATURE_BACK_COLOR
1491 && (col = new_color_pair(VAR_SIGNATURE_FORE_COLOR,
1492 VAR_SIGNATURE_BACK_COLOR))){
1493 if(!pico_is_good_colorpair(col))
1494 free_color_pair(&col);
1497 if(col){
1498 char *p, fg[RGBLEN + 1], bg[RGBLEN + 1], rgbbuf[RGBLEN + 1];
1500 ins = gf_line_test_new_ins(ins, line,
1501 color_embed(col->fg, col->bg),
1502 (2 * RGBLEN) + 4);
1504 strncpy(fg, color_to_asciirgb(VAR_NORM_FORE_COLOR), sizeof(fg));
1505 fg[sizeof(fg)-1] = '\0';
1506 strncpy(bg, color_to_asciirgb(VAR_NORM_BACK_COLOR), sizeof(bg));
1507 bg[sizeof(bg)-1] = '\0';
1510 * Loop watching colors, and override with
1511 * signature color whenever the normal foreground and background
1512 * colors are in force.
1515 for(p = line; *p; )
1516 if(*p++ == TAG_EMBED){
1518 switch(*p++){
1519 case TAG_HANDLE :
1520 p += *p + 1; /* skip handle key */
1521 break;
1523 case TAG_FGCOLOR :
1524 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1525 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1526 p += RGBLEN; /* advance past color value */
1528 if(!colorcmp(rgbbuf, VAR_NORM_FORE_COLOR)
1529 && !colorcmp(bg, VAR_NORM_BACK_COLOR))
1530 ins = gf_line_test_new_ins(ins, p,
1531 color_embed(col->fg,NULL),
1532 RGBLEN + 2);
1533 break;
1535 case TAG_BGCOLOR :
1536 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1537 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1538 p += RGBLEN; /* advance past color value */
1540 if(!colorcmp(rgbbuf, VAR_NORM_BACK_COLOR)
1541 && !colorcmp(fg, VAR_NORM_FORE_COLOR))
1542 ins = gf_line_test_new_ins(ins, p,
1543 color_embed(NULL,col->bg),
1544 RGBLEN + 2);
1546 break;
1548 default :
1549 break;
1553 ins = gf_line_test_new_ins(ins, line + strlen(line),
1554 color_embed(VAR_NORM_FORE_COLOR,
1555 VAR_NORM_BACK_COLOR),
1556 (2 * RGBLEN) + 4);
1557 free_color_pair(&col);
1560 return 0;
1565 * Line filter to add color to displayed headers.
1568 color_headers(long int linenum, char *line, LT_INS_S **ins, void *local)
1570 static char field[FBUF_LEN + 1];
1571 char fg[RGBLEN + 1], bg[RGBLEN + 1], rgbbuf[RGBLEN + 1];
1572 char *p, *q, *value, *beg;
1573 COLOR_PAIR *color;
1574 int in_quote = 0, in_comment = 0, did_color = 0;
1575 struct variable *vars = ps_global->vars;
1577 field[FBUF_LEN] = '\0';
1579 if(isspace((unsigned char)*line)) /* continuation line */
1580 value = line;
1581 else{
1582 if(!(value = strindex(line, ':')))
1583 return(0);
1585 memset(field, 0, sizeof(field));
1586 strncpy(field, line, MIN(value-line, sizeof(field)-1));
1589 for(value++; isspace((unsigned char)*value); value++)
1592 strncpy(fg, color_to_asciirgb(VAR_HEADER_GENERAL_FORE_COLOR), sizeof(fg));
1593 fg[sizeof(fg)-1] = '\0';
1594 strncpy(bg, color_to_asciirgb(VAR_HEADER_GENERAL_BACK_COLOR), sizeof(bg));
1595 bg[sizeof(bg)-1] = '\0';
1598 * Split into two cases depending on whether this is a header which
1599 * contains addresses or not. We may color addresses separately.
1601 if(is_an_addr_hdr(field)){
1604 * If none of the patterns are for this header, don't bother parsing
1605 * and checking each address.
1607 if(!any_hdr_color(field))
1608 return(0);
1611 * First check for patternless patterns which color whole line.
1613 if((color = hdr_color(field, NULL, ps_global->hdr_colors)) != NULL){
1614 if(pico_is_good_colorpair(color)){
1615 ins = gf_line_test_new_ins(ins, value,
1616 color_embed(color->fg, color->bg),
1617 (2 * RGBLEN) + 4);
1618 strncpy(fg, color_to_asciirgb(color->fg), sizeof(fg));
1619 fg[sizeof(fg)-1] = '\0';
1620 strncpy(bg, color_to_asciirgb(color->bg), sizeof(bg));
1621 bg[sizeof(bg)-1] = '\0';
1622 did_color++;
1624 else
1625 free_color_pair(&color);
1629 * Then go through checking address by address.
1630 * Keep track of quotes and watch for color changes, and override
1631 * with most recent header color whenever the normal foreground
1632 * and background colors are in force.
1634 beg = p = value;
1635 while(*p){
1636 switch(*p){
1637 case '\\':
1638 /* skip next character */
1639 if(*(p+1) && (in_comment || in_quote))
1640 p += 2;
1641 else
1642 p++;
1644 break;
1646 case '"':
1647 if(!in_comment)
1648 in_quote = 1 - in_quote;
1650 p++;
1651 break;
1653 case '(':
1654 in_comment++;
1655 p++;
1656 break;
1658 case ')':
1659 if(in_comment > 0)
1660 in_comment--;
1662 p++;
1663 break;
1665 case ',':
1666 if(!(in_quote || in_comment)){
1667 /* we reached the end of this address */
1668 *p = '\0';
1669 if(color)
1670 free_color_pair(&color);
1672 if((color = hdr_color(field, beg,
1673 ps_global->hdr_colors)) != NULL){
1674 if(pico_is_good_colorpair(color)){
1675 did_color++;
1676 ins = gf_line_test_new_ins(ins, beg,
1677 color_embed(color->fg,
1678 color->bg),
1679 (2 * RGBLEN) + 4);
1680 *p = ',';
1681 for(q = p; q > beg &&
1682 isspace((unsigned char)*(q-1)); q--)
1685 ins = gf_line_test_new_ins(ins, q,
1686 color_embed(fg, bg),
1687 (2 * RGBLEN) + 4);
1689 else
1690 free_color_pair(&color);
1692 else
1693 *p = ',';
1695 for(p++; isspace((unsigned char)*p); p++)
1698 beg = p;
1700 else
1701 p++;
1703 break;
1705 case TAG_EMBED:
1706 switch(*(++p)){
1707 case TAG_HANDLE:
1708 p++;
1709 p += *p + 1; /* skip handle key */
1710 break;
1712 case TAG_FGCOLOR:
1713 p++;
1714 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1715 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1716 p += RGBLEN; /* advance past color value */
1718 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_FORE_COLOR))
1719 ins = gf_line_test_new_ins(ins, p,
1720 color_embed(color->fg,NULL),
1721 RGBLEN + 2);
1722 break;
1724 case TAG_BGCOLOR:
1725 p++;
1726 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1727 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1728 p += RGBLEN; /* advance past color value */
1730 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_BACK_COLOR))
1731 ins = gf_line_test_new_ins(ins, p,
1732 color_embed(NULL,color->bg),
1733 RGBLEN + 2);
1735 break;
1737 default:
1738 break;
1741 break;
1743 default:
1744 p++;
1745 break;
1749 for(q = beg; *q && isspace((unsigned char)*q); q++)
1752 if(*q && !(in_quote || in_comment)){
1753 /* we reached the end of this address */
1754 if(color)
1755 free_color_pair(&color);
1757 if((color = hdr_color(field, beg, ps_global->hdr_colors)) != NULL){
1758 if(pico_is_good_colorpair(color)){
1759 did_color++;
1760 ins = gf_line_test_new_ins(ins, beg,
1761 color_embed(color->fg,
1762 color->bg),
1763 (2 * RGBLEN) + 4);
1764 for(q = p; q > beg && isspace((unsigned char)*(q-1)); q--)
1767 ins = gf_line_test_new_ins(ins, q,
1768 color_embed(fg, bg),
1769 (2 * RGBLEN) + 4);
1771 else
1772 free_color_pair(&color);
1776 if(color)
1777 free_color_pair(&color);
1779 if(did_color)
1780 ins = gf_line_test_new_ins(ins, line + strlen(line),
1781 color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
1782 VAR_HEADER_GENERAL_BACK_COLOR),
1783 (2 * RGBLEN) + 4);
1785 else{
1787 color = hdr_color(field, value, ps_global->hdr_colors);
1789 if(color){
1790 if(pico_is_good_colorpair(color)){
1791 ins = gf_line_test_new_ins(ins, value,
1792 color_embed(color->fg, color->bg),
1793 (2 * RGBLEN) + 4);
1796 * Loop watching colors, and override with header
1797 * color whenever the normal foreground and background
1798 * colors are in force.
1800 p = value;
1801 while(*p)
1802 if(*p++ == TAG_EMBED){
1804 switch(*p++){
1805 case TAG_HANDLE:
1806 p += *p + 1; /* skip handle key */
1807 break;
1809 case TAG_FGCOLOR:
1810 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1811 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1812 p += RGBLEN; /* advance past color value */
1814 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_FORE_COLOR)
1815 && !colorcmp(bg, VAR_HEADER_GENERAL_BACK_COLOR))
1816 ins = gf_line_test_new_ins(ins, p,
1817 color_embed(color->fg,NULL),
1818 RGBLEN + 2);
1819 break;
1821 case TAG_BGCOLOR:
1822 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1823 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1824 p += RGBLEN; /* advance past color value */
1826 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_BACK_COLOR)
1827 && !colorcmp(fg, VAR_HEADER_GENERAL_FORE_COLOR))
1828 ins = gf_line_test_new_ins(ins, p,
1829 color_embed(NULL,color->bg),
1830 RGBLEN + 2);
1832 break;
1834 default:
1835 break;
1839 ins = gf_line_test_new_ins(ins, line + strlen(line),
1840 color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
1841 VAR_HEADER_GENERAL_BACK_COLOR),
1842 (2 * RGBLEN) + 4);
1845 free_color_pair(&color);
1849 return(0);
1854 url_hilite(long int linenum, char *line, LT_INS_S **ins, void *local)
1856 register char *lp, *up = NULL, *urlp = NULL,
1857 *weburlp = NULL, *mailurlp = NULL;
1858 int n, n1, n2, n3, l;
1859 char buf[256], color[256];
1860 HANDLE_S *h;
1861 URL_HILITE_S *uh;
1863 for(lp = line; ; lp = up + n){
1864 /* scan for all of them so we can choose the first */
1865 if(F_ON(F_VIEW_SEL_URL,ps_global))
1866 urlp = rfc1738_scan(lp, &n1);
1867 if(F_ON(F_VIEW_SEL_URL_HOST,ps_global))
1868 weburlp = web_host_scan(lp, &n2);
1869 if(F_ON(F_SCAN_ADDR,ps_global))
1870 mailurlp = mail_addr_scan(lp, &n3);
1872 if(urlp || weburlp || mailurlp){
1873 up = urlp ? urlp :
1874 weburlp ? weburlp : mailurlp;
1875 if(up == urlp && weburlp && weburlp < up)
1876 up = weburlp;
1877 if(mailurlp && mailurlp < up)
1878 up = mailurlp;
1880 if(up == urlp){
1881 n = n1;
1882 weburlp = mailurlp = NULL;
1884 else if(up == weburlp){
1885 n = n2;
1886 mailurlp = NULL;
1888 else{
1889 n = n3;
1890 weburlp = NULL;
1893 else
1894 break;
1896 uh = (URL_HILITE_S *) local;
1898 h = new_handle(uh->handlesp);
1899 h->type = URL;
1900 h->h.url.path = (char *) fs_get((n + 10) * sizeof(char));
1901 snprintf(h->h.url.path, n+10, "%s%.*s",
1902 weburlp ? "http://" : (mailurlp ? "mailto:" : ""), n, up);
1903 h->h.url.path[n+10-1] = '\0';
1905 if(handle_start_color(color, sizeof(color), &l, uh->hdr_color))
1906 ins = gf_line_test_new_ins(ins, up, color, l);
1907 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global))
1908 ins = gf_line_test_new_ins(ins, up, url_embed(TAG_BOLDON), 2);
1910 buf[0] = TAG_EMBED;
1911 buf[1] = TAG_HANDLE;
1912 snprintf(&buf[3], sizeof(buf)-3, "%d", h->key);
1913 buf[sizeof(buf)-1] = '\0';
1914 buf[2] = strlen(&buf[3]);
1915 ins = gf_line_test_new_ins(ins, up, buf, (int) buf[2] + 3);
1917 /* in case it was the current selection */
1918 ins = gf_line_test_new_ins(ins, up + n, url_embed(TAG_INVOFF), 2);
1920 if(scroll_handle_end_color(color, sizeof(color), &l, uh->hdr_color))
1921 ins = gf_line_test_new_ins(ins, up + n, color, l);
1922 else
1923 ins = gf_line_test_new_ins(ins, up + n, url_embed(TAG_BOLDOFF), 2);
1925 urlp = weburlp = mailurlp = NULL;
1928 return(0);
1933 url_hilite_hdr(long int linenum, char *line, LT_INS_S **ins, void *local)
1935 static int check_for_urls = 0;
1936 register char *lp;
1938 if(isspace((unsigned char)*line)) /* continuation, check or not
1939 depending on last line */
1940 lp = line;
1941 else{
1942 check_for_urls = 0;
1943 if((lp = strchr(line, ':')) != NULL){ /* there ought to always be a colon */
1944 FieldType ft;
1946 *lp = '\0';
1948 if(((ft = pine_header_standard(line)) == FreeText
1949 || ft == Subject
1950 || ft == TypeUnknown)
1951 && strucmp(line, "message-id")
1952 && strucmp(line, "newsgroups")
1953 && strucmp(line, "references")
1954 && strucmp(line, "in-reply-to")
1955 && strucmp(line, "received")
1956 && strucmp(line, "date")){
1957 check_for_urls = 1;
1960 *lp = ':';
1964 if(check_for_urls)
1965 (void) url_hilite(linenum, lp + 1, ins, local);
1967 return(0);
1972 pad_to_right_edge(long int linenum, char *line, LT_INS_S **ins, void *local)
1974 char *p;
1975 int wid = 0;
1976 int total_wid;
1977 struct variable *vars = ps_global->vars;
1979 if(!line[0])
1980 return 0;
1982 total_wid = *((int *) local);
1984 /* calculate width of line */
1985 p = line;
1986 while(*p){
1988 switch(*p){
1989 case TAG_EMBED:
1990 p++;
1991 switch(*p){
1992 case TAG_HANDLE:
1993 p++;
1994 p += *p + 1; /* skip handle key */
1995 break;
1997 case TAG_FGCOLOR :
1998 case TAG_BGCOLOR :
1999 p += (RGBLEN + 1); /* 1 for TAG, RGBLEN for color */
2000 break;
2002 case TAG_INVON:
2003 case TAG_INVOFF:
2004 case TAG_BOLDON:
2005 case TAG_BOLDOFF:
2006 case TAG_ULINEON:
2007 case TAG_ULINEOFF:
2008 p++;
2009 break;
2011 default: /* literal embed char */
2012 break;
2015 break;
2017 case TAB:
2018 p++;
2019 while(((++wid) & 0x07) != 0) /* add tab's spaces */
2022 break;
2024 default:
2025 wid += width_at_this_position((unsigned char *) p, strlen(p));
2026 p++;
2027 break;
2031 if(total_wid > wid){
2032 ins = gf_line_test_new_ins(ins, line + strlen(line),
2033 color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
2034 VAR_HEADER_GENERAL_BACK_COLOR),
2035 (2 * RGBLEN) + 4);
2036 ins = gf_line_test_new_ins(ins, line+strlen(line),
2037 repeat_char(total_wid-wid, ' '), total_wid-wid);
2038 ins = gf_line_test_new_ins(ins, line + strlen(line),
2039 color_embed(VAR_NORM_FORE_COLOR,
2040 VAR_NORM_BACK_COLOR),
2041 (2 * RGBLEN) + 4);
2044 return(0);
2049 #define UES_LEN 12
2050 #define UES_MAX 32
2052 url_external_specific_handler(char *url, int len)
2054 static char list[UES_LEN * UES_MAX];
2056 if(url){
2057 char *p;
2058 int i;
2060 for(i = 0; i < UES_MAX && *(p = &list[i * UES_LEN]); i++)
2061 if(strlen(p) == len && !struncmp(p, url, len))
2062 return(1);
2064 else{ /* initialize! */
2065 char **l, *test, *cmd, *p, *p2;
2066 int i = 0, n;
2068 memset(list, 0, sizeof(list));
2069 for(l = ps_global->VAR_BROWSER ; l && *l; l++){
2070 get_pair(*l, &test, &cmd, 1, 1);
2072 if((p = srchstr(test, "_scheme(")) && (p2 = strstr(p+8, ")_"))){
2073 *p2 = '\0';
2075 for(p += 8; *p && i < UES_MAX; p += n)
2076 if((p2 = strchr(p, ',')) != NULL){
2077 if((n = p2 - p) < UES_LEN){
2078 strncpy(&list[i * UES_LEN], p, MIN(n, sizeof(list)-(i * UES_LEN)));
2079 i++;
2081 else
2082 dprint((1,
2083 "* * * HANLDER TOO LONG: %.*s\n", n,
2084 p ? p : "?"));
2086 n++;
2088 else{
2089 if(strlen(p) <= UES_LEN){
2090 strncpy(&list[i * UES_LEN], p, sizeof(list)-(i * UES_LEN));
2091 i++;
2094 break;
2098 if(test)
2099 fs_give((void **) &test);
2101 if(cmd)
2102 fs_give((void **) &cmd);
2106 return(0);
2111 url_imap_folder(char *true_url, char **folder, imapuid_t *uid_val,
2112 imapuid_t *uid, char **search, int silent)
2114 char *url, *scheme, *p, *cmd, *server = NULL,
2115 *user = NULL, *auth = NULL, *mailbox = NULL,
2116 *section = NULL;
2117 size_t l;
2118 int rv = URL_IMAP_ERROR;
2121 * Since we're planting nulls, operate on a temporary copy...
2123 scheme = silent ? NULL : "IMAP";
2124 url = cpystr(true_url + 7);
2126 /* Try to pick apart the "iserver" portion */
2127 if((cmd = strchr(url, '/')) != NULL){ /* iserver "/" [mailbox] ? */
2128 *cmd++ = '\0';
2130 else{
2131 dprint((2, "-- URL IMAP FOLDER: missing: %s\n",
2132 url ? url : "?"));
2133 cmd = &url[strlen(url)-1]; /* assume only iserver */
2136 if((p = strchr(url, '@')) != NULL){ /* user | auth | pass? */
2137 *p++ = '\0';
2138 server = rfc1738_str(p);
2140 /* only ";auth=*" supported (and also ";auth=anonymous") */
2141 if((p = srchstr(url, ";auth=")) != NULL){
2142 *p = '\0';
2143 auth = rfc1738_str(p + 6);
2146 if(*url)
2147 user = rfc1738_str(url);
2149 else
2150 server = rfc1738_str(url);
2152 if(!*server)
2153 return(url_bogus_imap(&url, scheme, "No server specified"));
2156 * "iserver" in hand, pick apart the "icommand"...
2158 p = NULL;
2159 if(!*cmd || (p = srchstr(cmd, ";type="))){
2160 char *criteria;
2163 * No "icommand" (all top-level folders) or "imailboxlist"...
2165 if(p){
2166 *p = '\0'; /* tie off criteria */
2167 criteria = rfc1738_str(cmd); /* get "enc_list_mailbox" */
2168 if(!strucmp(p = rfc1738_str(p+6), "lsub"))
2169 rv |= URL_IMAP_IMBXLSTLSUB;
2170 else if(strucmp(p, "list"))
2171 return(url_bogus_imap(&url, scheme,
2172 "Invalid list type specified"));
2174 else{
2175 rv |= URL_IMAP_ISERVERONLY;
2176 criteria = "";
2179 /* build folder list from specified server/criteria/list-method */
2180 l = strlen(server) + strlen(criteria) + 10 + (user ? (strlen(user)+2) : 9);
2181 *folder = (char *) fs_get((l+1) * sizeof(char));
2182 snprintf(*folder, l+1, "{%s/%s%s%s}%s%s%s", server,
2183 user ? "user=\"" : "Anonymous",
2184 user ? user : "",
2185 user ? "\"" : "",
2186 *criteria ? "[" : "", criteria, *criteria ? "[" : "");
2187 (*folder)[l] = '\0';
2188 rv |= URL_IMAP_IMAILBOXLIST;
2190 else{
2191 if((p = srchstr(cmd, "/;uid=")) != NULL){ /* "imessagepart" */
2192 *p = '\0'; /* tie off mailbox [uidvalidity] */
2193 if((section = srchstr(p += 6, "/;section=")) != NULL){
2194 *section = '\0'; /* tie off UID */
2195 section = rfc1738_str(section + 10);
2196 /* BUG: verify valid section spec ala rfc 2060 */
2197 dprint((2,
2198 "-- URL IMAP FOLDER: section not used: %s\n",
2199 section ? section : "?"));
2202 if(!(*uid = rfc1738_num(&p)) || *p) /* decode UID */
2203 return(url_bogus_imap(&url, scheme, "Invalid data in UID"));
2205 /* optional "uidvalidity"? */
2206 if((p = srchstr(cmd, ";uidvalidity=")) != NULL){
2207 *p = '\0';
2208 p += 13;
2209 if(!(*uid_val = rfc1738_num(&p)) || *p)
2210 return(url_bogus_imap(&url, scheme,
2211 "Invalid UIDVALIDITY"));
2214 mailbox = rfc1738_str(cmd);
2215 rv = URL_IMAP_IMESSAGEPART;
2217 else{ /* "imessagelist" */
2218 /* optional "uidvalidity"? */
2219 if((p = srchstr(cmd, ";uidvalidity=")) != NULL){
2220 *p = '\0';
2221 p += 13;
2222 if(!(*uid_val = rfc1738_num(&p)) || *p)
2223 return(url_bogus_imap(&url, scheme,
2224 "Invalid UIDVALIDITY"));
2227 /* optional "enc_search"? */
2228 if((p = strchr(cmd, '?')) != NULL){
2229 *p = '\0';
2230 if(search)
2231 *search = cpystr(rfc1738_str(p + 1));
2232 /* BUG: verify valid search spec ala rfc 2060 */
2235 mailbox = rfc1738_str(cmd);
2236 rv = URL_IMAP_IMESSAGELIST;
2239 if(auth && *auth != '*' && strucmp(auth, "anonymous"))
2240 q_status_message(SM_ORDER, 3, 3,
2241 "Unsupported authentication method. Using standard login.");
2244 * At this point our structure should contain the
2245 * digested url. Now put it together for c-client...
2247 l = strlen(server) + 8 + (mailbox ? strlen(mailbox) : 0)
2248 + (user ? (strlen(user)+2) : 9);
2249 *folder = (char *) fs_get((l+1) * sizeof(char));
2250 snprintf(*folder, l+1, "{%s%s%s%s%s}%s", server,
2251 (user || !(auth && strucmp(auth, "anonymous"))) ? "/" : "",
2252 user ? "user=\"" : ((auth && strucmp(auth, "anonymous")) ? "" : "Anonymous"),
2253 user ? user : "",
2254 user ? "\"" : "",
2255 mailbox);
2256 (*folder)[l] = '\0';
2259 fs_give((void **) &url);
2260 return(rv);
2265 url_bogus_imap(char **freeme, char *url, char *problem)
2267 fs_give((void **) freeme);
2268 (void) url_bogus(url, problem);
2269 return(URL_IMAP_ERROR);
2274 * url_bogus - report url syntax errors and such
2277 url_bogus(char *url, char *reason)
2279 dprint((2, "-- bogus url \"%s\": %s\n",
2280 url ? url : "<NULL URL>", reason ? reason : "?"));
2281 if(url)
2282 q_status_message3(SM_ORDER|SM_DING, 2, 3,
2283 "Malformed \"%.*s\" URL: %.200s",
2284 (void *) (strchr(url, ':') - url), url, reason);
2286 return(0);
2291 /*----------------------------------------------------------------------
2292 Format header text suitable for display
2294 Args: stream -- mail stream for various header text fetches
2295 msgno -- sequence number in stream of message we're interested in
2296 section -- which section of message
2297 env -- pointer to msg's envelope
2298 hdrs -- struct containing what's to get formatted
2299 prefix -- prefix to append to each output line
2300 handlesp -- address of pointer to the message's handles
2301 flags -- FM_ flags, see pith/mailview.h
2302 final_pc -- function to write header text with
2304 Result: 0 if all's well, -1 if write error, 1 if fetch error
2306 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2307 in the local convention.
2309 ----*/
2310 #define FHT_OK 0
2311 #define FHT_WRTERR -1
2312 #define FHT_FTCHERR 1
2314 format_header(MAILSTREAM *stream, long int msgno, char *section, ENVELOPE *env,
2315 HEADER_S *hdrs, char *prefix, HANDLE_S **handlesp, int flags,
2316 fmt_env_t fmt_env, gf_io_t final_pc)
2318 int rv = FHT_OK;
2319 int nfields, i;
2320 char *h = NULL, **fields = NULL, **v, *q, *start,
2321 *finish, *current;
2322 STORE_S *tmp_store;
2323 URL_HILITE_S uh;
2324 gf_io_t tmp_pc, tmp_gc;
2325 struct variable *vars = ps_global->vars;
2327 if((tmp_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL)
2328 gf_set_so_writec(&tmp_pc, tmp_store);
2329 else
2330 return(FHT_WRTERR);
2332 if(!fmt_env)
2333 fmt_env = format_envelope;
2335 if(ps_global->full_header == 2){
2336 rv = format_raw_header(stream, msgno, section, tmp_pc);
2338 else{
2340 * First, calculate how big a fields array we need.
2343 /* Custom header viewing list specified */
2344 if(hdrs->type == HD_LIST){
2345 /* view all these headers */
2346 if(!hdrs->except){
2347 for(nfields = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
2348 if(!is_an_env_hdr(q))
2349 nfields++;
2351 /* view all except these headers */
2352 else{
2353 for(nfields = 0, v = hdrs->h.l; *v != NULL; v++)
2354 nfields++;
2356 if(nfields > 1)
2357 nfields--; /* subtract one for ALL_EXCEPT field */
2360 else
2361 nfields = 6; /* default view */
2363 /* allocate pointer space */
2364 if(nfields){
2365 fields = (char **)fs_get((size_t)(nfields+1) * sizeof(char *));
2366 memset(fields, 0, (size_t)(nfields+1) * sizeof(char *));
2369 if(hdrs->type == HD_LIST){
2370 /* view all these headers */
2371 if(!hdrs->except){
2372 /* put the non-envelope headers in fields */
2373 if(nfields)
2374 for(i = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
2375 if(!is_an_env_hdr(q))
2376 fields[i++] = q;
2378 /* view all except these headers */
2379 else{
2380 /* put the list of headers not to view in fields */
2381 if(nfields)
2382 for(i = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
2383 if(strucmp(ALL_EXCEPT, q))
2384 fields[i++] = q;
2387 v = hdrs->h.l;
2389 else{
2390 if(nfields){
2391 fields[i = 0] = "Resent-Date";
2392 fields[++i] = "Resent-From";
2393 fields[++i] = "Resent-To";
2394 fields[++i] = "Resent-cc";
2395 fields[++i] = "Resent-Subject";
2398 v = fields;
2401 /* custom view with exception list */
2402 if(hdrs->type == HD_LIST && hdrs->except){
2404 * Go through each header in h and print it.
2405 * First we check to see if it is an envelope header so we
2406 * can print our envelope version of it instead of the raw version.
2409 /* fetch all the other headers */
2410 if(nfields)
2411 h = pine_fetchheader_lines_not(stream, msgno, section, fields);
2413 for(current = h;
2414 h && delineate_this_header(NULL, current, &start, &finish);
2415 current = finish){
2416 char tmp[MAILTMPLEN+1];
2417 char *colon_loc;
2419 colon_loc = strindex(start, ':');
2420 if(colon_loc && colon_loc < finish){
2421 strncpy(tmp, start, MIN(colon_loc-start, sizeof(tmp)-1));
2422 tmp[MIN(colon_loc-start, sizeof(tmp)-1)] = '\0';
2424 else
2425 colon_loc = NULL;
2427 if(colon_loc && is_an_env_hdr(tmp)){
2428 char *dummystart, *dummyfinish;
2431 * Pretty format for env hdrs.
2432 * If the same header appears more than once, only
2433 * print the last to avoid duplicates.
2434 * They should have been combined in the env when parsed.
2436 if(!delineate_this_header(tmp, current+1, &dummystart,
2437 &dummyfinish))
2438 format_env_hdr(stream, msgno, section, env,
2439 fmt_env, tmp_pc, tmp, hdrs->charset, flags);
2441 else{
2442 if((rv = format_raw_hdr_string(start, finish, tmp_pc,
2443 hdrs->charset, flags)) != 0)
2444 goto write_error;
2445 else
2446 start = finish;
2450 /* custom view or default */
2451 else{
2452 /* fetch the non-envelope headers */
2453 if(nfields)
2454 h = pine_fetchheader_lines(stream, msgno, section, fields);
2456 /* default envelope for default view */
2457 if(hdrs->type == HD_BFIELD)
2458 (*fmt_env)(stream, msgno, section, env, tmp_pc, hdrs->h.b, hdrs->charset, flags);
2460 /* go through each header in list, v initialized above */
2461 for(; (q = *v) != NULL; v++){
2462 if(is_an_env_hdr(q)){
2463 /* pretty format for env hdrs */
2464 format_env_hdr(stream, msgno, section, env,
2465 fmt_env, tmp_pc, q, hdrs->charset, flags);
2467 else{
2469 * Go through h finding all occurences of this header
2470 * and all continuation lines, and output.
2472 for(current = h;
2473 h && delineate_this_header(q,current,&start,&finish);
2474 current = finish){
2475 if((rv = format_raw_hdr_string(start, finish, tmp_pc,
2476 hdrs->charset, flags)) != 0)
2477 goto write_error;
2478 else
2479 start = finish;
2487 write_error:
2489 gf_clear_so_writec(tmp_store);
2491 if(!rv){ /* valid data? Do wrapping and filtering... */
2492 int column;
2493 char *errstr, *display_filter = NULL, trigger[MAILTMPLEN];
2494 STORE_S *df_store = NULL;
2496 so_seek(tmp_store, 0L, 0);
2498 column = (flags & FM_DISPLAY) ? ps_global->ttyo->screen_cols : 80;
2501 * Test for and act on any display filter
2502 * This barely makes sense. The display filter is going
2503 * to be getting UTF-8'ized headers here. In pre-alpine
2504 * pine the display filter was being fed already decoded
2505 * headers in whatever character set they were in.
2506 * The good news is that that didn't make much
2507 * sense either, so this shouldn't break anything.
2508 * It seems unlikely that anybody is doing anything useful
2509 * with the header part of display filters.
2511 if(ps_global->tools.display_filter
2512 && ps_global->tools.display_filter_trigger
2513 && (display_filter = (*ps_global->tools.display_filter_trigger)(NULL, trigger, sizeof(trigger)))){
2514 if((df_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
2516 gf_set_so_writec(&tmp_pc, df_store);
2517 gf_set_so_readc(&tmp_gc, df_store);
2518 if((errstr = (*ps_global->tools.display_filter)(display_filter, tmp_store, tmp_pc, NULL)) != NULL){
2519 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2520 _("Formatting error: %s"), errstr);
2521 rv = FHT_WRTERR;
2523 else
2524 so_seek(df_store, 0L, 0);
2526 gf_clear_so_writec(df_store);
2528 else{
2529 q_status_message(SM_ORDER | SM_DING, 3, 3,
2530 "No space for filtered text.");
2531 rv = FHT_WRTERR;
2534 else{
2535 so_seek(tmp_store, 0L, 0);
2536 gf_set_so_readc(&tmp_gc, tmp_store);
2539 if(!rv){
2540 int *margin, wrapflags = GFW_ONCOMMA;
2542 gf_filter_init();
2543 gf_link_filter(gf_local_nvtnl, NULL);
2545 if((F_ON(F_VIEW_SEL_URL, ps_global)
2546 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
2547 || F_ON(F_SCAN_ADDR, ps_global))
2548 && handlesp){
2549 gf_link_filter(gf_line_test,
2550 gf_line_test_opt(url_hilite_hdr,
2551 gf_url_hilite_opt(&uh,handlesp,1)));
2552 wrapflags |= GFW_HANDLES;
2555 if((flags & FM_DISPLAY)
2556 && !(flags & FM_NOCOLOR)
2557 && pico_usingcolor()
2558 && ((VAR_NORM_FORE_COLOR
2559 && VAR_HEADER_GENERAL_FORE_COLOR
2560 && colorcmp(VAR_NORM_FORE_COLOR, VAR_HEADER_GENERAL_FORE_COLOR))
2562 (VAR_NORM_BACK_COLOR
2563 && VAR_HEADER_GENERAL_BACK_COLOR
2564 && colorcmp(VAR_NORM_BACK_COLOR, VAR_HEADER_GENERAL_BACK_COLOR))))
2565 wrapflags |= GFW_HDRCOLOR;
2567 if((flags & FM_DISPLAY)
2568 && !(flags & FM_NOCOLOR)
2569 && pico_usingcolor()
2570 && ps_global->hdr_colors){
2571 gf_link_filter(gf_line_test,
2572 gf_line_test_opt(color_headers, NULL));
2573 wrapflags |= (GFW_HANDLES | GFW_HDRCOLOR);
2576 if(prefix && *prefix)
2577 column = MAX(column-strlen(prefix), 50);
2579 margin = format_view_margin();
2581 if(!(flags & FM_NOWRAP))
2582 gf_link_filter(gf_wrap,
2583 gf_wrap_filter_opt(column, column,
2584 (flags & FM_NOINDENT) ? NULL : margin,
2585 4, wrapflags));
2587 if(prefix && *prefix)
2588 gf_link_filter(gf_prefix, gf_prefix_opt(prefix));
2590 if((flags & FM_DISPLAY)
2591 && !(flags & FM_NOCOLOR)
2592 && pico_usingcolor()
2593 && ((VAR_NORM_FORE_COLOR
2594 && VAR_HEADER_GENERAL_FORE_COLOR
2595 && colorcmp(VAR_NORM_FORE_COLOR, VAR_HEADER_GENERAL_FORE_COLOR))
2597 (VAR_NORM_BACK_COLOR
2598 && VAR_HEADER_GENERAL_BACK_COLOR
2599 && colorcmp(VAR_NORM_BACK_COLOR, VAR_HEADER_GENERAL_BACK_COLOR)))){
2600 int right_margin;
2601 int total_wid;
2603 right_margin = margin ? margin[1] : 0;
2604 total_wid = column - right_margin;
2606 gf_link_filter(gf_line_test,
2607 gf_line_test_opt(pad_to_right_edge, (void *) &total_wid));
2608 wrapflags |= GFW_HANDLES;
2611 gf_link_filter(gf_nvtnl_local, NULL);
2613 if((errstr = gf_pipe(tmp_gc, final_pc)) != NULL){
2614 rv = FHT_WRTERR;
2615 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2616 "Can't build header : %.200s", errstr);
2620 if(df_store){
2621 gf_clear_so_readc(df_store);
2622 so_give(&df_store);
2624 else
2625 gf_clear_so_readc(tmp_store);
2628 so_give(&tmp_store);
2630 if(h)
2631 fs_give((void **)&h);
2633 if(fields)
2634 fs_give((void **)&fields);
2636 return(rv);
2640 /*----------------------------------------------------------------------
2641 Format RAW header text for display
2643 Args: stream -- mail stream for various header text fetches
2644 rawno -- sequence number in stream of message we're interested in
2645 section -- which section of message
2646 pc -- function to write header text with
2648 Result: 0 if all's well, -1 if write error, 1 if fetch error
2650 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2651 in the local convention.
2653 ----*/
2655 format_raw_header(MAILSTREAM *stream, long int msgno, char *section, gf_io_t pc)
2657 char *h = mail_fetch_header(stream, msgno, section, NULL, NULL, FT_PEEK);
2658 unsigned char c;
2660 if(h){
2661 while(*h){
2662 if(ISRFCEOL(h)){
2663 h += 2;
2664 if(!gf_puts(NEWLINE, pc))
2665 return(FHT_WRTERR);
2667 if(ISRFCEOL(h)) /* all done! */
2668 return(FHT_OK);
2670 else if((unsigned char)(*h) < 0x80 && FILTER_THIS(*h) &&
2671 !(*(h+1) && *h == ESCAPE && match_escapes(h+1))){
2672 c = (unsigned char) *h++;
2673 if(!((*pc)(c >= 0x80 ? '~' : '^')
2674 && (*pc)((c == 0x7f) ? '?' : (c & 0x1f) + '@')))
2675 return(FHT_WRTERR);
2677 else if(!(*pc)(*h++))
2678 return(FHT_WRTERR);
2681 else
2682 return(FHT_FTCHERR);
2684 return(FHT_OK);
2689 /*----------------------------------------------------------------------
2690 Format c-client envelope data suitable for display
2692 Args: s -- mail stream for various header text fetches
2693 n -- raw sequence number in stream of message we're interested in
2694 sect -- which section of message
2695 e -- pointer to msg's envelope
2696 pc -- function to write header text with
2697 which -- which header lines to write
2698 oacs --
2699 flags -- FM_ flags, see pith/mailview.h
2701 Result: 0 if all's well, -1 if write error, 1 if fetch error
2703 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2704 in the local convention.
2706 ----*/
2707 void
2708 format_envelope(MAILSTREAM *s, long int n, char *sect, ENVELOPE *e, gf_io_t pc,
2709 long int which, char *oacs, int flags)
2711 char *q, *p2, buftmp[MAILTMPLEN];
2713 if(!e)
2714 return;
2716 if((which & FE_DATE) && e->date) {
2717 q = "Date: ";
2718 snprintf(buftmp, sizeof(buftmp), "%s",
2719 F_ON(F_DATES_TO_LOCAL,ps_global)
2720 ? convert_date_to_local((char *) e->date) : (char *) e->date);
2721 buftmp[sizeof(buftmp)-1] = '\0';
2722 p2 = (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
2723 SIZEOF_20KBUF, buftmp);
2724 gf_puts(q, pc);
2725 format_env_puts(p2, pc);
2726 gf_puts(NEWLINE, pc);
2729 if((which & FE_FROM) && e->from)
2730 format_addr_string(s, n, sect, "From: ", e->from, flags, oacs, pc);
2732 if((which & FE_REPLYTO) && e->reply_to
2733 && (!e->from || !address_is_same(e->reply_to, e->from)))
2734 format_addr_string(s, n, sect, "Reply-To: ", e->reply_to, flags, oacs, pc);
2736 if((which & FE_TO) && e->to)
2737 format_addr_string(s, n, sect, "To: ", e->to, flags, oacs, pc);
2739 if((which & FE_CC) && e->cc)
2740 format_addr_string(s, n, sect, "Cc: ", e->cc, flags, oacs, pc);
2742 if((which & FE_BCC) && e->bcc)
2743 format_addr_string(s, n, sect, "Bcc: ", e->bcc, flags, oacs, pc);
2745 if((which & FE_RETURNPATH) && e->return_path)
2746 format_addr_string(s, n, sect, "Return-Path: ", e->return_path,
2747 flags, oacs, pc);
2749 if((which & FE_NEWSGROUPS) && e->newsgroups){
2750 int bogus = NIL;
2751 format_newsgroup_string("Newsgroups: ", e->newsgroups, flags, pc);
2752 if (!e->ngpathexists && e->message_id &&
2753 strncmp (e->message_id,"<alpine.",8) &&
2754 strncmp (e->message_id,"<Pine.",6) &&
2755 strncmp (e->message_id,"<MS-C.",6) &&
2756 strncmp (e->message_id,"<MailManager.",13) &&
2757 strncmp (e->message_id,"<EasyMail.",11) &&
2758 strncmp (e->message_id,"<ML-",4)) bogus = T;
2760 if(bogus)
2761 q_status_message(SM_ORDER, 0, 3,
2762 "Unverified Newsgroup header -- Message MAY or MAY NOT have been posted");
2765 if((which & FE_FOLLOWUPTO) && e->followup_to)
2766 format_newsgroup_string("Followup-To: ", e->followup_to, flags, pc);
2768 if((which & FE_SUBJECT) && e->subject && e->subject[0]){
2769 char *freeme = NULL;
2771 q = "Subject: ";
2772 gf_puts(q, pc);
2774 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->subject), SIZEOF_20KBUF-10000);
2776 if(flags & FM_DISPLAY
2777 && (ps_global->display_keywords_in_subject
2778 || ps_global->display_keywordinits_in_subject)){
2780 /* don't bother if no keywords are defined */
2781 if(some_user_flags_defined(s))
2782 p2 = freeme = prepend_keyword_subject(s, n, p2,
2783 ps_global->display_keywords_in_subject ? KW : KWInit,
2784 NULL, ps_global->VAR_KW_BRACES);
2787 format_env_puts(p2, pc);
2789 if(freeme)
2790 fs_give((void **) &freeme);
2792 gf_puts(NEWLINE, pc);
2795 if((which & FE_SENDER) && e->sender
2796 && (!e->from || !address_is_same(e->sender, e->from)))
2797 format_addr_string(s, n, sect, "Sender: ", e->sender, flags, oacs, pc);
2799 if((which & FE_MESSAGEID) && e->message_id){
2800 q = "Message-ID: ";
2801 gf_puts(q, pc);
2802 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->message_id), SIZEOF_20KBUF-10000);
2803 format_env_puts(p2, pc);
2804 gf_puts(NEWLINE, pc);
2807 if((which & FE_INREPLYTO) && e->in_reply_to){
2808 q = "In-Reply-To: ";
2809 gf_puts(q, pc);
2810 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);
2811 format_env_puts(p2, pc);
2812 gf_puts(NEWLINE, pc);
2815 if((which & FE_REFERENCES) && e->references) {
2816 q = "References: ";
2817 gf_puts(q, pc);
2818 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->references), SIZEOF_20KBUF-10000);
2819 format_env_puts(p2, pc);
2820 gf_puts(NEWLINE, pc);
2828 * The argument fieldname is something like "Subject:..." or "Subject".
2829 * Look through the specs in speccolor for a match of the fieldname,
2830 * and return the color that goes with any match, or NULL.
2831 * Caller should free the color.
2833 COLOR_PAIR *
2834 hdr_color(char *fieldname, char *value, SPEC_COLOR_S *speccolor)
2836 SPEC_COLOR_S *hc = NULL;
2837 COLOR_PAIR *color_pair = NULL;
2838 char *colon, *fname;
2839 char fbuf[FBUF_LEN+1];
2840 int gotit;
2841 PATTERN_S *pat;
2843 colon = strindex(fieldname, ':');
2844 if(colon){
2845 strncpy(fbuf, fieldname, MIN(colon-fieldname,FBUF_LEN));
2846 fbuf[MIN(colon-fieldname,FBUF_LEN)] = '\0';
2847 fname = fbuf;
2849 else
2850 fname = fieldname;
2852 if(fname && *fname)
2853 for(hc = speccolor; hc; hc = hc->next)
2854 if(hc->spec && !strucmp(fname, hc->spec)){
2855 if(!hc->val)
2856 break;
2858 gotit = 0;
2859 for(pat = hc->val; !gotit && pat; pat = pat->next)
2860 if(srchstr(value, pat->substring))
2861 gotit++;
2863 if(gotit)
2864 break;
2867 if(hc && hc->fg && hc->fg[0] && hc->bg && hc->bg[0])
2868 color_pair = new_color_pair(hc->fg, hc->bg);
2870 return(color_pair);
2875 * The argument fieldname is something like "Subject:..." or "Subject".
2876 * Look through the specs in hdr_colors for a match of the fieldname,
2877 * and return 1 if that fieldname is in one of the patterns, 0 otherwise.
2880 any_hdr_color(char *fieldname)
2882 SPEC_COLOR_S *hc = NULL;
2883 char *colon, *fname;
2884 char fbuf[FBUF_LEN+1];
2886 colon = strindex(fieldname, ':');
2887 if(colon){
2888 strncpy(fbuf, fieldname, MIN(colon-fieldname,FBUF_LEN));
2889 fbuf[MIN(colon-fieldname,FBUF_LEN)] = '\0';
2890 fname = fbuf;
2892 else
2893 fname = fieldname;
2895 if(fname && *fname)
2896 for(hc = ps_global->hdr_colors; hc; hc = hc->next)
2897 if(hc->spec && !strucmp(fname, hc->spec))
2898 break;
2900 return(hc ? 1 : 0);
2904 /*----------------------------------------------------------------------
2905 Format an address field, wrapping lines nicely at commas
2907 Args: field_name -- The name of the field we're formatting ("TO: ", ...)
2908 addr -- ADDRESS structure to format
2910 Result: A formatted, malloced string is returned.
2911 ----------------------------------------------------------------------*/
2912 void
2913 format_addr_string(MAILSTREAM *stream, long int msgno, char *section, char *field_name,
2914 struct mail_address *addr, int flags, char *oacs, gf_io_t pc)
2916 char *ptmp, *mtmp;
2917 int trailing = 0, group = 0;
2918 ADDRESS *atmp;
2920 if(!addr)
2921 return;
2924 * quickly run down address list to make sure none are patently bogus.
2925 * If so, just blat raw field out.
2927 for(atmp = addr; stream && atmp; atmp = atmp->next)
2928 if(atmp->host && atmp->host[0] == '.'){
2929 char *field, *fields[2];
2931 fields[1] = NULL;
2932 fields[0] = cpystr(field_name);
2933 if((ptmp = strchr(fields[0], ':')) != NULL)
2934 *ptmp = '\0';
2936 if((field = pine_fetchheader_lines(stream, msgno, section, fields)) != NULL){
2937 char *h, *t;
2939 for(t = h = field; *h ; t++)
2940 if(*t == '\015' && *(t+1) == '\012'){
2941 *t = '\0'; /* tie off line */
2942 format_env_puts(h, pc);
2943 if(*(h = (++t) + 1)) /* set new h and skip CRLF */
2944 gf_puts(NEWLINE, pc); /* more to write */
2945 else
2946 break;
2948 else if(!*t){ /* shouldn't happen much */
2949 if(h != t)
2950 format_env_puts(h, pc);
2952 break;
2955 fs_give((void **)&field);
2958 fs_give((void **)&fields[0]);
2959 gf_puts(NEWLINE, pc);
2960 dprint((2, "Error in \"%s\" field address\n",
2961 field_name ? field_name : "?"));
2962 return;
2965 gf_puts(field_name, pc);
2967 while(addr){
2968 atmp = addr->next; /* remember what's next */
2969 addr->next = NULL;
2970 if(!addr->host && addr->mailbox){
2971 mtmp = addr->mailbox;
2972 addr->mailbox = cpystr((char *)rfc1522_decode_to_utf8(
2973 (unsigned char *)tmp_20k_buf,
2974 SIZEOF_20KBUF, addr->mailbox));
2977 ptmp = addr->personal; /* RFC 1522 personal name? */
2978 addr->personal = iutf8ncpy((char *)tmp_20k_buf, (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+10000), SIZEOF_20KBUF-10000, addr->personal), 10000);
2979 tmp_20k_buf[10000-1] = '\0';
2981 if(!trailing) /* 1st pass, just address */
2982 trailing++;
2983 else{ /* else comma, unless */
2984 if(!((group == 1 && addr->host) /* 1st addr in group, */
2985 || (!addr->host && !addr->mailbox))){ /* or end of group */
2986 gf_puts(",", pc);
2987 #if 0
2988 gf_puts(NEWLINE, pc); /* ONE address/line please */
2989 gf_puts(" ", pc);
2990 #endif
2993 gf_puts(" ", pc);
2996 pine_rfc822_write_address_noquote(addr, pc, &group);
2998 addr->personal = ptmp; /* restore old personal ptr */
2999 if(!addr->host && addr->mailbox){
3000 fs_give((void **)&addr->mailbox);
3001 addr->mailbox = mtmp;
3004 addr->next = atmp;
3005 addr = atmp;
3008 gf_puts(NEWLINE, pc);
3014 const char *rspecials_minus_quote_and_dot = "()<>@,;:\\[]";
3015 /* RFC822 continuation, must start with CRLF */
3016 #define RFC822CONT "\015\012 "
3018 /* Write RFC822 address with some quoting turned off.
3019 * Accepts:
3020 * address to interpret
3022 * (This is a copy of c-client's rfc822_write_address except
3023 * we don't quote double quote and dot in personal names. It writes
3024 * to a gf_io_t instead of to a buffer so that we don't have to worry
3025 * about fixed sized buffer overflowing. It's also special cased to deal
3026 * with only a single address.)
3028 * The idea is that there are some places where we'd just like to display
3029 * the personal name as is before applying confusing quoting. However,
3030 * we do want to be careful not to break things that should be quoted so
3031 * we'll only use this where we are sure. Quoting may look ugly but it
3032 * doesn't usually break anything.
3034 void
3035 pine_rfc822_write_address_noquote(struct mail_address *adr, gf_io_t pc, int *group)
3037 extern const char *rspecials;
3039 if (adr->host) { /* ordinary address? */
3040 if (!(adr->personal || adr->adl)) pine_rfc822_address (adr, pc);
3041 else { /* no, must use phrase <route-addr> form */
3042 if (adr->personal)
3043 pine_rfc822_cat (adr->personal, rspecials_minus_quote_and_dot, pc);
3045 gf_puts(" <", pc); /* write address delimiter */
3046 pine_rfc822_address(adr, pc);
3047 gf_puts (">", pc); /* closing delimiter */
3050 if(*group)
3051 (*group)++;
3053 else if (adr->mailbox) { /* start of group? */
3054 /* yes, write group name */
3055 pine_rfc822_cat (adr->mailbox, rspecials, pc);
3057 gf_puts (": ", pc); /* write group identifier */
3058 *group = 1; /* in a group */
3060 else if (*group) { /* must be end of group (but be paranoid) */
3061 gf_puts (";", pc);
3062 *group = 0; /* no longer in that group */
3067 /* Write RFC822 route-address to string
3068 * Accepts:
3069 * address to interpret
3072 void
3073 pine_rfc822_address(struct mail_address *adr, gf_io_t pc)
3075 extern char *wspecials;
3077 if (adr && adr->host) { /* no-op if no address */
3078 if (adr->adl) { /* have an A-D-L? */
3079 gf_puts (adr->adl, pc);
3080 gf_puts (":", pc);
3082 /* write mailbox name */
3083 pine_rfc822_cat (adr->mailbox, wspecials, pc);
3084 if (*adr->host != '@') { /* unless null host (HIGHLY discouraged!) */
3085 gf_puts ("@", pc); /* host delimiter */
3086 gf_puts (adr->host, pc); /* write host name */
3092 /* Concatenate RFC822 string
3093 * Accepts:
3094 * pointer to string to concatenate
3095 * list of special characters
3098 void
3099 pine_rfc822_cat(char *src, const char *specials, gf_io_t pc)
3101 char *s;
3103 if (strpbrk (src,specials)) { /* any specials present? */
3104 gf_puts ("\"", pc); /* opening quote */
3105 /* truly bizarre characters in there? */
3106 while ((s = strpbrk (src,"\\\"")) != NULL) {
3107 char save[2];
3109 /* turn it into a null-terminated piece */
3110 save[0] = *s;
3111 save[1] = '\0';
3112 *s = '\0';
3113 gf_puts (src, pc); /* yes, output leader */
3114 *s = save[0];
3115 gf_puts ("\\", pc); /* quoting */
3116 gf_puts (save, pc); /* output the bizarre character */
3117 src = ++s; /* continue after the bizarre character */
3119 if (*src) gf_puts (src, pc);/* output non-bizarre string */
3120 gf_puts ("\"", pc); /* closing quote */
3122 else gf_puts (src, pc); /* otherwise it's the easy case */
3126 /*----------------------------------------------------------------------
3127 Format an address field, wrapping lines nicely at commas
3129 Args: field_name -- The name of the field we're formatting ("TO:", Cc:...)
3130 newsgrps -- ADDRESS structure to format
3132 Result: A formatted, malloced string is returned.
3134 The resuling lines formatted are 80 columns wide.
3135 ----------------------------------------------------------------------*/
3136 void
3137 format_newsgroup_string(char *field_name, char *newsgrps, int flags, gf_io_t pc)
3139 char buf[MAILTMPLEN];
3140 int trailing = 0, llen, alen;
3141 char *next_ng;
3143 if(!newsgrps || !*newsgrps)
3144 return;
3146 gf_puts(field_name, pc);
3148 llen = strlen(field_name);
3149 while(*newsgrps){
3150 for(next_ng = newsgrps; *next_ng && *next_ng != ','; next_ng++);
3151 strncpy(buf, newsgrps, MIN(next_ng - newsgrps, sizeof(buf)-1));
3152 buf[MIN(next_ng - newsgrps, sizeof(buf)-1)] = '\0';
3153 newsgrps = next_ng;
3154 if(*newsgrps)
3155 newsgrps++;
3156 alen = strlen(buf);
3157 if(!trailing){ /* first time thru, just address */
3158 llen += alen;
3159 trailing++;
3161 else{ /* else preceding comma */
3162 gf_puts(",", pc);
3163 llen++;
3165 if(alen + llen + 1 > 76){
3166 gf_puts(NEWLINE, pc);
3167 gf_puts(" ", pc);
3168 llen = alen + 5;
3170 else{
3171 gf_puts(" ", pc);
3172 llen += alen + 1;
3176 if(alen && llen > 76){ /* handle long addresses */
3177 register char *q, *p = &buf[alen-1];
3179 while(p > buf){
3180 if(isspace((unsigned char)*p)
3181 && (llen - (alen - (int)(p - buf))) < 76){
3182 for(q = buf; q < p; q++)
3183 (*pc)(*q); /* write character */
3185 gf_puts(NEWLINE, pc);
3186 gf_puts(" ", pc);
3187 gf_puts(p, pc);
3188 break;
3190 else
3191 p--;
3194 if(p == buf) /* no reasonable break point */
3195 gf_puts(buf, pc);
3197 else
3198 gf_puts(buf, pc);
3201 gf_puts(NEWLINE, pc);
3206 /*----------------------------------------------------------------------
3207 Format a text field that's part of some raw (non-envelope) message header
3209 Args: start --
3210 finish --
3211 pc --
3213 Result: Semi-digested text (RFC 1522 decoded, anyway) written with "pc"
3215 ----------------------------------------------------------------------*/
3217 format_raw_hdr_string(char *start, char *finish, gf_io_t pc, char *oacs, int flags)
3219 register char *current;
3220 unsigned char *p, *tmp = NULL, c;
3221 size_t n, len;
3222 char ch;
3223 int rv = FHT_OK;
3225 ch = *finish;
3226 *finish = '\0';
3228 if((n = 4*(finish-start)) > SIZEOF_20KBUF-1){
3229 len = n+1;
3230 p = tmp = (unsigned char *) fs_get(len * sizeof(unsigned char));
3232 else{
3233 len = SIZEOF_20KBUF;
3234 p = (unsigned char *) tmp_20k_buf;
3237 if(islower((unsigned char)(*start)))
3238 *start = toupper((unsigned char)(*start));
3240 current = (char *) rfc1522_decode_to_utf8(p, len, start);
3242 /* output from start to finish */
3243 while(*current && rv == FHT_OK)
3244 if(ISRFCEOL(current)){
3245 if(!gf_puts(NEWLINE, pc))
3246 rv = FHT_WRTERR;
3248 current += 2;
3250 else if((unsigned char)(*current) < 0x80 && FILTER_THIS(*current) &&
3251 !(*(current+1) && *current == ESCAPE && match_escapes(current+1))){
3252 c = (unsigned char) *current++;
3253 if(!((*pc)(c >= 0x80 ? '~' : '^')
3254 && (*pc)((c == 0x7f) ? '?' : (c & 0x1f) + '@')))
3255 rv = FHT_WRTERR;
3257 else if(!(*pc)(*current++))
3258 rv = FHT_WRTERR;
3260 if(tmp)
3261 fs_give((void **) &tmp);
3263 *finish = ch;
3265 return(rv);
3271 /*----------------------------------------------------------------------
3272 Format a text field that's part of some raw (non-envelope) message header
3274 Args: s --
3275 pc --
3277 Result: Output
3279 ----------------------------------------------------------------------*/
3281 format_env_puts(char *s, gf_io_t pc)
3283 if(ps_global->pass_ctrl_chars)
3284 return(gf_puts(s, pc));
3286 for(; *s; s++)
3287 if((unsigned char)(*s) < 0x80 && FILTER_THIS(*s) && !(*(s+1) && *s == ESCAPE && match_escapes(s+1))){
3288 if(!((*pc)((unsigned char) (*s) >= 0x80 ? '~' : '^')
3289 && (*pc)((*s == 0x7f) ? '?' : (*s & 0x1f) + '@')))
3290 return(0);
3292 else if(!(*pc)(*s))
3293 return(0);
3295 return(1);
3299 char *
3300 display_parameters(PARAMETER *params)
3302 int n, longest = 0;
3303 char *d, *printme;
3304 PARAMETER *p;
3305 PARMLIST_S *parmlist;
3307 for(p = params; p; p = p->next) /* ok if we include *'s */
3308 if(p->attribute && (n = strlen(p->attribute)) > longest)
3309 longest = MIN(32, n); /* shouldn't be any bigger than 32 */
3311 d = tmp_20k_buf;
3312 tmp_20k_buf[0] = '\0';
3313 if((parmlist = rfc2231_newparmlist(params)) != NULL){
3314 n = 0; /* n overloaded */
3315 while(rfc2231_list_params(parmlist) && d < tmp_20k_buf + 10000){
3316 if(n++){
3317 snprintf(d, 10000-(d-tmp_20k_buf), "\n");
3318 tmp_20k_buf[10000-1] = '\0';
3319 d += strlen(d);
3322 if(parmlist->value){
3323 if(parmlist->attrib && strucmp(parmlist->attrib, "url") == 0){
3324 snprintf(printme = tmp_20k_buf + 11000, 1000, "%s", parmlist->value);
3325 sqzspaces(printme);
3327 else
3328 printme = strsquish(tmp_20k_buf + 11000, 1000, parmlist->value, 100);
3330 else
3331 printme = "";
3333 snprintf(d, 10000-(d-tmp_20k_buf), "%-*s: %s", longest,
3334 parmlist->attrib ? parmlist->attrib : "", printme);
3336 tmp_20k_buf[10000-1] = '\0';
3337 d += strlen(d);
3340 rfc2231_free_parmlist(&parmlist);
3343 return(tmp_20k_buf);
3347 /*----------------------------------------------------------------------
3348 Fetch the requested header fields from the msgno specified
3350 Args: stream -- mail stream of open folder
3351 msgno -- number of message to get header lines from
3352 fields -- array of pointers to desired fields
3354 Returns: allocated string containing matched header lines,
3355 NULL on error.
3356 ----*/
3357 char *
3358 pine_fetch_header(MAILSTREAM *stream, long int msgno, char *section, char **fields, long int flags)
3360 STRINGLIST *sl;
3361 char *p, *m, *h = NULL, *match = NULL, *free_this, tmp[MAILTMPLEN];
3362 char **pflds = NULL, **pp = NULL, **qq;
3365 * If the user misconfigures it is possible to have one of the fields
3366 * set to the empty string instead of a header name. We want to catch
3367 * that here instead of asking the server the nonsensical question.
3369 for(pp = fields ? &fields[0] : NULL; pp && *pp; pp++)
3370 if(!**pp)
3371 break;
3373 if(pp && *pp){ /* found an empty header field, fix it */
3374 pflds = copy_list_array(fields);
3375 for(pp = pflds; pp && *pp; pp++){
3376 if(!**pp){ /* scoot rest of the lines up */
3377 free_this = *pp;
3378 for(qq = pp; *qq; qq++)
3379 *qq = *(qq+1);
3381 if(free_this)
3382 fs_give((void **) &free_this);
3386 /* no headers to look for, return NULL */
3387 if(pflds && !*pflds && !(flags & FT_NOT)){
3388 free_list_array(&pflds);
3389 return(NULL);
3392 else
3393 pflds = fields;
3395 sl = (pflds && *pflds) ? new_strlst(pflds) : NULL; /* package up fields */
3396 h = mail_fetch_header(stream, msgno, section, sl, NULL, flags | FT_PEEK);
3397 if (sl)
3398 free_strlst(&sl);
3400 if(!h){
3401 if(pflds && pflds != fields)
3402 free_list_array(&pflds);
3404 return(NULL);
3407 while(find_field(&h, tmp, sizeof(tmp))){
3408 for(pp = &pflds[0]; *pp && strucmp(tmp, *pp); pp++)
3411 /* interesting field? */
3412 if((p = (flags & FT_NOT) ? ((*pp) ? NULL : tmp) : *pp) != NULL){
3414 * Hold off allocating space for matching fields until
3415 * we at least find one to copy...
3417 if(!match)
3418 match = m = fs_get(strlen(h) + strlen(p) + 1);
3420 while(*p) /* copy field name */
3421 *m++ = *p++;
3423 while(*h && (*m++ = *h++)) /* header includes colon */
3424 if(*(m-1) == '\n' && (*h == '\r' || !isspace((unsigned char)*h)))
3425 break;
3427 *m = '\0'; /* tie off match string */
3429 else{ /* no match, pass this field */
3430 while(*h && !(*h++ == '\n'
3431 && (*h == '\r' || !isspace((unsigned char)*h))))
3436 if(pflds && pflds != fields)
3437 free_list_array(&pflds);
3439 return(match ? match : cpystr(""));
3444 find_field(char **h, char *tmp, size_t ntmp)
3446 char *otmp = tmp;
3448 if(!h || !*h || !**h || isspace((unsigned char)**h))
3449 return(0);
3451 while(tmp-otmp<ntmp-1 && **h && **h != ':' && !isspace((unsigned char)**h))
3452 *tmp++ = *(*h)++;
3454 *tmp = '\0';
3455 return(1);
3459 static char *_last_embedded_fg_color, *_last_embedded_bg_color;
3463 embed_color(COLOR_PAIR *cp, gf_io_t pc)
3465 if(cp && cp->fg){
3466 if(_last_embedded_fg_color)
3467 fs_give((void **)&_last_embedded_fg_color);
3469 _last_embedded_fg_color = cpystr(cp->fg);
3471 if(!(pc && (*pc)(TAG_EMBED) && (*pc)(TAG_FGCOLOR) &&
3472 gf_puts(color_to_asciirgb(cp->fg), pc)))
3473 return 0;
3476 if(cp && cp->bg){
3477 if(_last_embedded_bg_color)
3478 fs_give((void **)&_last_embedded_bg_color);
3480 _last_embedded_bg_color = cpystr(cp->bg);
3482 if(!(pc && (*pc)(TAG_EMBED) && (*pc)(TAG_BGCOLOR) &&
3483 gf_puts(color_to_asciirgb(cp->bg), pc)))
3484 return 0;
3487 return 1;
3491 COLOR_PAIR *
3492 get_cur_embedded_color(void)
3494 COLOR_PAIR *ret;
3496 if(_last_embedded_fg_color && _last_embedded_bg_color)
3497 ret = new_color_pair(_last_embedded_fg_color, _last_embedded_bg_color);
3498 else
3499 ret = pico_get_cur_color();
3501 return(ret);
3505 void
3506 clear_cur_embedded_color(void)
3508 if(_last_embedded_fg_color)
3509 fs_give((void **)&_last_embedded_fg_color);
3511 if(_last_embedded_bg_color)
3512 fs_give((void **)&_last_embedded_bg_color);
3517 scroll_handle_start_color(char *colorstring, size_t buflen, int *len)
3519 *len = 0;
3521 if(pico_usingcolor()){
3522 char *fg = NULL, *bg = NULL, *s;
3524 if(ps_global->VAR_SLCTBL_FORE_COLOR
3525 && colorcmp(ps_global->VAR_SLCTBL_FORE_COLOR,
3526 ps_global->VAR_NORM_FORE_COLOR))
3527 fg = ps_global->VAR_SLCTBL_FORE_COLOR;
3529 if(ps_global->VAR_SLCTBL_BACK_COLOR
3530 && colorcmp(ps_global->VAR_SLCTBL_BACK_COLOR,
3531 ps_global->VAR_NORM_BACK_COLOR))
3532 bg = ps_global->VAR_SLCTBL_BACK_COLOR;
3534 if(bg || fg){
3535 COLOR_PAIR *tmp;
3538 * The blacks are just known good colors for
3539 * testing whether the other color is good.
3541 if((tmp = new_color_pair(fg ? fg : colorx(COL_BLACK),
3542 bg ? bg : colorx(COL_BLACK))) != NULL){
3543 if(pico_is_good_colorpair(tmp))
3544 for(s = color_embed(fg, bg);
3545 (*len) < buflen && (colorstring[*len] = *s);
3546 s++, (*len)++)
3549 free_color_pair(&tmp);
3553 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
3554 strncpy(colorstring + (*len), url_embed(TAG_BOLDON), MIN(3,buflen-(*len)));
3555 *len += 2;
3559 colorstring[buflen-1] = '\0';
3561 return(*len != 0);
3566 scroll_handle_end_color(char *colorstring, size_t buflen, int *len, int use_hdr_color)
3568 *len = 0;
3569 if(pico_usingcolor()){
3570 char *fg = NULL, *bg = NULL, *s;
3571 char *basefg = NULL, *basebg = NULL;
3573 basefg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_FORE_COLOR
3574 : ps_global->VAR_NORM_FORE_COLOR;
3575 basebg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_BACK_COLOR
3576 : ps_global->VAR_NORM_BACK_COLOR;
3579 * We need to change the fg and bg colors back even if they
3580 * are the same as the Normal Colors so that color_a_quote
3581 * will have a chance to pick up those colors as the ones to
3582 * switch to. We don't do this before the handle above so that
3583 * the quote color will flow into the selectable item when
3584 * the selectable item color is partly the same as the
3585 * normal color. That is, suppose the normal color was black on
3586 * cyan and the selectable color was blue on cyan, only a fg color
3587 * change. We preserve the only-a-fg-color-change in a quote by
3588 * letting the quote background color flow into the selectable text.
3590 if(ps_global->VAR_SLCTBL_FORE_COLOR)
3591 fg = basefg;
3593 if(ps_global->VAR_SLCTBL_BACK_COLOR)
3594 bg = basebg;
3596 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
3597 strncpy(colorstring, url_embed(TAG_BOLDOFF), MIN(3,buflen));
3598 *len = 2;
3601 if(fg || bg)
3602 for(s = color_embed(fg, bg); (*len) < buflen && (colorstring[*len] = *s); s++, (*len)++)
3606 colorstring[buflen-1] = '\0';
3608 return(*len != 0);
3613 * Helper routine that is of limited use.
3614 * We need to tally up the screen width of
3615 * a UTF-8 string as we go through the string.
3616 * We just want the width of the character starting
3617 * at str (and no longer than remaining_octets).
3618 * If we're plopped into the middle of a UTF-8
3619 * character we just want to return width zero.
3622 width_at_this_position(unsigned char *str, unsigned long n)
3624 unsigned char *inputp = str;
3625 unsigned long remaining_octets = n;
3626 UCS ucs;
3627 int width = 0;
3629 ucs = (UCS) utf8_get(&inputp, &remaining_octets);
3630 if(!(ucs & U8G_ERROR || ucs == UBOGON)){
3631 width = wcellwidth(ucs);
3632 /* Writechar will print a '?' */
3633 if(width < 0)
3634 width = 1;
3637 return(width);