HTMLParser, verbose setting
[metaproxy-moved-to-github.git] / src / html_parser.cpp
1 /* This file is part of Metaproxy.
2    Copyright (C) 2005-2013 Index Data
3
4 Metaproxy is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8
9 Metaproxy is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17 */
18
19 #include "config.hpp"
20 #include "html_parser.hpp"
21
22 #include <assert.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <ctype.h>
26 #include <stdio.h>
27
28 #define SPACECHR " \t\r\n\f"
29
30
31 namespace metaproxy_1 {
32     class HTMLParser::Rep {
33         friend class HTMLParser;
34     public:
35         void parse_str(HTMLParserEvent &event, const char *cp);
36         void tagText(HTMLParserEvent &event,
37                      const char *text_start, const char *text_end);
38         int tagEnd(HTMLParserEvent &event,
39                    const char *tag, int tag_len, const char *cp);
40         int tagStart(HTMLParserEvent &event,
41                      int *tag_len, const char *cp, const char which);
42         int tagAttrs(HTMLParserEvent &event,
43                      const char *name, int len,
44                      const char *cp);
45         Rep();
46         ~Rep();
47         int m_verbose;
48     };
49 }
50
51 namespace mp = metaproxy_1;
52
53 mp::HTMLParser::Rep::Rep()
54 {
55     m_verbose = 0;
56 }
57
58 mp::HTMLParser::Rep::~Rep()
59 {
60 }
61
62 mp::HTMLParser::HTMLParser() : m_p(new Rep)
63 {
64 }
65
66 mp::HTMLParser::~HTMLParser()
67 {
68 }
69
70 void mp::HTMLParser::set_verbose(int v)
71 {
72     m_p->m_verbose = v;
73 }
74
75
76 void mp::HTMLParser::parse(mp::HTMLParserEvent & event, const char *str) const
77 {
78     m_p->parse_str(event, str);
79 }
80
81 static int skipSpace(const char *cp)
82 {
83     int i = 0;
84     while (cp[i] && strchr(SPACECHR, cp[i]))
85         i++;
86     return i;
87 }
88
89 static int skipName(const char *cp)
90 {
91     int i;
92     for (i = 0; cp[i] && !strchr(SPACECHR "/>=", cp[i]); i++)
93         ;
94     return i;
95 }
96
97 static int skipAttribute(const char *cp, int *attr_len,
98                          const char **value, int *val_len)
99 {
100     int i = skipName(cp);
101     *attr_len = i;
102     *value = NULL;
103     if (!i)
104         return skipSpace(cp);
105     i += skipSpace(cp + i);
106     if (cp[i] == '=')
107     {
108         int v0, v1;
109         i++;
110         i += skipSpace(cp + i);
111         if (cp[i] == '\"' || cp[i] == '\'')
112         {
113             char tr = cp[i];
114             v0 = ++i;
115             while (cp[i] != tr && cp[i])
116                 i++;
117             v1 = i;
118             if (cp[i])
119                 i++;
120         }
121         else
122         {
123             v0 = i;
124             while (cp[i] && !strchr(SPACECHR ">", cp[i]))
125                 i++;
126             v1 = i;
127         }
128         *value = cp + v0;
129         *val_len = v1 - v0;
130     }
131     i += skipSpace(cp + i);
132     return i;
133 }
134
135 int mp::HTMLParser::Rep::tagAttrs(HTMLParserEvent &event,
136                                   const char *name, int len,
137                                   const char *cp)
138 {
139     int i = skipSpace(cp);
140     while (cp[i] && cp[i] != '>' && cp[i] != '/')
141     {
142         const char *attr_name = cp + i;
143         int attr_len;
144         const char *value;
145         int val_len;
146         int nor = skipAttribute(cp+i, &attr_len, &value, &val_len);
147         i += nor;
148         if (nor)
149         {
150             if (m_verbose)
151                 printf ("------ attr %.*s=%.*s\n", attr_len, attr_name,
152                         val_len, value);
153             event.attribute(name, len, attr_name, attr_len, value, val_len);
154         }
155         else
156         {
157             i++;
158         }
159     }
160     return i;
161 }
162
163 int mp::HTMLParser::Rep::tagStart(HTMLParserEvent &event,
164                                   int *tag_len,
165                                   const char *cp, const char which)
166 {
167     int i;
168     switch (which)
169     {
170     case '/':
171         i = skipName(cp);
172         *tag_len = i;
173         if (m_verbose)
174             printf("------ tag close %.*s\n", i, cp);
175         event.closeTag(cp, i);
176         break;
177     case '!':
178         for (i = 0; cp[i] && cp[i] != '>'; i++)
179             ;
180         *tag_len = i;
181         event.openTagStart(cp, i);
182         if (m_verbose)
183             printf("------ dtd %.*s\n", i, cp);
184         break;
185     case '?':
186         for (i = 0; cp[i] && cp[i] != '>'; i++)
187             ;
188         *tag_len = i;
189         event.openTagStart(cp, i);
190         if (m_verbose)
191             printf("------ pi %.*s\n", i, cp);
192         break;
193     default:
194         i = skipName(cp);
195         *tag_len = i;
196         if (m_verbose)
197             printf("------ tag open %.*s\n", i, cp);
198         event.openTagStart(cp, i);
199
200         i += tagAttrs(event, cp, i, cp + i);
201
202         break;
203     }
204     return i;
205 }
206
207 int mp::HTMLParser::Rep::tagEnd(HTMLParserEvent &event,
208                                 const char *tag, int tag_len, const char *cp)
209 {
210     int i = 0;
211     int close_it = 0;
212     while (cp[i] && cp[i] != '>')
213     {
214         if (cp[i] == '/')
215             close_it = 1;
216         i++;
217     }
218     if (cp[i] == '>')
219     {
220         event.anyTagEnd(tag, tag_len, close_it);
221         i++;
222     }
223     return i;
224 }
225
226 void mp::HTMLParser::Rep::tagText(HTMLParserEvent &event,
227                                   const char *text_start, const char *text_end)
228 {
229     if (text_end - text_start) //got text to flush
230     {
231         if (m_verbose)
232             printf("------ text %.*s\n",
233                    (int) (text_end - text_start), text_start);
234         event.text(text_start, text_end-text_start);
235     }
236 }
237
238 void mp::HTMLParser::Rep::parse_str(HTMLParserEvent &event, const char *cp)
239 {
240     const char *text_start = cp;
241     const char *text_end = cp;
242     while (*cp)
243     {
244         if (cp[0] == '<' && cp[1])  //tag?
245         {
246             char which = cp[1];
247             if (which == '/')
248                 cp++;
249             if (!strchr(SPACECHR, cp[1])) //valid tag starts
250             {
251                 int i = 0;
252                 int tag_len;
253
254                 tagText(event, text_start, text_end); //flush any text
255                 cp++;
256                 i += tagStart(event, &tag_len, cp, which);
257                 i += tagEnd(event, cp, tag_len, cp + i);
258                 cp += i;
259                 text_start = cp;
260                 text_end = cp;
261                 continue;
262             }
263         }
264         //text
265         cp++;
266         text_end = cp;
267     }
268     tagText(event, text_start, text_end); //flush any text
269 }
270
271 /*
272  * Local variables:
273  * c-basic-offset: 4
274  * c-file-style: "Stroustrup"
275  * indent-tabs-mode: nil
276  * End:
277  * vim: shiftwidth=4 tabstop=8 expandtab
278  */
279