cql_rpn: use path for cql2rpn file MP-481
[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 // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html
31
32 namespace metaproxy_1 {
33     class HTMLParser::Rep {
34         friend class HTMLParser;
35     public:
36         void parse_str(HTMLParserEvent &event, const char *cp);
37         void tagText(HTMLParserEvent &event,
38                      const char *text_start, const char *text_end);
39         int tagEnd(HTMLParserEvent &event,
40                    const char *tag, int tag_len, const char *cp);
41         int tagAttrs(HTMLParserEvent &event,
42                      const char *name, int len,
43                      const char *cp);
44         int skipAttribute(HTMLParserEvent &event,
45                           const char *cp, int *attr_len,
46                           const char **value, int *val_len, int *tr);
47         Rep();
48         ~Rep();
49         int m_verbose;
50     };
51 }
52
53 namespace mp = metaproxy_1;
54
55 mp::HTMLParser::Rep::Rep()
56 {
57     m_verbose = 0;
58 }
59
60 mp::HTMLParser::Rep::~Rep()
61 {
62 }
63
64 mp::HTMLParser::HTMLParser() : m_p(new Rep)
65 {
66 }
67
68 mp::HTMLParser::~HTMLParser()
69 {
70 }
71
72 void mp::HTMLParser::set_verbose(int v)
73 {
74     m_p->m_verbose = v;
75 }
76
77
78 void mp::HTMLParser::parse(mp::HTMLParserEvent & event, const char *str) const
79 {
80     m_p->parse_str(event, str);
81 }
82
83 static int isAlpha(int c)
84 {
85     return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
86 }
87
88 static int skipSpace(const char *cp)
89 {
90     int i = 0;
91     while (cp[i] && strchr(SPACECHR, cp[i]))
92         i++;
93     return i;
94 }
95
96 static int skipName(const char *cp)
97 {
98     int i;
99     for (i = 0; cp[i] && !strchr(SPACECHR "/><=", cp[i]); i++)
100         ;
101     return i;
102 }
103
104 int mp::HTMLParser::Rep::skipAttribute(HTMLParserEvent &event,
105                                        const char *cp, int *attr_len,
106                                        const char **value, int *val_len,
107                                        int *tr)
108 {
109     int v0, v1;
110     int i = skipName(cp);
111     *attr_len = i;
112     *value = NULL;
113     if (!i)
114         return skipSpace(cp);
115     i += skipSpace(cp + i);
116     if (cp[i] == '=')
117     {
118         i++;
119         i += skipSpace(cp + i);
120         if (cp[i] == '\"' || cp[i] == '\'')
121         {
122             *tr = cp[i];
123             v0 = ++i;
124             while (cp[i] != *tr && cp[i])
125                 i++;
126             v1 = i;
127             if (cp[i])
128                 i++;
129         }
130         else
131         {
132             *tr = 0;
133             v0 = i;
134             while (cp[i] && !strchr(SPACECHR ">", cp[i]))
135                 i++;
136             v1 = i;
137         }
138         *value = cp + v0;
139         *val_len = v1 - v0;
140         i += skipSpace(cp + i);
141     }
142     return i;
143 }
144
145 int mp::HTMLParser::Rep::tagAttrs(HTMLParserEvent &event,
146                                   const char *name, int len,
147                                   const char *cp)
148 {
149     int i = skipSpace(cp);
150     while (cp[i] && !strchr("/><", cp[i]))
151     {
152         const char *attr_name = cp + i;
153         int attr_len;
154         const char *value;
155         int val_len;
156         int tr;
157         char x[2];
158         int nor = skipAttribute(event, cp+i, &attr_len, &value, &val_len, &tr);
159         if (!nor)
160             break;
161         i += nor;
162
163         x[0] = tr;
164         x[1] = 0;
165         if (m_verbose)
166             printf ("------ attr %.*s=%.*s\n", attr_len, attr_name,
167                     val_len, value);
168         event.attribute(name, len, attr_name, attr_len, value, val_len, x);
169     }
170     return i;
171 }
172
173 int mp::HTMLParser::Rep::tagEnd(HTMLParserEvent &event,
174                                 const char *tag, int tag_len, const char *cp)
175 {
176     int i = 0;
177     int close_it = 0;
178     for (; cp[i] && !strchr("/><", cp[i]); i++)
179         ;
180     if (i > 0)
181     {
182         if (m_verbose)
183             printf("------ text %.*s\n", i, cp);
184         event.text(cp, i);
185     }
186     if (cp[i] == '/')
187     {
188         close_it = 1;
189         i++;
190     }
191     if (cp[i] == '>')
192     {
193         if (m_verbose)
194             printf("------ any tag %s %.*s\n",
195                    close_it ? " close" : "end", tag_len, tag);
196         event.anyTagEnd(tag, tag_len, close_it);
197         i++;
198     }
199     return i;
200 }
201
202 void mp::HTMLParser::Rep::tagText(HTMLParserEvent &event,
203                                   const char *text_start, const char *text_end)
204 {
205     if (text_end - text_start) //got text to flush
206     {
207         if (m_verbose)
208             printf("------ text %.*s\n",
209                    (int) (text_end - text_start), text_start);
210         event.text(text_start, text_end-text_start);
211     }
212 }
213
214 void mp::HTMLParser::Rep::parse_str(HTMLParserEvent &event, const char *cp)
215 {
216     const char *text_start = cp;
217     while (*cp)
218     {
219         if (*cp++ != '<')
220             continue;
221
222         if (*cp == '!')
223         {
224             int i;
225             tagText(event, text_start, cp - 1);
226             if (cp[1] == '-' && cp[2] == '-')
227             {
228                 for (i = 3; cp[i]; i++)
229                     if (cp[i] == '-' && cp[i+1] == '-' && cp[i+2] == '>')
230                     {
231                         i+= 2;
232                         event.openTagStart(cp, i);
233                         break;
234                     }
235             }
236             else
237             {
238                 for (i = 1; cp[i] && cp[i] != '>'; i++)
239                     ;
240                 event.openTagStart(cp, i);
241             }
242             if (m_verbose)
243                 printf("------ dtd %.*s\n", i, cp);
244             i += tagEnd(event, cp, i, cp + i);
245             cp += i;
246             text_start = cp;
247         }
248         else if (*cp == '?')
249         {
250             int i;
251             tagText(event, text_start, cp - 1);
252             for (i = 1; cp[i] && cp[i] != '>'; i++)
253                 ;
254             event.openTagStart(cp, i);
255             if (m_verbose)
256                 printf("------ pi %.*s\n", i, cp);
257             i += tagEnd(event, cp, i, cp + i);
258             cp += i;
259             text_start = cp;
260         }
261         else if (*cp == '/' && isAlpha(cp[1]))
262         {
263             int i;
264             tagText(event, text_start, cp - 1);
265
266             i = skipName(++cp);
267             event.closeTag(cp, i);
268             if (m_verbose)
269                 printf("------ tag close %.*s\n", i, cp);
270             i += tagEnd(event, cp, i, cp + i);
271             cp += i;
272             text_start = cp;
273         }
274         else if (isAlpha(*cp))
275         {
276             int i, j;
277             tagText(event, text_start, cp - 1);
278             i = skipName(cp);
279             event.openTagStart(cp, i);
280             if (m_verbose)
281                 printf("------ tag open %.*s\n", i, cp);
282             j = tagAttrs(event, cp, i, cp + i);
283             j += tagEnd(event, cp, i, cp + i + j);
284             cp += i + j;
285             text_start = cp;
286         }
287     }
288     tagText(event, text_start, cp);
289 }
290
291 mp::HTMLParserEvent::~HTMLParserEvent()
292 {
293 }
294
295 /*
296  * Local variables:
297  * c-basic-offset: 4
298  * c-file-style: "Stroustrup"
299  * indent-tabs-mode: nil
300  * End:
301  * vim: shiftwidth=4 tabstop=8 expandtab
302  */
303