File Coverage

strip_html.c
Criterion Covered Total %
statement 127 161 78.8
branch 102 156 65.3
condition n/a
subroutine n/a
pod n/a
total 229 317 72.2


line stmt bran cond sub pod time code
1             #include
2             #include
3             #include
4              
5             #include "strip_html.h"
6              
7             #ifdef _MSC_VER
8             #define strcasecmp(a,b) stricmp(a,b)
9             #endif
10              
11             static int utf8_char_width(unsigned char * string);
12              
13             void
14 41           _strip_html( Stripper * stripper, char * raw, char * output, int is_utf8_p ) {
15 41           char * p_raw = raw;
16 41           char * raw_end = raw + strlen(raw);
17 41           char * p_output = output;
18             int width;
19              
20 41 50         if( stripper->o_debug ) {
21 0           printf( "[DEBUG] input string: %s\n", p_raw );
22             }
23              
24 3288 100         while( p_raw < raw_end ) {
25 3247 100         width = is_utf8_p ? utf8_char_width(p_raw) : 1;
26             // either a single char or a set of unicode code points
27              
28 3247 50         if( stripper->o_debug ) {
29 0 0         printf( "[DEBUG] char:%C w%i state:%c%c%c tag:%5s last:%c%c%c%c in:%c%c%c quote:%c ",
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
30 0           *p_raw,
31             width,
32 0           (stripper->f_closing ? 'C' : ' '),
33 0           (stripper->f_in_tag ? 'T' : ' '),
34 0           (stripper->f_full_tagname ? 'F' : ' '),
35 0           stripper->tagname,
36 0           (stripper->f_just_seen_tag ? 'T' : ' '),
37 0           (stripper->f_outputted_space ? 'S' : ' '),
38 0           (stripper->f_lastchar_slash ? '/' : ' '),
39 0           (stripper->f_lastchar_minus ? '-' : ' '),
40 0           (stripper->f_in_decl ? 'D' : ' '),
41 0           (stripper->f_in_comment ? 'C' : ' '),
42 0           (stripper->f_in_striptag ? 'X' : ' '),
43 0           (stripper->f_in_quote ? stripper->quote : ' ')
44             );
45             }
46              
47 3247 100         if( stripper->f_in_tag ) {
48             /* inside a tag */
49             /* check we don't know either the tagname, or that we're in a declaration */
50 723 100         if( !stripper->f_full_tagname && !stripper->f_in_decl ) {
    100          
51             /* if this is the first character, check if it's a '!'; if so, we're in a declaration */
52 858 100         if( stripper->p_tagname == stripper->tagname && *p_raw == '!' ) {
    100          
53 6           stripper->f_in_decl = 1;
54             }
55             /* then check if the first character is a '/', in which case, this is a closing tag */
56 423 100         else if( stripper->p_tagname == stripper->tagname && *p_raw == '/' ) {
    100          
57 47           stripper->f_closing = 1;
58             }
59             /* if the first character wasn't a '/', and we're in a stripped block,
60             * assume any previous '<' was a mathematical operator and reset */
61 376 100         else if( !stripper->f_closing && stripper->f_in_striptag && stripper->p_tagname == stripper->tagname && *p_raw != '/' ) {
    100          
    50          
    50          
62 3           stripper->f_in_tag = 0;
63 3           stripper->f_closing = 0;
64             /* within a stripped tags block (e.g. scripts), we only care about closing tags
65             * within normal tags, we care about both opening and closing tags */
66 373 100         } else if( !stripper->f_in_striptag || stripper->f_closing ) {
    50          
67             /* if we don't have the full tag name yet, add p_raw character unless it's whitespace, a '/', or a '>';
68             otherwise null pad the string and set the full tagname flag, and check the tagname against stripped ones.
69             also sanity check we haven't reached the array bounds, and truncate the tagname here if we have */
70 373 100         if( (!isspace( *p_raw ) && *p_raw != '/' && *p_raw != '>') &&
    50          
    100          
    50          
71 267           !( (stripper->p_tagname - stripper->tagname) == MAX_TAGNAMELENGTH ) ) {
72 267           *stripper->p_tagname++ = *p_raw;
73             } else {
74 106           *stripper->p_tagname = 0;
75 106           stripper->f_full_tagname = 1;
76             /* if we're in a stripped tag block, and this is a closing tag, check to see if it ends the stripped block */
77 106 100         if( stripper->f_in_striptag && stripper->f_closing ) {
    50          
78 15 100         if( strcasecmp( stripper->tagname, stripper->striptag ) == 0 ) {
79 7           stripper->f_in_striptag = 0;
80             }
81             /* if we're outside a stripped tag block, check if tagname represents a newline,
82             * then check tagname against stripped tag list */
83 98 50         } else if( !stripper->f_in_striptag && !stripper->f_closing ) {
    100          
84 59 100         if( strcasecmp( stripper->tagname, "p" ) ||
    50          
85 12           strcasecmp( stripper->tagname, "br" ) ) {
86 59 50         if( stripper->o_emit_newlines ) {
87 0 0         if( stripper->o_debug ) {
88 0           printf("NEWLINE ");
89             }
90 0           *p_output++ = '\n';
91 0           stripper->f_outputted_space = 1;
92             }
93             }
94             int i;
95 265 100         for( i = 0; i < stripper->numstriptags; i++ ) {
96 214 100         if( strcasecmp( stripper->tagname, stripper->o_striptags[i] ) == 0 ) {
97 8           stripper->f_in_striptag = 1;
98 8           strcpy( stripper->striptag, stripper->tagname );
99 8           break;
100             }
101             }
102             }
103 106           check_end( stripper, *p_raw );
104             }
105             }
106             }
107             /* we know the tagname, or that we're in a decl */
108             else {
109 294 100         if( stripper->f_in_quote ) {
110             /* inside a quote */
111             /* end of quote if p_raw character matches the opening quote character */
112 113 100         if( *p_raw == stripper->quote ) {
113 5           stripper->quote = 0;
114 113           stripper->f_in_quote = 0;
115             }
116             } else {
117             /* not in a quote */
118             /* check for quote characters, but not in a comment */
119 181 100         if( !stripper->f_in_comment &&
    50          
120 73 100         ( *p_raw == '\'' || *p_raw == '\"' ) ) {
121 5           stripper->f_in_quote = 1;
122 5           stripper->quote = *p_raw;
123             /* reset lastchar_* flags in case we have something perverse like '-"' or '/"' */
124 5           stripper->f_lastchar_minus = 0;
125 5           stripper->f_lastchar_slash = 0;
126             } else {
127 176 100         if( stripper->f_in_decl ) {
128             /* inside a declaration */
129 146 100         if( stripper->f_lastchar_minus ) {
130             /* last character was a minus, so if p_raw one is, then we're either entering or leaving a comment */
131 10 50         if( *p_raw == '-' ) {
132 10           stripper->f_in_comment = !stripper->f_in_comment;
133             }
134 10           stripper->f_lastchar_minus = 0;
135             } else {
136             /* if p_raw character is a minus, we might be starting a comment marker */
137 136 100         if( *p_raw == '-' ) {
138 10           stripper->f_lastchar_minus = 1;
139             }
140             }
141 146 100         if( !stripper->f_in_comment ) {
142 146           check_end( stripper, *p_raw );
143             }
144             } else {
145 723           check_end( stripper, *p_raw );
146             }
147             } /* quote character check */
148             } /* in quote check */
149             } /* full tagname check */
150             }
151             else {
152             /* not in a tag */
153             /* check for tag opening, and reset parameters if one has */
154 2524 100         if( *p_raw == '<' ) {
155 115           stripper->f_in_tag = 1;
156 115           stripper->tagname[0] = 0;
157 115           stripper->p_tagname = stripper->tagname;
158 115           stripper->f_full_tagname = 0;
159 115           stripper->f_closing = 0;
160 115           stripper->f_just_seen_tag = 1;
161             }
162             else {
163             /* copy to stripped provided we're not in a stripped block */
164 2409 100         if( !stripper->f_in_striptag ) {
165             /* only emit spaces if we're configured to do so (on by default) */
166 2066 50         if( stripper->o_emit_spaces ){
167             /* output a space in place of tags we have previously parsed,
168             and set a flag so we only do this once for every group of tags.
169             done here to prevent unnecessary trailing spaces */
170 2066 100         if( !isspace(*p_raw) &&
    100          
171             /* don't output a space if this character is one anyway */
172 1492 100         !stripper->f_outputted_space &&
173 1492           stripper->f_just_seen_tag ) {
174 20 50         if( stripper->o_debug ) {
175 0           printf("SPACE ");
176             }
177 20           *p_output++ = ' ';
178 20           stripper->f_outputted_space = 1;
179             }
180             }
181 2066           strncpy(p_output, p_raw, width);
182 2066 50         if( stripper->o_debug ) {
183 0           printf("CHAR %c", *p_raw);
184             }
185 2066           p_output += width;
186              
187             /* reset 'just seen tag' flag */
188 2066           stripper->f_just_seen_tag = 0;
189             /* reset 'outputted space' flag if character is not one */
190 2066 100         if (!isspace(*p_raw)) {
191 1779           stripper->f_outputted_space = 0;
192             } else {
193 287           stripper->f_outputted_space = 1;
194             }
195             }
196             }
197             } /* in tag check */
198 3247           p_raw += width;
199 3247 50         if( stripper->o_debug ) {
200 0           printf("\n");
201             }
202             } /* while loop */
203              
204 41           *p_output = 0;
205              
206 41 100         if (stripper->o_auto_reset) {
207 6           _reset( stripper );
208             }
209 41           }
210              
211             static int
212 1873           utf8_char_width(unsigned char * string) {
213 1873 100         if (~*string & 128) { // 0xxxxxxx
214 568           return 1;
215 1305 50         } else if ((*string & 192) == 128) { // 10xxxxxx
216             /* latter bytes of a multibyte utf8 char
217             XXX this should never happen in practice XXX
218             but we account for it anyway */
219 0           int width = 1;
220 0           char * p = string;
221 0 0         while ((*p++ & 192) == 128) {
222 0           width++;
223             }
224 0           return width;
225 1305 100         } else if ((*string & 224) == 192) { // 110xxxxx
226 1302           return 2;
227 3 50         } else if ((*string & 240) == 224) { // 1110xxxx
228 3           return 3;
229 0 0         } else if ((*string & 248) == 240) { // 11110xxx
230 0           return 4;
231             /* part of original utf8 spec, but not used
232             } else if ((*string & 252) == 248) { // 111110xx
233             return 5;
234             } else if ((*string & 254) == 252) { // 1111110x
235             return 6;
236             */
237             } else {
238 0           printf( "[WARN] invalid utf8 char ord=%i\n", *string );
239 0           return 1;
240             }
241             }
242              
243             void
244 48           _reset( Stripper * stripper ) {
245 48           stripper->f_in_tag = 0;
246 48           stripper->f_closing = 0;
247 48           stripper->f_lastchar_slash = 0;
248 48           stripper->f_full_tagname = 0;
249             /* hack to stop a space being output on strings starting with a tag */
250 48           stripper->f_outputted_space = 1;
251 48           stripper->f_just_seen_tag = 0;
252              
253 48           stripper->f_in_quote = 0;
254              
255 48           stripper->f_in_decl = 0;
256 48           stripper->f_in_comment = 0;
257 48           stripper->f_lastchar_minus = 0;
258              
259 48           stripper->f_in_striptag = 0;
260              
261 48           memset(stripper->tagname, 0, sizeof(stripper->tagname));
262 48           }
263              
264             void
265 20           clear_striptags( Stripper * stripper ) {
266 20           strcpy(stripper->o_striptags[0], "");
267 20           stripper->numstriptags = 0;
268 20           }
269              
270             void
271 75           add_striptag( Stripper * stripper, char * striptag ) {
272 75 50         if( stripper->numstriptags < MAX_STRIPTAGS-1 ) {
273 75           strcpy(stripper->o_striptags[stripper->numstriptags++], striptag);
274             } else {
275 0           fprintf( stderr, "Cannot have more than %i strip tags", MAX_STRIPTAGS );
276             }
277 75           }
278              
279             void
280 174           check_end( Stripper * stripper, char end ) {
281             /* if p_raw character is a slash, may be a closed tag */
282 174 100         if( end == '/' ) {
283 1           stripper->f_lastchar_slash = 1;
284             } else {
285             /* if the p_raw character is a '>', then the tag has ended */
286             /* slight hack to deal with mathematical characters in script tags:
287             * if we're in a stripped block, and this is a closing tag, spaces
288             * will also end the tag, since we only want it for comparison with
289             * the opening one */
290 173 100         if( (end == '>') ||
    100          
291 6 50         (stripper->f_in_striptag && stripper->f_closing && isspace(end)) ) {
    0          
292 112           stripper->f_in_quote = 0;
293 112           stripper->f_in_comment = 0;
294 112           stripper->f_in_decl = 0;
295 112           stripper->f_in_tag = 0;
296 112           stripper->f_closing = 0;
297             /* Do not start a stripped tag block if the tag is a closed one, e.g. '