]>
Commit | Line | Data |
---|---|---|
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 |