2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2008 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 /*======================================================================
18 Implements message text fetching and decoding
23 #include "../pith/headers.h"
24 #include "../pith/text.h"
25 #include "../pith/state.h"
26 #include "../pith/conf.h"
27 #include "../pith/handle.h"
28 #include "../pith/margin.h"
29 #include "../pith/editorial.h"
30 #include "../pith/color.h"
31 #include "../pith/filter.h"
32 #include "../pith/charset.h"
33 #include "../pith/status.h"
34 #include "../pith/mailview.h"
35 #include "../pith/mimedesc.h"
36 #include "../pith/detach.h"
39 /* internal prototypes */
40 int charset_editorial(char *, long, HANDLE_S
**, int, int, int, gf_io_t
);
41 int replace_quotes(long, char *, LT_INS_S
**, void *);
42 void mark_handles_in_line(char *, HANDLE_S
**, int);
43 void clear_html_risk(void);
44 void set_html_risk(void);
45 int get_html_risk(void);
48 #define CHARSET_DISCLAIMER_1 \
49 "The following text is in the \"%.40s\" character set.\015\012Your "
50 #define CHARSET_DISCLAIMER_2 "display is set"
51 #define CHARSET_DISCLAIMER_3 \
52 " for the \"%.40s\" character set. \015\012Some %.40scharacters may be displayed incorrectly."
53 #define ENCODING_DISCLAIMER \
54 "The following text contains the unknown encoding type \"%.20s\". \015\012Some or all of the text may be displayed incorrectly."
55 #define HTML_WARNING \
56 "The following HTML text may contain deceptive links. Carefully note the destination URL before visiting any links."
60 * HTML risk state holder.
62 static int _html_risk
;
66 /*----------------------------------------------------------------------
67 Handle fetching and filtering a text message segment to be displayed
68 by scrolltool or printed or exported or piped.
70 Args: att -- segment to fetch
71 msgno -- message number segment is a part of
72 pc -- function to write characters from segment with
73 style -- Indicates special handling for error messages
74 flags -- Indicates special necessary handling
76 Returns: 1 if errors encountered, 0 if everything went A-OK
80 decode_text(ATTACH_S
*att
,
87 FILTLIST_S filters
[14];
89 int filtcnt
= 0, error_found
= 0, column
, wrapit
;
90 int is_in_sig
= OUT_SIG_BLOCK
;
91 int is_flowed_msg
= 0;
95 char *free_this
= NULL
;
96 STORE_S
*warn_so
= NULL
;
100 column
= (flags
& FM_DISPLAY
) ? ps_global
->ttyo
->screen_cols
: 80;
102 if(!(flags
& FM_DISPLAY
))
103 flags
|= FM_NOINDENT
;
107 memset(filters
, 0, sizeof(filters
));
109 /* charset the body part is in */
110 charset
= parameter_val(att
->body
->parameter
, "charset");
112 /* determined if it's flowed, affects wrapping and quote coloring */
113 if(att
->body
->type
== TYPETEXT
114 && !strucmp(att
->body
->subtype
, "plain")
115 && (parmval
= parameter_val(att
->body
->parameter
, "format"))){
116 if(!strucmp(parmval
, "flowed"))
118 fs_give((void **) &parmval
);
121 if((parmval
= parameter_val(att
->body
->parameter
, "delsp")) != NULL
){
122 if(!strucmp(parmval
, "yes"))
125 fs_give((void **) &parmval
);
130 if(!ps_global
->pass_ctrl_chars
){
131 filters
[filtcnt
++].filter
= gf_escape_filter
;
132 filters
[filtcnt
].filter
= gf_control_filter
;
135 filters
[filtcnt
++].data
= gf_control_filter_opt(&filt_only_c0
);
138 if(flags
& FM_DISPLAY
)
139 filters
[filtcnt
++].filter
= gf_tag_filter
;
142 * if it's just plain old text, look for url's
144 if(!(att
->body
->subtype
&& strucmp(att
->body
->subtype
, "plain"))){
145 struct variable
*vars
= ps_global
->vars
;
147 if((F_ON(F_VIEW_SEL_URL
, ps_global
)
148 || F_ON(F_VIEW_SEL_URL_HOST
, ps_global
)
149 || F_ON(F_SCAN_ADDR
, ps_global
))
153 * The url_hilite filter really ought to come
154 * after flowing, because flowing with the DelSp=yes parameter
155 * can reassemble broken urls back into identifiable urls.
156 * We add the preflow filter to do only the reassembly part
157 * of the flowing so that we can spot the urls.
158 * At this time (2005-03-29) we know that Apple Mail does
159 * send mail like this sometimes. This filter removes the
160 * sequence SP CRLF if that seems safe.
162 if(ps_global
->full_header
!= 2 && is_delsp_yes
)
163 filters
[filtcnt
++].filter
= gf_preflow
;
165 filters
[filtcnt
].filter
= gf_line_test
;
166 filters
[filtcnt
++].data
= gf_line_test_opt(url_hilite
,
167 gf_url_hilite_opt(&uh
,handlesp
,0));
171 * First, paint the signature.
172 * Disclaimers noted below for coloring quotes apply here as well.
174 if((flags
& FM_DISPLAY
)
175 && !(flags
& FM_NOCOLOR
)
177 && VAR_SIGNATURE_FORE_COLOR
178 && VAR_SIGNATURE_BACK_COLOR
){
179 filters
[filtcnt
].filter
= gf_line_test
;
180 filters
[filtcnt
++].data
= gf_line_test_opt(color_signature
,
185 * Gotta be careful with this. The color_a_quote filter adds color
186 * to the beginning and end of the line. This will break some
187 * line_test-style filters which come after it. For example, if they
188 * are looking for something at the start of a line (like color_a_quote
189 * itself). I guess we could fix that by ignoring tags at the
190 * beginning of the line when doing the search.
192 if((flags
& FM_DISPLAY
)
193 && !(flags
& FM_NOCOLOR
)
195 && VAR_QUOTE1_FORE_COLOR
196 && VAR_QUOTE1_BACK_COLOR
){
197 filters
[filtcnt
].filter
= gf_line_test
;
198 filters
[filtcnt
++].data
= gf_line_test_opt(color_a_quote
,
202 else if(!strucmp(att
->body
->subtype
, "richtext")){
205 plain_opt
= !(flags
&FM_DISPLAY
);
207 /* maybe strip everything! */
208 filters
[filtcnt
].filter
= gf_rich2plain
;
209 filters
[filtcnt
++].data
= gf_rich2plain_opt(&plain_opt
);
210 /* width to use for file or printer */
214 else if(!strucmp(att
->body
->subtype
, "enriched")){
217 plain_opt
= !(flags
&FM_DISPLAY
);
219 filters
[filtcnt
].filter
= gf_enriched2plain
;
220 filters
[filtcnt
++].data
= gf_enriched2plain_opt(&plain_opt
);
221 /* width to use for file or printer */
225 else if(!strucmp(att
->body
->subtype
, "html") && ps_global
->full_header
< 2){
226 /*BUG: sniff the params for "version=2.0" ala draft-ietf-html-spec-01 */
231 if(flags
& FM_DISPLAY
){
234 if(handlesp
){ /* pass on handles awareness */
235 opts
|= GFHP_HANDLES
;
237 if(F_OFF(F_QUELL_HOST_AFTER_URL
, ps_global
) && !(flags
& FM_HIDESERVER
))
238 opts
|= GFHP_SHOW_SERVER
;
241 if(!(flags
& FM_NOEDITORIAL
)){
242 warn_so
= so_get(CharStar
, NULL
, EDIT_ACCESS
);
243 gf_set_so_writec(&warn_pc
, warn_so
);
244 format_editorial(HTML_WARNING
, column
, flags
, handlesp
, warn_pc
);
245 gf_clear_so_writec(warn_so
);
246 so_puts(warn_so
, "\015\012");
247 so_writec('\0', warn_so
);
251 opts
|= GFHP_STRIPPED
; /* don't embed anything! */
253 if(flags
& FM_NOHTMLREL
)
254 opts
|= GFHP_NO_RELATIVE
;
256 if(flags
& FM_HTMLRELATED
)
257 opts
|= GFHP_RELATED_CONTENT
;
262 if(flags
& FM_HTMLIMAGES
)
263 opts
|= GFHP_HTML_IMAGES
;
265 wrapit
= 0; /* wrap already handled! */
266 filters
[filtcnt
].filter
= gf_html2plain
;
267 filters
[filtcnt
++].data
= gf_html2plain_opt(NULL
, column
,
268 (flags
& (FM_NOINDENT
| FM_HTML
))
270 : format_view_margin(),
271 handlesp
, set_html_risk
, opts
);
274 filters
[filtcnt
].filter
= gf_prepend_editorial
;
275 filters
[filtcnt
++].data
= gf_prepend_editorial_opt(get_html_risk
,
276 (char *) so_text(warn_so
));
281 * If the message is not flowed, we do the quote suppression before
282 * the wrapping, because the wrapping does not preserve the quote
283 * characters at the beginnings of the lines in that case.
284 * Otherwise, we defer until after the wrapping.
286 * Also, this is a good place to do quote-replacement on nonflowed
287 * messages because no other filters depend on the "> ".
288 * Quote-replacement is easier in the flowed case and occurs
289 * automatically in the flowed wrapping filter.
292 && ps_global
->full_header
== 0
293 && !(att
->body
->subtype
&& strucmp(att
->body
->subtype
, "plain"))
294 && (flags
& FM_DISPLAY
)){
295 if(ps_global
->quote_suppression_threshold
!= 0){
296 memset(&dq
, 0, sizeof(dq
));
297 dq
.lines
= ps_global
->quote_suppression_threshold
;
298 dq
.is_flowed
= is_flowed_msg
;
299 dq
.indent_length
= 0; /* indent didn't happen yet */
300 dq
.saved_line
= &free_this
;
301 dq
.handlesp
= handlesp
;
302 dq
.do_color
= (!(flags
& FM_NOCOLOR
) && pico_usingcolor());
304 filters
[filtcnt
].filter
= gf_line_test
;
305 filters
[filtcnt
++].data
= gf_line_test_opt(delete_quotes
, &dq
);
307 if(ps_global
->VAR_QUOTE_REPLACE_STRING
308 && F_ON(F_QUOTE_REPLACE_NOFLOW
, ps_global
)){
309 filters
[filtcnt
].filter
= gf_line_test
;
310 filters
[filtcnt
++].data
= gf_line_test_opt(replace_quotes
, NULL
);
314 if(wrapit
&& !(flags
& FM_NOWRAP
)){
315 int wrapflags
= (flags
& FM_DISPLAY
) ? (GFW_HANDLES
|GFW_SOFTHYPHEN
)
318 if(flags
& FM_DISPLAY
319 && !(flags
& FM_NOCOLOR
)
320 && pico_usingcolor())
321 wrapflags
|= GFW_USECOLOR
;
323 /* text/flowed (RFC 2646 + draft)? */
324 if(ps_global
->full_header
!= 2 && is_flowed_msg
){
325 wrapflags
|= GFW_FLOWED
;
328 wrapflags
|= GFW_DELSP
;
331 filters
[filtcnt
].filter
= gf_wrap
;
332 filters
[filtcnt
++].data
= gf_wrap_filter_opt(wrapit
, column
,
333 (flags
& FM_NOINDENT
)
335 : format_view_margin(),
340 * This has to come after wrapping has happened because the user tells
341 * us how many quoted lines to display, and we want that number to be
342 * the wrapped lines, not the pre-wrapped lines. We do it before the
343 * wrapping if the message is not flowed, because the wrapping does not
344 * preserve the quote characters at the beginnings of the lines in that
348 && ps_global
->full_header
== 0
349 && !(att
->body
->subtype
&& strucmp(att
->body
->subtype
, "plain"))
350 && (flags
& FM_DISPLAY
)
351 && ps_global
->quote_suppression_threshold
!= 0){
352 memset(&dq
, 0, sizeof(dq
));
353 dq
.lines
= ps_global
->quote_suppression_threshold
;
354 dq
.is_flowed
= is_flowed_msg
;
355 dq
.indent_length
= (wrapit
&& !(flags
& FM_NOWRAP
)
356 && !(flags
& FM_NOINDENT
))
357 ? format_view_margin()[0]
359 dq
.saved_line
= &free_this
;
360 dq
.handlesp
= handlesp
;
361 dq
.do_color
= (!(flags
& FM_NOCOLOR
) && pico_usingcolor());
363 filters
[filtcnt
].filter
= gf_line_test
;
364 filters
[filtcnt
++].data
= gf_line_test_opt(delete_quotes
, &dq
);
368 if(F_OFF(F_QUELL_CHARSET_WARNING
, ps_global
)
369 && !(flags
& FM_NOEDITORIAL
)){
371 CONV_TABLE
*ct
= NULL
;
374 * Need editorial if message charset is not ascii
375 * and the display charset is not either the same
376 * as the message charset or UTF-8.
378 if(strucmp(charset
, "us-ascii")
379 && !((ps_global
->display_charmap
380 && !strucmp(charset
, ps_global
->display_charmap
))
381 || !strucmp("UTF-8", ps_global
->display_charmap
))){
384 * This is probably overkill. We're just using this
385 * conversion_table routine to get at the quality.
387 if(ps_global
->display_charmap
)
388 ct
= conversion_table(charset
, ps_global
->display_charmap
);
390 rv
= charset_editorial(charset
, msgno
, handlesp
, flags
,
391 ct
? ct
->quality
: CV_NO_TRANSLATE_POSSIBLE
,
398 fs_give((void **) &charset
);
401 /* Format editorial comment about unknown encoding */
402 if(att
->body
->encoding
> ENCQUOTEDPRINTABLE
){
405 snprintf(buf
, sizeof(buf
), ENCODING_DISCLAIMER
, body_encodings
[att
->body
->encoding
]);
407 if(!(format_editorial(buf
, column
, flags
, handlesp
, pc
) == NULL
408 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
412 err
= detach(ps_global
->mail_stream
, msgno
, att
->number
, 0L,
413 NULL
, pc
, filtcnt
? filters
: NULL
, 0);
415 delete_unused_handles(handlesp
);
418 fs_give((void **) &free_this
);
425 if(style
== QStatus
) {
426 q_status_message1(SM_ORDER
, 3, 4, "%.200s", err
);
427 } else if(style
== InLine
) {
428 char buftmp
[MAILTMPLEN
];
430 snprintf(buftmp
, sizeof(buftmp
), "%s",
431 att
->body
->description
? att
->body
->description
: "");
432 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s [Error: %s] %c%s%c%s%s",
434 att
->body
->description
? '\"' : ' ',
435 att
->body
->description
? buftmp
: "",
436 att
->body
->description
? '\"' : ' ',
438 if(!gf_puts(tmp_20k_buf
, pc
))
443 if(att
->body
->subtype
444 && (!strucmp(att
->body
->subtype
, "richtext")
445 || !strucmp(att
->body
->subtype
, "enriched"))
446 && !(flags
& FM_DISPLAY
)){
447 if(!gf_puts(NEWLINE
, pc
) || !gf_puts(NEWLINE
, pc
))
455 q_status_message1(SM_ORDER
, 3, 4, "Error writing message: %.200s",
456 error_description(errno
));
464 * Format editorial comment about charset mismatch
467 charset_editorial(char *charset
, long int msgno
, HANDLE_S
**handlesp
, int flags
,
468 int quality
, int width
, gf_io_t pc
)
470 char *p
, color
[64], buf
[2048];
474 snprintf(buf
, sizeof(buf
), CHARSET_DISCLAIMER_1
, charset
? charset
: "US-ASCII");
475 p
= &buf
[strlen(buf
)];
477 if(!(ps_global
->display_charmap
478 && strucmp(ps_global
->display_charmap
, "US-ASCII"))
480 && (flags
& FM_DISPLAY
) == FM_DISPLAY
){
481 h
= new_handle(handlesp
);
483 h
->h
.url
.path
= cpystr("x-alpine-help:h_config_char_set");
486 * Because this filter comes after the delete_quotes filter, we need
487 * to tell delete_quotes that this handle is used, so it won't
488 * delete it. This is a dangerous thing.
492 if(!(flags
& FM_NOCOLOR
)
493 && scroll_handle_start_color(color
, sizeof(color
), &n
)){
494 for(i
= 0; i
< n
; i
++)
495 if(p
-buf
< sizeof(buf
))
498 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
499 if(p
-buf
+1 < sizeof(buf
)){
505 if(p
-buf
+1 < sizeof(buf
)){
510 snprintf(p
+ 1, sizeof(buf
)-(p
+1-buf
), "%d", h
->key
);
511 if(p
-buf
< sizeof(buf
))
517 sstrncpy(&p
, CHARSET_DISCLAIMER_2
, sizeof(buf
)-(p
-buf
));
520 /* in case it was the current selection */
521 if(p
-buf
+1 < sizeof(buf
)){
526 if(scroll_handle_end_color(color
, sizeof(color
), &n
, 0)){
527 for(i
= 0; i
< n
; i
++)
528 if(p
-buf
< sizeof(buf
))
532 if(p
-buf
+1 < sizeof(buf
)){
538 if(p
-buf
< sizeof(buf
))
542 snprintf(p
, sizeof(buf
)-(p
-buf
), CHARSET_DISCLAIMER_3
,
543 ps_global
->display_charmap
? ps_global
->display_charmap
: "US-ASCII",
544 (quality
== CV_LOSES_SPECIAL_CHARS
) ? "special " : "");
546 buf
[sizeof(buf
)-1] = '\0';
548 return(format_editorial(buf
, width
, flags
, handlesp
, pc
) == NULL
549 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
));
556 * This one is a little more complicated because it comes late in the
557 * filtering sequence after colors have been added and url's hilited and
558 * so on. So if it wants to look at the beginning of the line it has to
559 * wade through some stuff done by the other filters first. This one is
560 * skipping the leading indent added by the viewer-margin stuff and leading
564 delete_quotes(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
568 int i
, lines
, not_a_quote
= 0;
571 dq
= (DELQ_S
*) local
;
575 if(dq
->lines
> 0 || dq
->lines
== Q_DEL_ALL
){
577 lines
= (dq
->lines
== Q_DEL_ALL
) ? 0 : dq
->lines
;
580 * First determine if this line is part of a quote.
583 /* skip over indent added by viewer-margin-left */
585 for(i
= dq
->indent_length
; i
> 0 && !not_a_quote
&& *lp
; i
--)
589 /* skip over leading tags */
591 && ((unsigned char) (*lp
) == (unsigned char) TAG_EMBED
)){
627 /* skip over whitespace */
629 while(isspace((unsigned char) *lp
))
632 /* check first character to see if it is a quote */
633 if(!not_a_quote
&& *lp
!= '>')
637 if(dq
->in_quote
> lines
+1 && !dq
->delete_all
){
639 COLOR_PAIR
*col
= NULL
;
640 char cestart
[2 * RGBLEN
+ 5];
641 char ceend
[2 * RGBLEN
+ 5];
644 * Display how many lines were hidden.
647 cestart
[0] = ceend
[0] = '\0';
649 && ps_global
->VAR_METAMSG_FORE_COLOR
650 && ps_global
->VAR_METAMSG_BACK_COLOR
651 && (col
= new_color_pair(ps_global
->VAR_METAMSG_FORE_COLOR
,
652 ps_global
->VAR_METAMSG_BACK_COLOR
)))
653 if(!pico_is_good_colorpair(col
))
654 free_color_pair(&col
);
657 strncpy(cestart
, color_embed(col
->fg
, col
->bg
), sizeof(cestart
));
658 cestart
[sizeof(cestart
)-1] = '\0';
659 strncpy(ceend
, color_embed(ps_global
->VAR_NORM_FORE_COLOR
,
660 ps_global
->VAR_NORM_BACK_COLOR
), sizeof(ceend
));
661 ceend
[sizeof(ceend
)-1] = '\0';
662 free_color_pair(&col
);
665 snprintf(tmp
, sizeof(tmp
),
666 "%s[ %s%d lines of quoted text hidden from view%s ]\r\n",
667 repeat_char(dq
->indent_length
, SPACE
),
668 cestart
, dq
->in_quote
- lines
, ceend
);
669 if(strlen(tmp
)-strlen(cestart
)-strlen(ceend
)-2 > ps_global
->ttyo
->screen_cols
){
671 snprintf(tmp
, sizeof(tmp
), "%s[ %s%d lines of quoted text hidden%s ]\r\n",
672 repeat_char(dq
->indent_length
, SPACE
),
673 cestart
, dq
->in_quote
- lines
, ceend
);
675 if(strlen(tmp
)-strlen(cestart
)-strlen(ceend
)-2 > ps_global
->ttyo
->screen_cols
){
676 snprintf(tmp
, sizeof(tmp
), "%s[ %s%d lines hidden%s ]\r\n",
677 repeat_char(dq
->indent_length
, SPACE
),
678 cestart
, dq
->in_quote
- lines
, ceend
);
680 if(strlen(tmp
)-strlen(cestart
)-strlen(ceend
)-2 > ps_global
->ttyo
->screen_cols
){
681 snprintf(tmp
, sizeof(tmp
), "%s[ %s%d hidden%s ]\r\n",
682 repeat_char(dq
->indent_length
, SPACE
),
683 cestart
, dq
->in_quote
- lines
, ceend
);
685 if(strlen(tmp
)-strlen(cestart
)-strlen(ceend
)-2 > ps_global
->ttyo
->screen_cols
){
686 snprintf(tmp
, sizeof(tmp
), "%s[...]\r\n",
687 repeat_char(dq
->indent_length
, SPACE
));
689 if(strlen(tmp
)-2 > ps_global
->ttyo
->screen_cols
){
690 snprintf(tmp
, sizeof(tmp
), "%s...\r\n",
691 repeat_char(dq
->indent_length
, SPACE
));
693 if(strlen(tmp
)-2 > ps_global
->ttyo
->screen_cols
){
694 snprintf(tmp
, sizeof(tmp
), "%s\r\n",
695 repeat_char(MIN(ps_global
->ttyo
->screen_cols
,3),
704 ins
= gf_line_test_new_ins(ins
, line
, tmp
, strlen(tmp
));
705 mark_handles_in_line(line
, dq
->handlesp
, 1);
706 ps_global
->some_quoting_was_suppressed
= 1;
708 else if(dq
->in_quote
== lines
+1
709 && dq
->saved_line
&& *dq
->saved_line
){
711 * We would have only had to delete a single line. Instead
712 * of deleting it, just show it.
714 ins
= gf_line_test_new_ins(ins
, line
,
716 strlen(*dq
->saved_line
));
717 mark_handles_in_line(*dq
->saved_line
, dq
->handlesp
, 1);
720 mark_handles_in_line(line
, dq
->handlesp
, 1);
722 if(dq
->saved_line
&& *dq
->saved_line
)
723 fs_give((void **) dq
->saved_line
);
728 dq
->in_quote
++; /* count it */
729 if(dq
->in_quote
> lines
){
731 * If the hidden part is a single line we'll show it instead.
733 if(dq
->in_quote
== lines
+1 && !dq
->delete_all
){
734 if(dq
->saved_line
&& *dq
->saved_line
)
735 fs_give((void **) dq
->saved_line
);
737 *dq
->saved_line
= fs_get(strlen(line
) + 3);
738 snprintf(*dq
->saved_line
, strlen(line
)+3, "%s\r\n", line
);
741 mark_handles_in_line(line
, dq
->handlesp
, 0);
742 /* skip it, at least for now */
746 mark_handles_in_line(line
, dq
->handlesp
, 1);
755 * This is a line-at-a-time filter that replaces incoming UTF-8 text with
759 translate_utf8_to_2022_jp(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
764 lpj
= (LOC_2022_JP
*) local
;
770 converted
= utf8_to_charset(line
, "ISO-2022-JP", lpj
? lpj
->report_err
: 0);
775 if(converted
&& converted
!= line
){
776 /* delete the old line and replace it with the translation */
778 ins
= gf_line_test_new_ins(ins
, line
, "", -((int) len
));
779 ins
= gf_line_test_new_ins(ins
, line
+len
, converted
, strlen(converted
));
780 fs_give((void **) &converted
);
789 * Replace quotes of nonflowed messages. This needs to happen
790 * towards the end of filtering because a lot of prior filters
791 * depend on "> ", such as quote coloring and suppression.
792 * Quotes are already colored here, so we have to account for
796 replace_quotes(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
798 char *lp
= line
, *prefix
= NULL
, *last_prefix
= NULL
;
799 int no_more_quotes
= 0, len
= 0, saw_quote
= 0;
801 if(ps_global
->VAR_QUOTE_REPLACE_STRING
){
802 get_pair(ps_global
->VAR_QUOTE_REPLACE_STRING
, &prefix
, &last_prefix
, 0, 0);
803 if(!prefix
&& last_prefix
){
804 prefix
= last_prefix
;
811 while(isspace((unsigned char)(*lp
)))
813 while(*lp
&& !no_more_quotes
){
817 ins
= gf_line_test_new_ins(ins
, lp
- 1, "", -2);
821 ins
= gf_line_test_new_ins(ins
, lp
- 1, "", -1);
823 ins
= gf_line_test_new_ins(ins
, lp
, prefix
, strlen(prefix
));
845 if(saw_quote
&& last_prefix
)
846 ins
= gf_line_test_new_ins(ins
, lp
- 1, last_prefix
, strlen(last_prefix
));
852 fs_give((void **)&prefix
);
854 fs_give((void **)&last_prefix
);
860 * We need to delete handles that we are removing from the displayed
861 * text. But there is a complication. Some handles appear at more than
862 * one location in the text. So we keep track of which handles we're
863 * actually using as we go, then at the end we delete the ones we
866 * Args line -- the text line, which includes tags
867 * handlesp -- handles pointer
868 * used -- If 1, set handles in this line to used
869 * if 0, leave handles in this line set as they already are
872 mark_handles_in_line(char *line
, HANDLE_S
**handlesp
, int used
)
879 if(!(line
&& handlesp
&& *handlesp
))
882 length
= strlen(line
);
884 /* mimic code in PutLine0n8b */
887 c
= (unsigned char) *line
++;
889 if(c
== (unsigned char) TAG_EMBED
&& length
){
895 length
-= *line
+ 1; /* key length plus length tag */
897 for(key
= 0, n
= *line
++; n
; n
--)
898 key
= (key
* 10) + (*line
++ - '0');
900 h
= get_handle(*handlesp
, key
);
902 h
->using_is_used
= 1;
904 * If it's already marked is_used, leave it marked
905 * that way. We only call this function with used = 0
906 * in order to set using_is_used.
908 h
->is_used
= (h
->is_used
|| used
) ? 1 : 0;
942 * parsed html risk state functions
945 clear_html_risk(void)