]> git.saurik.com Git - wxWidgets.git/blob - contrib/src/applet/prepifelse.cpp
glibc's vswprintf doesn't nul terminate on truncation.
[wxWidgets.git] / contrib / src / applet / prepifelse.cpp
1 /****************************************************************************
2 *
3 * wxWindows HTML Applet Package
4 *
5 * Copyright (C) 1991-2001 SciTech Software, Inc.
6 * All rights reserved.
7 *
8 * ========================================================================
9 *
10 * The contents of this file are subject to the wxWindows License
11 * Version 3.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.wxwindows.org/licence3.txt
14 *
15 * Software distributed under the License is distributed on an
16 * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
17 * implied. See the License for the specific language governing
18 * rights and limitations under the License.
19 *
20 * ========================================================================
21 *
22 * Language: ANSI C++
23 * Environment: Any
24 *
25 * Description: This file is the implementation of the Preprocessor object
26 * for parsing the <!--#if directive
27 *
28 ****************************************************************************/
29
30 // Include private headers
31 #include "wx/applet/prepifelse.h"
32 #include "wx/applet/ifelsevar.h"
33 #include "wx/applet/echovar.h"
34 #include "wx/string.h"
35
36 // Force link macro
37 #include "wx/html/forcelnk.h"
38 // wxWindows
39 #include "wx/msgdlg.h"
40
41 /*----------------------------- Implementation ----------------------------*/
42
43 /* {SECRET} */
44 /****************************************************************************
45 REMARKS:
46 None of the Reverse Find functions in wxWindows appear to work in a way that
47 can be used by our code. This includes the libstr rfind implementations which
48 do not correctly pass the given return value.
49 ****************************************************************************/
50 int ReverseFind(
51 const wxString &tstr,
52 const wxString &str,
53 int start = -1)
54 {
55 wxASSERT( str.GetStringData()->IsValid() );
56
57 // TODO could be made much quicker than that
58 int p = tstr.Len()-str.Len()-1;
59 int p2 = start-str.Len();
60
61 // if the user supplied a valid start point, use it
62 if (start != -1 && p > p2) p = p2;
63 while ( p >= 0 ) {
64 if ( wxStrncmp(tstr.c_str() + p, str.c_str(), str.Len()) == 0 )
65 return p;
66 p--;
67 }
68 return -1;
69 }
70
71 /* {SECRET} */
72 /****************************************************************************
73 REMARKS:
74 tells if a character is a letter.
75 replace this when wxWindows gets regex library. (without strange licensing
76 restrictions)
77 ****************************************************************************/
78 bool IsLetter(
79 char c, bool acceptspace = false)
80 {
81 if (acceptspace && (c == ' ')) return true;
82 if (c >= '0' && c <= '9') return true;
83 return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '\"' || c == '\'' );
84 }
85
86 #define IsQuote(c) (c == '\'' || c == '\"')
87
88 /* {SECRET} */
89 /****************************************************************************
90 REMARKS:
91 tells if a character is a letter.
92 replace this when wxWindows gets regex library. (without strange licensing
93 restrictions)
94 ****************************************************************************/
95 wxString GetEquals(
96 wxString var,
97 wxString value)
98 {
99 if (!wxEchoVariable::Exists(var)) {
100 // TODO: when we implement the set variable, check for a set variable as well
101 #ifdef CHECKED
102 wxMessageBox(wxString("wxHTML #if\\else error: Variable ") + var + wxString(" not found."),"Error",wxICON_ERROR);
103 #endif
104 return wxString("0"); // false
105 }
106
107 wxString tmp = wxEchoVariable::GetValue(var);
108
109 if (IsQuote( value.GetChar(0) ))
110 value = value.Mid(1);
111 if (IsQuote(value.GetChar(value.Length()-1)))
112 value = value.Mid(0,value.Length()-1);
113
114 if (tmp.CmpNoCase(value) == 0) return wxString("1");
115 return wxString("0");
116 }
117
118 /****************************************************************************
119 PARAMETERS:
120 str - text of #if statement
121
122 RETURNS:
123 true or false depending on how it evaluated
124
125 REMARKS:
126 TODO: rewrite this whole thing using regular expressions when they are done.
127
128 SEE ALSO:
129 wxIfElseVariable
130 ****************************************************************************/
131 bool ParseIfStatementValue(
132 wxString &str)
133 {
134 // Find out if the tag has parenthesis
135 // recursive to parse the text within the parenthesis,
136 // replacing the text with 1 or 0, (hardcoded true or false)
137 int b;
138 while ((b = str.Find('(')) != -1) {
139 int e;
140 // Find the matching parenthesis
141 int nextbeg, nextend;
142 int parencount = 1, min = b+1;
143 do {
144 nextbeg = str.find('(', min);
145 nextend = str.find(')', min);
146 if (nextbeg < nextend && nextbeg != wxString::npos) {
147 parencount++;
148 min = nextbeg+1;
149 }
150 else {
151 parencount--;
152 min = nextend+1;
153 }
154
155 if (nextend == wxString::npos) {
156 #ifdef CHECKED
157 wxMessageBox("wxHTML #if\\else error: Unmatched parenthesis in #if expression.","Error",wxICON_ERROR);
158 #endif
159 return true;
160 }
161 // once parencount reaches 0 again we have found our matchin )
162 } while (parencount > 0);
163
164 e = nextend;
165
166 // Extract the expression from the parenthesis block and recurse
167 // to solve it.
168 wxString tag;
169 tag = str.Mid(b+1, e-b-1);
170 bool val = ParseIfStatementValue(tag);
171 // Add extra spaces just in case of NOT(VAL)
172 if (val) str = str.Mid(0, b) + " 1" + str.Mid(e+1);
173 else str = str.Mid(0, b) + " 0" + str.Mid(e+1);
174 }
175
176 // Remove spaces from left and right
177 str.Trim(false);
178 str.Trim(true);
179
180 // Convert text method of operators "AND" and "OR" to c style
181 // this makes only one special case necessary for each later on
182 str.Replace(" AND ", "&&");
183 str.Replace(" OR ", "||");
184 str.Replace(" EQUALS ", "==");
185
186 // Check for equals statements
187 // == statements are special because they are evaluated as a single block
188 int equ;
189 equ = str.find("==");
190 while (equ != wxString::npos) {
191 int begin, end;
192 int begin2, end2; // ends of words
193 begin = equ-1;
194 end = equ+2;
195
196 // remove spaces, find extents
197 while (end < str.Length() && str.GetChar(end) == ' ')
198 end++;
199 while (begin >= 0 && str.GetChar(begin) == ' ')
200 begin--;
201 end2 = end;
202 begin2 = begin;
203 if (str.GetChar(end2) == '\'' || str.GetChar(end2) == '\"') {
204 end2++;
205 while (end2 < str.Length() && str.GetChar(end2) != '\'' && str.GetChar(end2) != '\"' )
206 end2++;
207 end2++;
208 }
209 else {
210 while (end2 < str.Length() && IsLetter(str.GetChar(end2)))
211 end2++;
212 }
213 while (begin >= 0 && IsLetter(str.GetChar(begin)))
214 begin--;
215
216 if (begin < 0) begin = 0;
217 else begin++;
218 if (end2 >= str.Length()) end2 = str.Length();
219
220 wxString tmpeq = GetEquals(str.Mid(begin, begin2-begin+1), str.Mid(end, end2-end));
221 str = str.Mid(0, begin) + wxString(" ") + tmpeq + wxString(" ") +
222 str.Mid(end2);
223 equ = str.find("==");
224
225 // Remove spaces from left and right
226 str.Trim(false);
227 str.Trim(true);
228 }
229
230 // We use ReverseFind so that the whole left expression gets evaluated agains
231 // the right single item, creating a left -> right evaluation
232 // Search for || operators, recurse to solve (so we don't have to handle special cases here)
233 int and, or;
234 and = ReverseFind(str, "&&");
235 or = ReverseFind(str, "||");
236 if ( (and != -1) || (or != -1) ) {
237 wxString tag1, tag2;
238 // handle the rightmost first to force left->right evaluation
239 if ( (and > or) ) {
240 return (
241 ParseIfStatementValue(tag2 = str.Mid(and+2)) &&
242 ParseIfStatementValue(tag1 = str.Mid(0, and)) );
243 }
244 else {
245 return (
246 ParseIfStatementValue(tag2 = str.Mid(or+2)) ||
247 ParseIfStatementValue(tag1 = str.Mid(0, or)) );
248 }
249 }
250
251 // By the time we get to this place in the function we are guarenteed to have a single
252 // variable operation, perhaps with a NOT or ! operator
253 bool notval = false;
254
255 // search for a NOT or ! operator
256 if (str.Mid(0, 1) == "!") {
257 str.Remove(0, 1);
258 str.Trim(false); // trim spaces from left
259 notval = true;
260 }
261 else if (str.Mid(0,4).CmpNoCase("NOT ") == 0) {
262 str.Remove(0, 4);
263 str.Trim(false); // trim any extra spaces from left
264 notval = true;
265 }
266
267 // now all we have left is the name of the class or a hardcoded 0 or 1
268 if (str == "") {
269 #ifdef CHECKED
270 wxMessageBox("wxHTML #if\\else error: Empty expression in #if\\#elif statement.","Error",wxICON_ERROR);
271 #endif
272 return true;
273 }
274
275 // check for hardcoded 0 and 1 cases, (these are used by parenthesis catcher)
276 // this just decomplicates the recursion algorithm
277 if (str == "0") return notval;
278 if (str == "1") return !notval;
279
280 // Grab the value from the variable class identified by cname
281 bool value = wxIfElseVariable::GetValue(str);
282 if (notval) value = !value;
283 return value;
284
285 }
286 /****************************************************************************
287 PARAMETERS:
288 text - HTML to process for if/else blocks
289
290 RETURNS:
291 The string containing the processed HTML
292
293 REMARKS:
294 This function replaces #if, #else, #elif, and #endif directives with the text
295 contained within the blocks, dependant on the value of the given boolean
296 variable. The variable is created by making a sub class of wxIfElseVariable.
297 Dynamic class construction is used at run time internally to create an instance
298 of this class and access the value of the variable.
299
300 SEE ALSO:
301 wxIfElseVariable
302 ****************************************************************************/
303 wxString wxIfElsePrep::Process(
304 const wxString& text) const
305 {
306 int b;
307 char ft[] = "<!--#if ";
308 char ftend[] = "<!--#endif-->";
309 char ftelse[] = "<!--#else-->";
310 char ftnot[] = "<!--#if not ";
311 char ftnot2[] = "<!--#if !";
312 char ftelif[] = "<!--#elif ";
313
314 // make a copy so we can replace text as we go without affecting the original
315 wxString output = text;
316
317 // Avoid duplication of our parsing code by turning any #elif blocks into appropriate
318 // else/if blocks
319 while ((b = ReverseFind(output.Lower(), ftelif)) != -1) {
320 int e;
321 // Replace beginning of block
322 e = output.find("-->", b + strlen(ftelif));
323
324 if (e == wxString::npos) {
325 #ifdef CHECKED
326 wxMessageBox("wxHTML #elif error: Premature end of file while parsing #elif.","Error",wxICON_ERROR);
327 #endif
328 break;
329 }
330
331 // Convert to lower case so find is easy, grab everything after #elif tag
332 wxString remains = (output.Mid(e+strlen("-->"))).Lower();
333
334 // find matching else or endif
335 int nextif, nextendif;
336 int ifcount = 1, min = 0;
337 do {
338 nextif = remains.find(ft, min);
339 nextendif = remains.find(ftend, min);
340 if (nextif < nextendif && nextif != wxString::npos) {
341 ifcount++;
342 min = nextif+1;
343 }
344 else {
345 ifcount--;
346 min = nextendif+1;
347 }
348
349 if (nextendif == wxString::npos) {
350 #ifdef CHECKED
351 wxMessageBox("wxHTML #elif error: Premature end of file before finding #endif.","Error",wxICON_ERROR);
352 #endif
353 break;
354 }
355 // once ifcount reaches 0 again we have found our matchin #endif
356 } while (ifcount > 0);
357
358 // If it couldn't be found die gracefully
359 if (nextendif == wxString::npos) {
360 // We already displayed a message, just break all the way out
361 break;
362 }
363
364 int elifsize = e - (b + strlen(ftelif)) + strlen("-->");
365 // Create the #if/else block, removing the #elif code
366 output = output.Mid(0, b) +
367 wxString(wxString(ftelse)+wxString(ft)) +
368 output.Mid(b+strlen(ftelif), elifsize+nextendif) +
369 wxString(ftend) +
370 output.Mid(b+strlen(ftelif)+elifsize+nextendif);
371 }
372
373 // Parse out the if else blocks themselves
374 while ((b = ReverseFind(output.Lower(), ft)) != -1) {
375 // Loop until every #if directive is found
376 // We search from the end of the string so that #if statements will properly recurse
377 // and we avoid the hassle of matching statements with the correct <!--#endif-->
378 bool notval = false;
379 int off = 0;
380 int end;
381 wxString usecode, code;
382 wxString cname;
383 wxString tag;
384 bool value;
385
386 code = wxString("");
387
388 // grab the tag and get the name of the variable
389 end = (output.Mid(b)).Find("-->");
390 if (end == -1) {
391 #ifdef CHECKED
392 wxMessageBox("wxHTML #if error: Premature end of file while parsing #if.","Error",wxICON_ERROR);
393 #endif
394 break;
395 }
396
397 end += 3;
398 // remove the <!--#if and --> sections from the tag before passing it on to be parsed
399 tag = output.Mid(b+strlen(ft), end-strlen(ft)-3);
400 output.Remove(b, end);
401
402 value = ParseIfStatementValue(tag);
403
404 // Find the end of the tag (<!--#endif-->) and copy it all into the variable code
405 end = ((output.Mid(b)).Lower()).Find(ftend);
406 if (end == -1) {
407 #ifdef CHECKED
408 wxMessageBox("wxHTML #if error: Premature end of file while searching for matching #endif.","Error",wxICON_ERROR);
409 #endif
410 break;
411 }
412
413 code = output.Mid(b, end);
414 output.Remove(b, end+strlen(ftend)); // remove the entire #if block from original document
415
416 // Find out if there is an else statement
417 end = (code.Lower()).Find(ftelse);
418 if (end != -1) {
419 if (!value) {
420 // Use the else statement
421 usecode = code.Mid(end+strlen(ftelse));
422 }
423 else {
424 // Use statement before #else
425 usecode = code.Mid(0, end);
426 }
427 }
428 else if (value) {
429 // There is no #else statement
430 usecode = code;
431 }
432
433 if (usecode != wxString(""))
434 output = (output.Mid(0,b) + usecode + output.Mid(b));
435 }
436
437 return output;
438 }
439
440 FORCE_LINK(ifelsevar)
441