fixed yet another bug in wxHtmlTag's parameters parser
[wxWidgets.git] / src / html / htmltag.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: htmltag.cpp
3 // Purpose: wxHtmlTag class (represents single tag)
4 // Author: Vaclav Slavik
5 // RCS-ID: $Id$
6 // Copyright: (c) 1999 Vaclav Slavik
7 // Licence: wxWindows Licence
8 /////////////////////////////////////////////////////////////////////////////
9
10
11 #ifdef __GNUG__
12 #pragma implementation
13 #endif
14
15 #include "wx/wxprec.h"
16
17 #include "wx/defs.h"
18 #if wxUSE_HTML
19
20 #ifdef __BORDLANDC__
21 #pragma hdrstop
22 #endif
23
24 #ifndef WXPRECOMP
25 #include "wx/wx.h"
26 #endif
27
28 #include "wx/html/htmltag.h"
29 #include <stdio.h> // for vsscanf
30 #include <stdarg.h>
31
32
33 //-----------------------------------------------------------------------------
34 // wxHtmlTagsCache
35 //-----------------------------------------------------------------------------
36
37 struct wxHtmlCacheItem
38 {
39 // this is "pos" value passed to wxHtmlTag's constructor.
40 // it is position of '<' character of the tag
41 int Key;
42
43 // end positions for the tag:
44 // end1 is '<' of ending tag,
45 // end2 is '>' or both are
46 // -1 if there is no ending tag for this one...
47 // or -2 if this is ending tag </...>
48 int End1, End2;
49
50 // name of this tag
51 wxChar *Name;
52 };
53
54
55 IMPLEMENT_CLASS(wxHtmlTagsCache,wxObject)
56
57 #define CACHE_INCREMENT 64
58
59 wxHtmlTagsCache::wxHtmlTagsCache(const wxString& source)
60 {
61 const wxChar *src = source.c_str();
62 int i, tg, pos, stpos;
63 int lng = source.Length();
64 wxChar dummy[256];
65
66 m_Cache = NULL;
67 m_CacheSize = 0;
68 m_CachePos = 0;
69
70 pos = 0;
71 while (pos < lng)
72 {
73 if (src[pos] == wxT('<')) // tag found:
74 {
75 if (m_CacheSize % CACHE_INCREMENT == 0)
76 m_Cache = (wxHtmlCacheItem*) realloc(m_Cache, (m_CacheSize + CACHE_INCREMENT) * sizeof(wxHtmlCacheItem));
77 tg = m_CacheSize++;
78 m_Cache[tg].Key = stpos = pos++;
79 dummy[0] = 0; i = 0;
80 while (pos < lng &&
81 src[pos] != wxT('>') &&
82 src[pos] != wxT(' ') && src[pos] != wxT('\r') &&
83 src[pos] != wxT('\n') && src[pos] != wxT('\t'))
84 {
85 dummy[i] = src[pos++];
86 if ((dummy[i] >= wxT('a')) && (dummy[i] <= wxT('z'))) dummy[i] -= (wxT('a') - wxT('A'));
87 i++;
88 }
89 dummy[i] = 0;
90 m_Cache[tg].Name = new wxChar[i+1];
91 memcpy(m_Cache[tg].Name, dummy, (i+1)*sizeof(wxChar));
92
93 while (pos < lng && src[pos] != wxT('>')) pos++;
94
95 if (src[stpos+1] == wxT('/')) // ending tag:
96 {
97 m_Cache[tg].End1 = m_Cache[tg].End2 = -2;
98 // find matching begin tag:
99 for (i = tg; i >= 0; i--)
100 if ((m_Cache[i].End1 == -1) && (wxStrcmp(m_Cache[i].Name, dummy+1) == 0))
101 {
102 m_Cache[i].End1 = stpos;
103 m_Cache[i].End2 = pos + 1;
104 break;
105 }
106 }
107 else
108 {
109 m_Cache[tg].End1 = m_Cache[tg].End2 = -1;
110 }
111 }
112
113 pos++;
114 }
115
116 // ok, we're done, now we'll free .Name members of cache - we don't need it anymore:
117 for (i = 0; i < m_CacheSize; i++)
118 {
119 delete[] m_Cache[i].Name;
120 m_Cache[i].Name = NULL;
121 }
122 }
123
124
125
126 void wxHtmlTagsCache::QueryTag(int at, int* end1, int* end2)
127 {
128 if (m_Cache == NULL) return;
129 if (m_Cache[m_CachePos].Key != at)
130 {
131 int delta = (at < m_Cache[m_CachePos].Key) ? -1 : 1;
132 do {m_CachePos += delta;} while (m_Cache[m_CachePos].Key != at);
133 }
134 *end1 = m_Cache[m_CachePos].End1;
135 *end2 = m_Cache[m_CachePos].End2;
136 }
137
138
139
140
141 //-----------------------------------------------------------------------------
142 // wxHtmlTag
143 //-----------------------------------------------------------------------------
144
145 IMPLEMENT_CLASS(wxHtmlTag,wxObject)
146
147 wxHtmlTag::wxHtmlTag(const wxString& source, int pos, int end_pos, wxHtmlTagsCache* cache) : wxObject()
148 {
149 int i;
150 char c;
151
152 // fill-in name, params and begin pos:
153 m_Name = m_Params = wxEmptyString;
154 i = pos+1;
155 if (source[i] == wxT('/')) { m_Ending = TRUE; i++; }
156 else m_Ending = FALSE;
157
158 // find tag's name and convert it to uppercase:
159 while ((i < end_pos) &&
160 ((c = source[i++]) != wxT(' ') && c != wxT('\r') &&
161 c != wxT('\n') && c != wxT('\t') &&
162 c != wxT('>')))
163 {
164 if ((c >= wxT('a')) && (c <= wxT('z'))) c -= (wxT('a') - wxT('A'));
165 m_Name += c;
166 }
167
168 // if the tag has parameters, read them and "normalize" them,
169 // i.e. convert to uppercase, replace whitespaces by spaces and
170 // remove whitespaces around '=':
171 if (source[i-1] != wxT('>'))
172 while ((i < end_pos) && ((c = source[i++]) != wxT('>')))
173 {
174 if ((c >= wxT('a')) && (c <= wxT('z')))
175 c -= (wxT('a') - wxT('A'));
176 if (c == wxT('\r') || c == wxT('\n') || c == wxT('\t'))
177 c = wxT(' '); // make future parsing a bit simpler
178 m_Params += c;
179 if (c == wxT('"'))
180 {
181 // remove spaces around the '=' character:
182 if (m_Params.Length() > 1 &&
183 m_Params[m_Params.Length()-2] == wxT(' '))
184 {
185 m_Params.RemoveLast();
186 while (m_Params.Length() > 0 && m_Params.Last() == wxT(' '))
187 m_Params.RemoveLast();
188 m_Params += wxT('"');
189 }
190 while ((i < end_pos) && (source[i++] == wxT(' '))) {}
191 if (i < end_pos) i--;
192
193 // ...and copy the value to m_Params:
194 while ((i < end_pos) && ((c = source[i++]) != wxT('"')))
195 m_Params += c;
196 m_Params += c;
197 }
198 else if (c == wxT('\''))
199 {
200 while ((i < end_pos) && ((c = source[i++]) != wxT('\'')))
201 m_Params += c;
202 m_Params += c;
203 }
204 }
205 m_Begin = i;
206
207 cache->QueryTag(pos, &m_End1, &m_End2);
208 if (m_End1 > end_pos) m_End1 = end_pos;
209 if (m_End2 > end_pos) m_End2 = end_pos;
210 }
211
212
213
214 bool wxHtmlTag::HasParam(const wxString& par) const
215 {
216 const wxChar *st = m_Params, *p = par;
217 const wxChar *st2, *p2;
218 const wxChar invalid = wxT('\1');
219
220 if (*st == 0) return FALSE;
221 if (*p == 0) return FALSE;
222 for (st2 = st, p2 = p; ; st2++)
223 {
224 if (*p2 == 0 && *st2 == wxT('=')) return TRUE;
225 if (*st2 == 0) return FALSE;
226 if (*p2 != *st2) p2 = &invalid;
227 if (*p2 == *st2) p2++;
228 if (*st2 == wxT(' ')) p2 = p;
229 else if (*st2 == wxT('='))
230 {
231 p2 = p;
232 while (*st2 != wxT(' '))
233 {
234 if (*st2 == wxT('"'))
235 {
236 st2++;
237 while (*st2 != wxT('"')) st2++;
238 }
239 st2++;
240 if (*st2 == 0) return FALSE;
241 }
242 }
243 }
244 }
245
246
247
248 wxString wxHtmlTag::GetParam(const wxString& par, bool with_commas) const
249 {
250 const wxChar *st = m_Params, *p = par;
251 const wxChar *st2, *p2;
252 const wxChar invalid = wxT('\1');
253 bool comma;
254 wxChar comma_char;
255
256 if (*st == 0) return wxEmptyString;
257 if (*p == 0) return wxEmptyString;
258 for (st2 = st, p2 = p; ; st2++)
259 {
260 if (*p2 == 0 && *st2 == wxT('=')) // found
261 {
262 wxString fnd = wxEmptyString;
263 st2++; // '=' character
264 comma = FALSE;
265 comma_char = wxT('\0');
266 if (!with_commas && (*(st2) == wxT('"')))
267 {
268 st2++;
269 comma = TRUE;
270 comma_char = wxT('"');
271 }
272 else if (!with_commas && (*(st2) == wxT('\'')))
273 {
274 st2++;
275 comma = TRUE;
276 comma_char = wxT('\'');
277 }
278
279 while (*st2 != 0)
280 {
281 if (comma && *st2 == comma_char) comma = FALSE;
282 else if ((*st2 == wxT(' ')) && (!comma)) break;
283 fnd += (*(st2++));
284 }
285 if (!with_commas && (*(st2-1) == comma_char)) fnd.RemoveLast();
286 return fnd;
287 }
288 if (*st2 == 0) return wxEmptyString;
289 if (*p2 != *st2) p2 = &invalid;
290 if (*p2 == *st2) p2++;
291 if (*st2 == wxT(' ')) p2 = p;
292 else if (*st2 == wxT('='))
293 {
294 p2 = p;
295 while (*st2 != wxT(' '))
296 {
297 if (*st2 == wxT('"'))
298 {
299 st2++;
300 while (*st2 != wxT('"')) st2++;
301 }
302 else if (*st2 == wxT('\''))
303 {
304 st2++;
305 while (*st2 != wxT('\'')) st2++;
306 }
307 st2++;
308 }
309 }
310 }
311 }
312
313
314
315 int wxHtmlTag::ScanParam(const wxString& par, wxChar *format, void *param) const
316 {
317 wxString parval = GetParam(par);
318 return wxSscanf(parval, format, param);
319 }
320
321 #endif