No dupe function (leaks)
[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 TAG_MAX_LEN 64
29
30 #define SPACECHR " \t\r\n\f"
31
32 #define DEBUG(x) x
33
34 #if HAVE_SYS_TYPES_H
35 #include <sys/types.h>
36 #endif
37
38 namespace mp = metaproxy_1;
39
40 mp::HTMLParser::HTMLParser()
41 {
42 }
43
44 mp::HTMLParser::~HTMLParser()
45 {
46 }
47
48 static void parse_str(mp::HTMLParserEvent & event, const char * str);
49
50 void mp::HTMLParser::parse(mp::HTMLParserEvent & event, const char *str) const
51 {
52     parse_str(event, str);
53 }
54
55 static int skipSpace(const char *cp)
56 {
57     int i = 0;
58     while (cp[i] && strchr(SPACECHR, cp[i]))
59         i++;
60     return i;
61 }
62
63 static int skipName(const char *cp, char *dst)
64 {
65     int i;
66     int j = 0;
67     for (i = 0; cp[i] && !strchr(SPACECHR "/>=", cp[i]); i++)
68         if (j < TAG_MAX_LEN-1)
69         {
70             dst[j] = tolower(cp[j]);
71             j++;
72         }
73     dst[j] = '\0';
74     return i;
75 }
76
77 static int skipAttribute(const char *cp, char *name, const char **value, int *val_len)
78 {
79     int i = skipName(cp, name);
80     *value = NULL;
81     if (!i)
82         return skipSpace(cp);
83     i += skipSpace(cp + i);
84     if (cp[i] == '=')
85     {
86         int v0, v1;
87         i++;
88         i += skipSpace(cp + i);
89         if (cp[i] == '\"' || cp[i] == '\'')
90         {
91             char tr = cp[i];
92             v0 = ++i;
93             while (cp[i] != tr && cp[i])
94                 i++;
95             v1 = i;
96             if (cp[i])
97                 i++;
98         }
99         else
100         {
101             v0 = i;
102             while (cp[i] && !strchr(SPACECHR ">", cp[i]))
103                 i++;
104             v1 = i;
105         }
106         *value = cp + v0;
107         *val_len = v1 - v0;
108     }
109     i += skipSpace(cp + i);
110     return i;
111 }
112
113 static int tagAttrs(mp::HTMLParserEvent & event,
114                      const char *tagName,
115                      const char *cp)
116 {
117     char attr_name[TAG_MAX_LEN];
118     const char *attr_value;
119     int val_len;
120     int i = skipSpace(cp);
121     while (cp[i] && cp[i] != '>' && cp[i] != '/')
122     {
123         int nor = skipAttribute(cp+i, attr_name, &attr_value, &val_len);
124         i += nor;
125         if (nor)
126         {
127             DEBUG(printf ("------ attr %s=%.*s\n", attr_name, val_len, attr_value));
128             event.attribute(tagName, attr_name, attr_value, val_len);
129         }
130         else
131         {
132             if (!nor)
133                 i++;
134         }
135     }
136     return i;
137 }
138
139 static int tagStart(mp::HTMLParserEvent & event,
140         char *tagName, const char *cp, const char which)
141 {
142     int i = skipName(cp, tagName);
143     switch (which)
144     {
145     case '/' :
146         DEBUG(printf("------ tag close %s\n", tagName));
147         event.closeTag(tagName);
148         break;
149     case '!' :
150         DEBUG(printf("------ dtd %s\n", tagName));
151         break;
152     case '?' :
153         DEBUG(printf("------ pi %s\n", tagName));
154         break;
155     default :
156         DEBUG(printf("------ tag open %s\n", tagName));
157         event.openTagStart(tagName);
158         break;
159     }
160     return i;
161 }
162
163 static int tagEnd(mp::HTMLParserEvent & event, const char *tagName, const char *cp)
164 {
165     int i = 0;
166     int close_it = 0;
167     while (cp[i] && cp[i] != '>')
168     {
169         if (cp[i] == '/')
170             close_it = 1;
171         i++;
172     }
173     if (cp[i] == '>')
174     {
175         event.anyTagEnd(tagName, close_it);
176         i++;
177     }
178     return i;
179 }
180
181 static void tagText(mp::HTMLParserEvent & event, const char *text_start, const char *text_end)
182 {
183     if (text_end - text_start) //got text to flush
184     {
185         DEBUG(printf("------ text %.*s\n",
186                      (int) (text_end - text_start), text_start));
187         event.text(text_start, text_end-text_start);
188     }
189 }
190
191 static void parse_str(mp::HTMLParserEvent & event, const char *cp)
192 {
193     const char *text_start = cp;
194     const char *text_end = cp;
195     while (*cp)
196     {
197         if (cp[0] == '<' && cp[1])  //tag?
198         {
199             char which = cp[1];
200             if (which == '/')
201                 cp++;
202             if (!strchr(SPACECHR, cp[1])) //valid tag starts
203             {
204                 tagText(event, text_start, text_end); //flush any text
205                 char tagName[TAG_MAX_LEN];
206                 cp++;
207                 if (which == '/')
208                 {
209                     cp += tagStart(event, tagName, cp, which);
210                 }
211                 else if (which == '!' || which == '?') //pi or dtd
212                 {
213                     cp++;
214                     cp += tagStart(event, tagName, cp, which);
215                 }
216                 else
217                 {
218                     cp += tagStart(event, tagName, cp, which);
219                     cp += tagAttrs(event, tagName, cp);
220                 }
221                 cp += tagEnd(event, tagName, cp);
222                 text_start = cp;
223                 text_end = cp;
224                 continue;
225             }
226         }
227         //text
228         cp++;
229         text_end = cp;
230     }
231     tagText(event, text_start, text_end); //flush any text
232 }
233
234 /*
235  * Local variables:
236  * c-basic-offset: 4
237  * c-file-style: "Stroustrup"
238  * indent-tabs-mode: nil
239  * End:
240  * vim: shiftwidth=4 tabstop=8 expandtab
241  */
242