]>
Commit | Line | Data |
---|---|---|
716cd410 KB |
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 | // For compilers that support precompilation | |
31 | #include "wx/wxprec.h" | |
32 | #include "wx/html/forcelnk.h" | |
33 | ||
34 | // Include private headers | |
35 | #include "wx/applet/prepifelse.h" | |
36 | #include "wx/applet/ifelsevar.h" | |
37 | ||
38 | /*---------------------------- Global variables ---------------------------*/ | |
39 | ||
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, | |
19193a2c KB |
52 | const wxString &str, |
53 | int start = -1) | |
716cd410 KB |
54 | { |
55 | wxASSERT( str.GetStringData()->IsValid() ); | |
56 | ||
57 | // TODO could be made much quicker than that | |
58 | int p = tstr.Len()-str.Len()-1; | |
19193a2c KB |
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; | |
716cd410 KB |
63 | while ( p >= 0 ) { |
64 | if ( wxStrncmp(tstr.c_str() + p, str.c_str(), str.Len()) == 0 ) | |
65 | return p; | |
66 | p--; | |
67 | } | |
68 | ||
69 | return -1; | |
70 | } | |
71 | ||
19193a2c KB |
72 | /**************************************************************************** |
73 | PARAMETERS: | |
74 | str - text of #if statement | |
75 | ||
76 | RETURNS: | |
77 | true or false depending on how it evaluated | |
78 | ||
79 | REMARKS: | |
80 | ||
81 | SEE ALSO: | |
82 | wxIfElseVariable | |
83 | ****************************************************************************/ | |
84 | bool ParseIfStatementValue(wxString &str) { | |
85 | ||
86 | // Find out if the tag has parenthesis | |
87 | // recursive to parse the text within the parenthesis, | |
88 | // replacing the text with 1 or 0, (hardcoded true or false) | |
89 | int b; | |
90 | while ((b = str.Find('(')) != -1) { | |
91 | int e; | |
92 | // Find the matching parenthesis | |
93 | int nextbeg, nextend; | |
94 | int parencount = 1, min = b+1; | |
95 | do { | |
96 | ||
97 | nextbeg = str.find('(', min); | |
98 | nextend = str.find(')', min); | |
99 | if (nextbeg < nextend && nextbeg != wxString::npos) { | |
100 | parencount++; | |
101 | min = nextbeg+1; | |
102 | } | |
103 | else { | |
104 | parencount--; | |
105 | min = nextend+1; | |
106 | } | |
107 | ||
108 | if (nextend == wxString::npos) { | |
109 | #ifdef CHECKED | |
110 | wxMessageBox("wxHTML #if\\else error: Unmatched parenthesis in #if expression.","Error",wxICON_ERROR); | |
111 | #endif | |
112 | return true; | |
113 | } | |
114 | // once parencount reaches 0 again we have found our matchin ) | |
115 | } while (parencount > 0); | |
116 | ||
117 | e = nextend; | |
118 | ||
119 | // Extract the expression from the parenthesis block and recurse | |
120 | // to solve it. | |
121 | wxString tag; | |
122 | tag = str.Mid(b+1, e-b-1); | |
123 | bool val = ParseIfStatementValue(tag); | |
124 | // Add extra spaces just in case of NOT(VAL) | |
125 | if (val) str = str.Mid(0, b) + " 1" + str.Mid(e+1); | |
126 | else str = str.Mid(0, b) + " 0" + str.Mid(e+1); | |
127 | ||
128 | } | |
129 | ||
130 | // Remove spaces from left and right | |
131 | str.Trim(false); | |
132 | str.Trim(true); | |
133 | ||
134 | // Convert text method of operators "AND" and "OR" to c style | |
135 | // this makes only one special case necessary for each later on | |
136 | str.Replace(" AND ", "&&"); | |
137 | str.Replace(" OR ", "||"); | |
138 | ||
139 | // We use ReverseFind so that the whole left expression gets evaluated agains | |
140 | // the right single item, creating a left -> right evaluation | |
141 | // Search for || operators, recurse to solve (so we don't have to handle special cases here) | |
142 | int and, or; | |
143 | and = ReverseFind(str, "&&"); | |
144 | or = ReverseFind(str, "||"); | |
145 | if ( (and != -1) || (or != -1) ) { | |
146 | wxString tag1, tag2; | |
147 | // handle the rightmost first to force left->right evaluation | |
148 | if (and > or) { | |
149 | return ( | |
150 | ParseIfStatementValue(tag2 = str.Mid(and+2)) && | |
151 | ParseIfStatementValue(tag1 = str.Mid(0, and)) ); | |
152 | } | |
153 | else { | |
154 | return ( | |
155 | ParseIfStatementValue(tag2 = str.Mid(or+2)) || | |
156 | ParseIfStatementValue(tag1 = str.Mid(0, or)) ); | |
157 | } | |
158 | ||
159 | } | |
160 | ||
161 | // By the time we get to this place in the function we are guarenteed to have a single | |
162 | // variable operation, perhaps with a NOT or ! operator | |
163 | bool notval = false; | |
164 | ||
165 | // search for a NOT or ! operator | |
166 | if (str.Mid(0, 1) == "!") { | |
167 | str.Remove(0, 1); | |
168 | str.Trim(false); // trim spaces from left | |
169 | notval = true; | |
170 | } | |
171 | else if (str.Mid(0,4).CmpNoCase("NOT ") == 0) { | |
172 | str.Remove(0, 4); | |
173 | str.Trim(false); // trim any extra spaces from left | |
174 | notval = true; | |
175 | } | |
176 | ||
177 | // now all we have left is the name of the class or a hardcoded 0 or 1 | |
178 | ||
179 | if (str == "") { | |
180 | #ifdef CHECKED | |
181 | wxMessageBox("wxHTML #if\\else error: Empty expression in #if\\#elif statement.","Error",wxICON_ERROR); | |
182 | #endif | |
183 | return true; | |
184 | } | |
185 | ||
186 | // check for hardcoded 0 and 1 cases, (these are used by parenthesis catcher) | |
187 | // this just decomplicates the recursion algorithm | |
188 | if (str == "0") return notval; | |
189 | if (str == "1") return !notval; | |
190 | ||
191 | // Grab the value from the variable class identified by cname | |
192 | bool value = wxIfElseVariable::FindValue(str); | |
193 | if (notval) value = !value; | |
194 | return value; | |
195 | ||
196 | } | |
716cd410 KB |
197 | /**************************************************************************** |
198 | PARAMETERS: | |
199 | text - HTML to process for if/else blocks | |
200 | ||
201 | RETURNS: | |
202 | The string containing the processed HTML | |
203 | ||
204 | REMARKS: | |
19193a2c | 205 | This function replaces #if, #else, #elif, and #endif directives with the text |
716cd410 KB |
206 | contained within the blocks, dependant on the value of the given boolean |
207 | variable. The variable is created by making a sub class of wxIfElseVariable. | |
208 | Dynamic class construction is used at run time internally to create an instance | |
209 | of this class and access the value of the variable. | |
210 | ||
211 | SEE ALSO: | |
212 | wxIfElseVariable | |
213 | ****************************************************************************/ | |
214 | wxString wxIfElsePrep::Process( | |
215 | const wxString& text) const | |
216 | { | |
217 | int b; | |
218 | char ft[] = "<!--#if "; | |
19193a2c KB |
219 | char ftend[] = "<!--#endif-->"; |
220 | char ftelse[] = "<!--#else-->"; | |
221 | char ftnot[] = "<!--#if not "; | |
505710ca KB |
222 | char ftnot2[] = "<!--#if !"; |
223 | ||
19193a2c KB |
224 | char ftelif[] = "<!--#elif "; |
225 | ||
226 | // make a copy so we can replace text as we go without affecting the original | |
716cd410 | 227 | wxString output = text; |
19193a2c KB |
228 | |
229 | // Avoid duplication of our parsing code by turning any #elif blocks into appropriate | |
230 | // else/if blocks | |
231 | while ((b = ReverseFind(output.Lower(), ftelif)) != -1) { | |
232 | int e; | |
233 | // Replace beginning of block | |
234 | e = output.find("-->", b + strlen(ftelif)); | |
235 | ||
236 | if (e == wxString::npos) { | |
237 | #ifdef CHECKED | |
238 | wxMessageBox("wxHTML #elif error: Premature end of file while parsing #elif.","Error",wxICON_ERROR); | |
239 | #endif | |
240 | break; | |
241 | } | |
242 | ||
243 | // Convert to lower case so find is easy, grab everything after #elif tag | |
244 | wxString remains = (output.Mid(e+strlen("-->"))).Lower(); | |
245 | ||
246 | // find matching else or endif | |
247 | int nextif, nextendif; | |
248 | int ifcount = 1, min = 0; | |
249 | do { | |
250 | nextif = remains.find(ft, min); | |
251 | nextendif = remains.find(ftend, min); | |
252 | if (nextif < nextendif && nextif != wxString::npos) { | |
253 | ifcount++; | |
254 | min = nextif+1; | |
255 | } | |
256 | else { | |
257 | ifcount--; | |
258 | min = nextendif+1; | |
259 | } | |
260 | ||
261 | if (nextendif == wxString::npos) { | |
262 | #ifdef CHECKED | |
263 | wxMessageBox("wxHTML #elif error: Premature end of file before finding #endif.","Error",wxICON_ERROR); | |
264 | #endif | |
265 | break; | |
266 | } | |
267 | // once ifcount reaches 0 again we have found our matchin #endif | |
268 | } while (ifcount > 0); | |
269 | ||
270 | // If it couldn't be found die gracefully | |
271 | if (nextendif == wxString::npos) { | |
272 | // We already displayed a message, just break all the way out | |
273 | break; | |
274 | } | |
275 | ||
276 | int elifsize = e - (b + strlen(ftelif)) + strlen("-->"); | |
277 | // Create the #if/else block, removing the #elif code | |
278 | output = output.Mid(0, b) + | |
279 | wxString(wxString(ftelse)+wxString(ft)) + | |
280 | output.Mid(b+strlen(ftelif), elifsize+nextendif) + | |
281 | wxString(ftend) + | |
282 | output.Mid(b+strlen(ftelif)+elifsize+nextendif); | |
283 | ||
284 | } | |
285 | ||
286 | // Parse out the if else blocks themselves | |
716cd410 | 287 | while ((b = ReverseFind(output.Lower(), ft)) != -1) { |
505710ca | 288 | // Loop until every #if directive is found |
716cd410 KB |
289 | // We search from the end of the string so that #if statements will properly recurse |
290 | // and we avoid the hassle of matching statements with the correct <!--#endif--> | |
505710ca KB |
291 | bool notval = false; |
292 | int off = 0; | |
19193a2c | 293 | int end; |
716cd410 KB |
294 | wxString usecode, code; |
295 | wxString cname; | |
296 | wxString tag; | |
297 | bool value; | |
298 | ||
299 | code = wxString(""); | |
300 | ||
301 | // grab the tag and get the name of the variable | |
302 | end = (output.Mid(b)).Find("-->"); | |
303 | if (end == -1) { | |
304 | #ifdef CHECKED | |
305 | wxMessageBox("wxHTML #if error: Premature end of file while parsing #if.","Error",wxICON_ERROR); | |
306 | #endif | |
307 | break; | |
308 | } | |
309 | ||
310 | end += 3; | |
19193a2c KB |
311 | // remove the <!--#if and --> sections from the tag before passing it on to be parsed |
312 | tag = output.Mid(b+strlen(ft), end-strlen(ft)-3); | |
716cd410 KB |
313 | output.Remove(b, end); |
314 | ||
19193a2c | 315 | value = ParseIfStatementValue(tag); |
716cd410 KB |
316 | |
317 | // Find the end of the tag (<!--#endif-->) and copy it all into the variable code | |
19193a2c | 318 | end = ((output.Mid(b)).Lower()).Find(ftend); |
716cd410 KB |
319 | if (end == -1) { |
320 | #ifdef CHECKED | |
321 | wxMessageBox("wxHTML #if error: Premature end of file while searching for matching #endif.","Error",wxICON_ERROR); | |
322 | #endif | |
323 | break; | |
324 | } | |
325 | ||
326 | code = output.Mid(b, end); | |
19193a2c | 327 | output.Remove(b, end+strlen(ftend)); // remove the entire #if block from original document |
716cd410 KB |
328 | |
329 | // Find out if there is an else statement | |
19193a2c | 330 | end = (code.Lower()).Find(ftelse); |
716cd410 KB |
331 | if (end != -1) { |
332 | if (!value) { | |
333 | // Use the else statement | |
19193a2c | 334 | usecode = code.Mid(end+strlen(ftelse)); |
716cd410 KB |
335 | } |
336 | else { | |
337 | // Use statement before #else | |
338 | usecode = code.Mid(0, end); | |
339 | } | |
340 | } | |
341 | else if (value) { | |
342 | // There is no #else statement | |
343 | usecode = code; | |
344 | } | |
345 | ||
346 | if (usecode != wxString("")) | |
347 | output = (output.Mid(0,b) + usecode + output.Mid(b)); | |
348 | } | |
349 | ||
350 | return output; | |
351 | } | |
352 | ||
353 | FORCE_LINK(ifelsevar) |