1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: text.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-2016 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 /*======================================================================
22 Implements message text fetching and decoding
27 #include "../pith/headers.h"
28 #include "../pith/text.h"
29 #include "../pith/state.h"
30 #include "../pith/conf.h"
31 #include "../pith/handle.h"
32 #include "../pith/margin.h"
33 #include "../pith/editorial.h"
34 #include "../pith/color.h"
35 #include "../pith/filter.h"
36 #include "../pith/charset.h"
37 #include "../pith/status.h"
38 #include "../pith/mailview.h"
39 #include "../pith/mimedesc.h"
40 #include "../pith/detach.h"
43 /* internal prototypes */
44 int charset_editorial(char *, long, HANDLE_S
**, int, int, int, gf_io_t
);
45 int replace_quotes(long, char *, LT_INS_S
**, void *);
46 void mark_handles_in_line(char *, HANDLE_S
**, int);
47 void clear_html_risk(void);
48 void set_html_risk(void);
49 int get_html_risk(void);
52 #define CHARSET_DISCLAIMER_1 \
53 "The following text is in the \"%.40s\" character set.\015\012Your "
54 #define CHARSET_DISCLAIMER_2 "display is set"
55 #define CHARSET_DISCLAIMER_3 \
56 " for the \"%.40s\" character set. \015\012Some %.40scharacters may be displayed incorrectly."
57 #define ENCODING_DISCLAIMER \
58 "The following text contains the unknown encoding type \"%.20s\". \015\012Some or all of the text may be displayed incorrectly."
59 #define HTML_WARNING \
60 "The following HTML text may contain deceptive links. Carefully note the destination URL before visiting any links."
64 * HTML risk state holder.
66 static int _html_risk
;
70 /*----------------------------------------------------------------------
71 Handle fetching and filtering a text message segment to be displayed
72 by scrolltool or printed or exported or piped.
74 Args: att -- segment to fetch
75 msgno -- message number segment is a part of
76 pc -- function to write characters from segment with
77 style -- Indicates special handling for error messages
78 flags -- Indicates special necessary handling
80 Returns: 1 if errors encountered, 0 if everything went A-OK
84 decode_text(ATTACH_S
*att
,
91 FILTLIST_S filters
[14];
93 int filtcnt
= 0, error_found
= 0, column
, wrapit
;
94 int is_in_sig
= OUT_SIG_BLOCK
;
95 int is_flowed_msg
= 0;
99 char *free_this
= NULL
;
100 STORE_S
*warn_so
= NULL
;
104 column
= (flags
& FM_DISPLAY
) ? ps_global
->ttyo
->screen_cols
: 80;
106 if(!(flags
& FM_DISPLAY
))
107 flags
|= FM_NOINDENT
;
111 memset(filters
, 0, sizeof(filters
));
113 /* charset the body part is in */
114 charset
= parameter_val(att
->body
->parameter
, "charset");
116 /* determined if it's flowed, affects wrapping and quote coloring */
117 if(att
->body
->type
== TYPETEXT
118 && !strucmp(att
->body
->subtype
, "plain")
119 && (parmval
= parameter_val(att
->body
->parameter
, "format"))){
120 if(!strucmp(parmval
, "flowed"))
122 fs_give((void **) &parmval
);
125 if((parmval
= parameter_val(att
->body
->parameter
, "delsp")) != NULL
){
126 if(!strucmp(parmval
, "yes"))
129 fs_give((void **) &parmval
);
134 if(!ps_global
->pass_ctrl_chars
){
135 filters
[filtcnt
++].filter
= gf_escape_filter
;
136 filters
[filtcnt
].filter
= gf_control_filter
;
139 filters
[filtcnt
++].data
= gf_control_filter_opt(&filt_only_c0
);
142 if(flags
& FM_DISPLAY
)
143 filters
[filtcnt
++].filter
= gf_tag_filter
;
146 * if it's just plain old text, look for url's
148 if(!(att
->body
->subtype
&& strucmp(att
->body
->subtype
, "plain"))){
149 struct variable
*vars
= ps_global
->vars
;
151 if((F_ON(F_VIEW_SEL_URL
, ps_global
)
152 || F_ON(F_VIEW_SEL_URL_HOST
, ps_global
)
153 || F_ON(F_SCAN_ADDR
, ps_global
))
157 * The url_hilite filter really ought to come
158 * after flowing, because flowing with the DelSp=yes parameter
159 * can reassemble broken urls back into identifiable urls.
160 * We add the preflow filter to do only the reassembly part
161 * of the flowing so that we can spot the urls.
162 * At this time (2005-03-29) we know that Apple Mail does
163 * send mail like this sometimes. This filter removes the
164 * sequence SP CRLF if that seems safe.
166 if(ps_global
->full_header
!= 2 && is_delsp_yes
)
167 filters
[filtcnt
++].filter
= gf_preflow
;
169 filters
[filtcnt
].filter
= gf_line_test
;
170 filters
[filtcnt
++].data
= gf_line_test_opt(url_hilite
,
171 gf_url_hilite_opt(&uh
,handlesp
,0));
175 * First, paint the signature.
176 * Disclaimers noted below for coloring quotes apply here as well.
178 if((flags
& FM_DISPLAY
)
179 && !(flags
& FM_NOCOLOR
)
181 && VAR_SIGNATURE_FORE_COLOR
182 && VAR_SIGNATURE_BACK_COLOR
){
183 filters
[filtcnt
].filter
= gf_line_test
;
184 filters
[filtcnt
++].data
= gf_line_test_opt(color_signature
,
189 * Gotta be careful with this. The color_a_quote filter adds color
190 * to the beginning and end of the line. This will break some
191 * line_test-style filters which come after it. For example, if they
192 * are looking for something at the start of a line (like color_a_quote
193 * itself). I guess we could fix that by ignoring tags at the
194 * beginning of the line when doing the search.
196 if((flags
& FM_DISPLAY
)
197 && !(flags
& FM_NOCOLOR
)
199 && VAR_QUOTE1_FORE_COLOR
200 && VAR_QUOTE1_BACK_COLOR
){
201 filters
[filtcnt
].filter
= gf_line_test
;
202 filters
[filtcnt
++].data
= gf_line_test_opt(color_a_quote
,
206 else if(!strucmp(att
->body
->subtype
, "richtext")){
209 plain_opt
= !(flags
&FM_DISPLAY
);
211 /* maybe strip everything! */
212 filters
[filtcnt
].filter
= gf_rich2plain
;
213 filters
[filtcnt
++].data
= gf_rich2plain_opt(&plain_opt
);
214 /* width to use for file or printer */
218 else if(!strucmp(att
->body
->subtype
, "enriched")){
221 plain_opt
= !(flags
&FM_DISPLAY
);
223 filters
[filtcnt
].filter
= gf_enriched2plain
;
224 filters
[filtcnt
++].data
= gf_enriched2plain_opt(&plain_opt
);
225 /* width to use for file or printer */
229 else if(!strucmp(att
->body
->subtype
, "html") && ps_global
->full_header
< 2){
230 /*BUG: sniff the params for "version=2.0" ala draft-ietf-html-spec-01 */
235 if(flags
& FM_DISPLAY
){
238 if(handlesp
){ /* pass on handles awareness */
239 opts
|= GFHP_HANDLES
;
241 if(F_OFF(F_QUELL_HOST_AFTER_URL
, ps_global
) && !(flags
& FM_HIDESERVER
))
242 opts
|= GFHP_SHOW_SERVER
;
245 if(!(flags
& FM_NOEDITORIAL
)){
246 warn_so
= so_get(CharStar
, NULL
, EDIT_ACCESS
);
247 gf_set_so_writec(&warn_pc
, warn_so
);
248 format_editorial(HTML_WARNING
, column
, flags
, handlesp
, warn_pc
);
249 gf_clear_so_writec(warn_so
);
250 so_puts(warn_so
, "\015\012");
251 so_writec('\0', warn_so
);
255 opts
|= GFHP_STRIPPED
; /* don't embed anything! */
257 if(flags
& FM_NOHTMLREL
)
258 opts
|= GFHP_NO_RELATIVE
;
260 if(flags
& FM_HTMLRELATED
)
261 opts
|= GFHP_RELATED_CONTENT
;
266 if(flags
& FM_HTMLIMAGES
)
267 opts
|= GFHP_HTML_IMAGES
;
269 wrapit
= 0; /* wrap already handled! */
270 filters
[filtcnt
].filter
= gf_html2plain
;
271 filters
[filtcnt
++].data
= gf_html2plain_opt(NULL
, column
,
272 (flags
& (FM_NOINDENT
| FM_HTML
))
274 : format_view_margin(),
275 handlesp
, set_html_risk
, opts
);
278 filters
[filtcnt
].filter
= gf_prepend_editorial
;
279 filters
[filtcnt
++].data
= gf_prepend_editorial_opt(get_html_risk
,
280 (char *) so_text(warn_so
));
285 * If the message is not flowed, we do the quote suppression before
286 * the wrapping, because the wrapping does not preserve the quote
287 * characters at the beginnings of the lines in that case.
288 * Otherwise, we defer until after the wrapping.
290 * Also, this is a good place to do quote-replacement on nonflowed
291 * messages because no other filters depend on the "> ".
292 * Quote-replacement is easier in the flowed case and occurs
293 * automatically in the flowed wrapping filter.
296 && ps_global
->full_header
== 0
297 && !(att
->body
->subtype
&& strucmp(att
->body
->subtype
, "plain"))
298 && (flags
& FM_DISPLAY
)){
299 if(ps_global
->quote_suppression_threshold
!= 0){
300 memset(&dq
, 0, sizeof(dq
));
301 dq
.lines
= ps_global
->quote_suppression_threshold
;
302 dq
.is_flowed
= is_flowed_msg
;
303 dq
.indent_length
= 0; /* indent didn't happen yet */
304 dq
.saved_line
= &free_this
;
305 dq
.handlesp
= handlesp
;
306 dq
.do_color
= (!(flags
& FM_NOCOLOR
) && pico_usingcolor());
308 filters
[filtcnt
].filter
= gf_line_test
;
309 filters
[filtcnt
++].data
= gf_line_test_opt(delete_quotes
, &dq
);
311 if(ps_global
->VAR_QUOTE_REPLACE_STRING
312 && F_ON(F_QUOTE_REPLACE_NOFLOW
, ps_global
)){
313 filters
[filtcnt
].filter
= gf_line_test
;
314 filters
[filtcnt
++].data
= gf_line_test_opt(replace_quotes
, NULL
);
318 if(wrapit
&& !(flags
& FM_NOWRAP
)){
319 int wrapflags
= (flags
& FM_DISPLAY
) ? (GFW_HANDLES
|GFW_SOFTHYPHEN
)
322 if(flags
& FM_DISPLAY
323 && !(flags
& FM_NOCOLOR
)
324 && pico_usingcolor())
325 wrapflags
|= GFW_USECOLOR
;
327 /* text/flowed (RFC 2646 + draft)? */
328 if(ps_global
->full_header
!= 2 && is_flowed_msg
){
329 wrapflags
|= GFW_FLOWED
;
332 wrapflags
|= GFW_DELSP
;
335 filters
[filtcnt
].filter
= gf_wrap
;
336 filters
[filtcnt
++].data
= gf_wrap_filter_opt(wrapit
, column
,
337 (flags
& FM_NOINDENT
)
339 : format_view_margin(),
344 * This has to come after wrapping has happened because the user tells
345 * us how many quoted lines to display, and we want that number to be
346 * the wrapped lines, not the pre-wrapped lines. We do it before the
347 * wrapping if the message is not flowed, because the wrapping does not
348 * preserve the quote characters at the beginnings of the lines in that
352 && ps_global
->full_header
== 0
353 && !(att
->body
->subtype
&& strucmp(att
->body
->subtype
, "plain"))
354 && (flags
& FM_DISPLAY
)
355 && ps_global
->quote_suppression_threshold
!= 0){
356 memset(&dq
, 0, sizeof(dq
));
357 dq
.lines
= ps_global
->quote_suppression_threshold
;
358 dq
.is_flowed
= is_flowed_msg
;
359 dq
.indent_length
= (wrapit
&& !(flags
& FM_NOWRAP
)
360 && !(flags
& FM_NOINDENT
))
361 ? format_view_margin()[0]
363 dq
.saved_line
= &free_this
;
364 dq
.handlesp
= handlesp
;
365 dq
.do_color
= (!(flags
& FM_NOCOLOR
) && pico_usingcolor());
367 filters
[filtcnt
].filter
= gf_line_test
;
368 filters
[filtcnt
++].data
= gf_line_test_opt(delete_quotes
, &dq
);
372 if(F_OFF(F_QUELL_CHARSET_WARNING
, ps_global
)
373 && !(flags
& FM_NOEDITORIAL
)){
375 CONV_TABLE
*ct
= NULL
;
378 * Need editorial if message charset is not ascii
379 * and the display charset is not either the same
380 * as the message charset or UTF-8.
382 if(strucmp(charset
, "us-ascii")
383 && !((ps_global
->display_charmap
384 && !strucmp(charset
, ps_global
->display_charmap
))
385 || !strucmp("UTF-8", ps_global
->display_charmap
))){
388 * This is probably overkill. We're just using this
389 * conversion_table routine to get at the quality.
391 if(ps_global
->display_charmap
)
392 ct
= conversion_table(charset
, ps_global
->display_charmap
);
394 rv
= charset_editorial(charset
, msgno
, handlesp
, flags
,
395 ct
? ct
->quality
: CV_NO_TRANSLATE_POSSIBLE
,
402 fs_give((void **) &charset
);
405 /* Format editorial comment about unknown encoding */
406 if(att
->body
->encoding
> ENCQUOTEDPRINTABLE
){
409 snprintf(buf
, sizeof(buf
), ENCODING_DISCLAIMER
, body_encodings
[att
->body
->encoding
]);
411 if(!(format_editorial(buf
, column
, flags
, handlesp
, pc
) == NULL
412 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
416 err
= detach(ps_global
->mail_stream
, msgno
, att
->number
, 0L,
417 NULL
, pc
, filtcnt
? filters
: NULL
, 0);
419 delete_unused_handles(handlesp
);
422 fs_give((void **) &free_this
);
429 if(style
== QStatus
) {
430 q_status_message1(SM_ORDER
, 3, 4, "%.200s", err
);
431 } else if(style
== InLine
) {
432 char buftmp
[MAILTMPLEN
];
434 snprintf(buftmp
, sizeof(buftmp
), "%s",
435 att
->body
->description
? att
->body
->description
: "");
436 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s [Error: %s] %c%s%c%s%s",
438 att
->body
->description
? '\"' : ' ',
439 att
->body
->description
? buftmp
: "",
440 att
->body
->description
? '\"' : ' ',
442 if(!gf_puts(tmp_20k_buf
, pc
))
447 if(att
->body
->subtype
448 && (!strucmp(att
->body
->subtype
, "richtext")
449 || !strucmp(att
->body
->subtype
, "enriched"))
450 && !(flags
& FM_DISPLAY
)){
451 if(!gf_puts(NEWLINE
, pc
) || !gf_puts(NEWLINE
, pc
))
459 q_status_message1(SM_ORDER
, 3, 4, "Error writing message: %.200s",
460 error_description(errno
));
468 * Format editorial comment about charset mismatch
471 charset_editorial(char *charset
, long int msgno
, HANDLE_S
**handlesp
, int flags
,
472 int quality
, int width
, gf_io_t pc
)
474 char *p
, color
[64], buf
[2048];
478 snprintf(buf
, sizeof(buf
), CHARSET_DISCLAIMER_1
, charset
? charset
: "US-ASCII");
479 p
= &buf
[strlen(buf
)];
481 if(!(ps_global
->display_charmap
482 && strucmp(ps_global
->display_charmap
, "US-ASCII"))
484 && (flags
& FM_DISPLAY
) == FM_DISPLAY
){
485 h
= new_handle(handlesp
);
487 h
->h
.url
.path
= cpystr("x-alpine-help:h_config_char_set");
490 * Because this filter comes after the delete_quotes filter, we need
491 * to tell delete_quotes that this handle is used, so it won't
492 * delete it. This is a dangerous thing.
496 if(!(flags
& FM_NOCOLOR
)
497 && scroll_handle_start_color(color
, sizeof(color
), &n
)){
498 for(i
= 0; i
< n
; i
++)
499 if(p
-buf
< sizeof(buf
))
502 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
503 if(p
-buf
+1 < sizeof(buf
)){
509 if(p
-buf
+1 < sizeof(buf
)){
514 snprintf(p
+ 1, sizeof(buf
)-(p
+1-buf
), "%d", h
->key
);
515 if(p
-buf
< sizeof(buf
))
521 sstrncpy(&p
, CHARSET_DISCLAIMER_2
, sizeof(buf
)-(p
-buf
));
524 /* in case it was the current selection */
525 if(p
-buf
+1 < sizeof(buf
)){
530 if(scroll_handle_end_color(color
, sizeof(color
), &n
, 0)){
531 for(i
= 0; i
< n
; i
++)
532 if(p
-buf
< sizeof(buf
))
536 if(p
-buf
+1 < sizeof(buf
)){
542 if(p
-buf
< sizeof(buf
))
546 snprintf(p
, sizeof(buf
)-(p
-buf
), CHARSET_DISCLAIMER_3
,
547 ps_global
->display_charmap
? ps_global
->display_charmap
: "US-ASCII",
548 (quality
== CV_LOSES_SPECIAL_CHARS
) ? "special " : "");
550 buf
[sizeof(buf
)-1] = '\0';
552 return(format_editorial(buf
, width
, flags
, handlesp
, pc
) == NULL
553 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
));
560 * This one is a little more complicated because it comes late in the
561 * filtering sequence after colors have been added and url's hilited and
562 * so on. So if it wants to look at the beginning of the line it has to
563 * wade through some stuff done by the other filters first. This one is
564 * skipping the leading indent added by the viewer-margin stuff and leading
568 delete_quotes(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
572 int i
, lines
, not_a_quote
= 0;
575 dq
= (DELQ_S
*) local
;
579 if(dq
->lines
> 0 || dq
->lines
== Q_DEL_ALL
){
581 lines
= (dq
->lines
== Q_DEL_ALL
) ? 0 : dq
->lines
;
584 * First determine if this line is part of a quote.
587 /* skip over indent added by viewer-margin-left */
589 for(i
= dq
->indent_length
; i
> 0 && !not_a_quote
&& *lp
; i
--)
593 /* skip over leading tags */
595 && ((unsigned char) (*lp
) == (unsigned char) TAG_EMBED
)){
631 /* skip over whitespace */
633 while(isspace((unsigned char) *lp
))
636 /* check first character to see if it is a quote */
637 if(!not_a_quote
&& *lp
!= '>')
641 if(dq
->in_quote
> lines
+1 && !dq
->delete_all
){
643 COLOR_PAIR
*col
= NULL
;
644 char cestart
[2 * RGBLEN
+ 5];
645 char ceend
[2 * RGBLEN
+ 5];
648 * Display how many lines were hidden.
651 cestart
[0] = ceend
[0] = '\0';
653 && ps_global
->VAR_METAMSG_FORE_COLOR
654 && ps_global
->VAR_METAMSG_BACK_COLOR
655 && (col
= new_color_pair(ps_global
->VAR_METAMSG_FORE_COLOR
,
656 ps_global
->VAR_METAMSG_BACK_COLOR
)))
657 if(!pico_is_good_colorpair(col
))
658 free_color_pair(&col
);
661 strncpy(cestart
, color_embed(col
->fg
, col
->bg
), sizeof(cestart
));
662 cestart
[sizeof(cestart
)-1] = '\0';
663 strncpy(ceend
, color_embed(ps_global
->VAR_NORM_FORE_COLOR
,
664 ps_global
->VAR_NORM_BACK_COLOR
), sizeof(ceend
));
665 ceend
[sizeof(ceend
)-1] = '\0';
666 free_color_pair(&col
);
669 snprintf(tmp
, sizeof(tmp
),
670 "%s[ %s%d lines of quoted text hidden from view%s ]\r\n",
671 repeat_char(dq
->indent_length
, SPACE
),
672 cestart
, dq
->in_quote
- lines
, ceend
);
673 if(strlen(tmp
)-strlen(cestart
)-strlen(ceend
)-2 > ps_global
->ttyo
->screen_cols
){
675 snprintf(tmp
, sizeof(tmp
), "%s[ %s%d lines of quoted text hidden%s ]\r\n",
676 repeat_char(dq
->indent_length
, SPACE
),
677 cestart
, dq
->in_quote
- lines
, ceend
);
679 if(strlen(tmp
)-strlen(cestart
)-strlen(ceend
)-2 > ps_global
->ttyo
->screen_cols
){
680 snprintf(tmp
, sizeof(tmp
), "%s[ %s%d lines hidden%s ]\r\n",
681 repeat_char(dq
->indent_length
, SPACE
),
682 cestart
, dq
->in_quote
- lines
, ceend
);
684 if(strlen(tmp
)-strlen(cestart
)-strlen(ceend
)-2 > ps_global
->ttyo
->screen_cols
){
685 snprintf(tmp
, sizeof(tmp
), "%s[ %s%d hidden%s ]\r\n",
686 repeat_char(dq
->indent_length
, SPACE
),
687 cestart
, dq
->in_quote
- lines
, ceend
);
689 if(strlen(tmp
)-strlen(cestart
)-strlen(ceend
)-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(dq
->indent_length
, SPACE
));
697 if(strlen(tmp
)-2 > ps_global
->ttyo
->screen_cols
){
698 snprintf(tmp
, sizeof(tmp
), "%s\r\n",
699 repeat_char(MIN(ps_global
->ttyo
->screen_cols
,3),
708 ins
= gf_line_test_new_ins(ins
, line
, tmp
, strlen(tmp
));
709 mark_handles_in_line(line
, dq
->handlesp
, 1);
710 ps_global
->some_quoting_was_suppressed
= 1;
712 else if(dq
->in_quote
== lines
+1
713 && dq
->saved_line
&& *dq
->saved_line
){
715 * We would have only had to delete a single line. Instead
716 * of deleting it, just show it.
718 ins
= gf_line_test_new_ins(ins
, line
,
720 strlen(*dq
->saved_line
));
721 mark_handles_in_line(*dq
->saved_line
, dq
->handlesp
, 1);
724 mark_handles_in_line(line
, dq
->handlesp
, 1);
726 if(dq
->saved_line
&& *dq
->saved_line
)
727 fs_give((void **) dq
->saved_line
);
732 dq
->in_quote
++; /* count it */
733 if(dq
->in_quote
> lines
){
735 * If the hidden part is a single line we'll show it instead.
737 if(dq
->in_quote
== lines
+1 && !dq
->delete_all
){
738 if(dq
->saved_line
&& *dq
->saved_line
)
739 fs_give((void **) dq
->saved_line
);
741 *dq
->saved_line
= fs_get(strlen(line
) + 3);
742 snprintf(*dq
->saved_line
, strlen(line
)+3, "%s\r\n", line
);
745 mark_handles_in_line(line
, dq
->handlesp
, 0);
746 /* skip it, at least for now */
750 mark_handles_in_line(line
, dq
->handlesp
, 1);
759 * This is a line-at-a-time filter that replaces incoming UTF-8 text with
763 translate_utf8_to_2022_jp(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
768 lpj
= (LOC_2022_JP
*) local
;
774 converted
= utf8_to_charset(line
, "ISO-2022-JP", lpj
? lpj
->report_err
: 0);
779 if(converted
&& converted
!= line
){
780 /* delete the old line and replace it with the translation */
782 ins
= gf_line_test_new_ins(ins
, line
, "", -((int) len
));
783 ins
= gf_line_test_new_ins(ins
, line
+len
, converted
, strlen(converted
));
784 fs_give((void **) &converted
);
793 * Replace quotes of nonflowed messages. This needs to happen
794 * towards the end of filtering because a lot of prior filters
795 * depend on "> ", such as quote coloring and suppression.
796 * Quotes are already colored here, so we have to account for
800 replace_quotes(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
802 char *lp
= line
, *prefix
= NULL
, *last_prefix
= NULL
;
803 int no_more_quotes
= 0, len
, saw_quote
= 0;
805 if(ps_global
->VAR_QUOTE_REPLACE_STRING
){
806 get_pair(ps_global
->VAR_QUOTE_REPLACE_STRING
, &prefix
, &last_prefix
, 0, 0);
807 if(!prefix
&& last_prefix
){
808 prefix
= last_prefix
;
815 while(isspace((unsigned char)(*lp
)))
817 while(*lp
&& !no_more_quotes
){
821 ins
= gf_line_test_new_ins(ins
, lp
- 1, "", -2);
825 ins
= gf_line_test_new_ins(ins
, lp
- 1, "", -1);
827 ins
= gf_line_test_new_ins(ins
, lp
, prefix
, strlen(prefix
));
849 if(saw_quote
&& last_prefix
)
850 ins
= gf_line_test_new_ins(ins
, lp
- 1, last_prefix
, strlen(last_prefix
));
856 fs_give((void **)&prefix
);
858 fs_give((void **)&last_prefix
);
864 * We need to delete handles that we are removing from the displayed
865 * text. But there is a complication. Some handles appear at more than
866 * one location in the text. So we keep track of which handles we're
867 * actually using as we go, then at the end we delete the ones we
870 * Args line -- the text line, which includes tags
871 * handlesp -- handles pointer
872 * used -- If 1, set handles in this line to used
873 * if 0, leave handles in this line set as they already are
876 mark_handles_in_line(char *line
, HANDLE_S
**handlesp
, int used
)
883 if(!(line
&& handlesp
&& *handlesp
))
886 length
= strlen(line
);
888 /* mimic code in PutLine0n8b */
891 c
= (unsigned char) *line
++;
893 if(c
== (unsigned char) TAG_EMBED
&& length
){
899 length
-= *line
+ 1; /* key length plus length tag */
901 for(key
= 0, n
= *line
++; n
; n
--)
902 key
= (key
* 10) + (*line
++ - '0');
904 h
= get_handle(*handlesp
, key
);
906 h
->using_is_used
= 1;
908 * If it's already marked is_used, leave it marked
909 * that way. We only call this function with used = 0
910 * in order to set using_is_used.
912 h
->is_used
= (h
->is_used
|| used
) ? 1 : 0;
946 * parsed html risk state functions
949 clear_html_risk(void)