winhlp32: Properly handle a syntax error inside a macro.
[wine] / programs / winhlp32 / macro.lex.l
1 %{ /* -*-C-*- */
2 /*
3  * Help Viewer
4  *
5  * Copyright 1996 Ulrich Schmid
6  * Copyright 2002,2008 Eric Pouech
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22 %}
23 %option noinput nounput interactive 8bit
24 %x quote
25 %{
26 #include "config.h"
27 #include <assert.h>
28
29 #ifndef HAVE_UNISTD_H
30 #define YY_NO_UNISTD_H
31 #endif
32
33 #include "macro.h"
34
35 #include "wine/debug.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(winhelp);
38
39 struct lex_data {
40     LPCSTR   macroptr;
41     LPSTR    strptr;
42     int      quote_stack[32];
43     unsigned quote_stk_idx;
44     LPSTR    cache_string[32];
45     int      cache_used;
46 };
47 static struct lex_data* lex_data = NULL;
48
49 struct lexret  yylval;
50
51 #define YY_INPUT(buf,result,max_size)\
52   if ((result = *lex_data->macroptr ? 1 : 0)) buf[0] = *lex_data->macroptr++;
53
54 %}
55 %%
56
57 [-+]?[0-9]+             yylval.integer = strtol(yytext, NULL, 10);      return INTEGER;
58 [-+]?0[xX][0-9a-f]+     yylval.integer = strtol(yytext, NULL, 16);      return INTEGER;
59
60 [a-zA-Z][_0-9a-zA-Z]*   return MACRO_Lookup(yytext, &yylval);
61
62 \`          |
63 \"          |
64 \'          |
65 <quote>\`   |
66 <quote>\"   |
67 <quote>\'   {
68     if (lex_data->quote_stk_idx == 0 ||
69         (yytext[0] == '\"' && lex_data->quote_stack[lex_data->quote_stk_idx - 1] != '\"') ||
70         (yytext[0] == '`'))
71     {
72         /* opening a new one */
73         if (lex_data->quote_stk_idx == 0)
74         {
75             assert(lex_data->cache_used < sizeof(lex_data->cache_string) / sizeof(lex_data->cache_string[0]));
76             lex_data->strptr = lex_data->cache_string[lex_data->cache_used] = HeapAlloc(GetProcessHeap(), 0, strlen(lex_data->macroptr) + 1);
77             yylval.string = lex_data->strptr;
78             lex_data->cache_used++;
79             BEGIN(quote);
80         }
81         else *lex_data->strptr++ = yytext[0];
82         lex_data->quote_stack[lex_data->quote_stk_idx++] = yytext[0];
83         assert(lex_data->quote_stk_idx < sizeof(lex_data->quote_stack) / sizeof(lex_data->quote_stack[0]));
84     }
85     else
86     {
87         if (yytext[0] == '`') assert(0);
88         /* close the current quote */
89         if (--lex_data->quote_stk_idx == 0)
90         {
91             BEGIN INITIAL;
92             *lex_data->strptr++ = '\0';
93             return STRING;
94         }
95         else *lex_data->strptr++ = yytext[0];
96     }
97 }
98
99 <quote>.                *lex_data->strptr++ = yytext[0];
100 <quote>\\.              *lex_data->strptr++ = yytext[1];
101 <quote><<EOF>>          return 0;
102
103 " "
104 .                       return yytext[0];
105 %%
106
107 #if 0
108 /* all code for testing macros */
109 #include "winhelp.h"
110 static CHAR szTestMacro[256];
111
112 static LRESULT CALLBACK MACRO_TestDialogProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
113 {
114     if (msg == WM_COMMAND && wParam == IDOK)
115     {
116         GetDlgItemText(hDlg, 99, szTestMacro, sizeof(szTestMacro));
117         EndDialog(hDlg, IDOK);
118         return TRUE;
119     }
120     return FALSE;
121 }
122
123 void macro_test(void)
124 {
125     WNDPROC lpfnDlg = MakeProcInstance(MACRO_TestDialogProc, Globals.hInstance);
126     DialogBox(Globals.hInstance, STRING_DIALOG_TEST, Globals.active_win->hMainWnd, (DLGPROC)lpfnDlg);
127     FreeProcInstance(lpfnDlg);
128     macro = szTestMacro;
129 }
130 #endif
131
132 /* small helper function for debug messages */
133 static const char* ts(int t)
134 {
135     static char c[2] = {0,0};
136
137     switch (t)
138     {
139     case EMPTY: return "EMPTY";
140     case VOID_FUNCTION: return "VOID_FUNCTION";
141     case BOOL_FUNCTION: return "BOOL_FUNCTION";
142     case INTEGER: return "INTEGER";
143     case STRING: return "STRING";
144     case IDENTIFIER: return "IDENTIFIER";
145     default: c[0] = (char)t; return c;
146     }
147 }
148
149 static int MACRO_CallBoolFunc(FARPROC fn, const char* args, void** ret);
150
151 /******************************************************************
152  *              MACRO_CheckArgs
153  *
154  * checks number of arguments against prototype, and stores arguments on
155  * stack pa for later call
156  * returns -1 on error, otherwise the number of pushed parameters
157  */
158 static int MACRO_CheckArgs(void* pa[], unsigned max, const char* args)
159 {
160     int t;
161     unsigned int len = 0, idx = 0;
162
163     WINE_TRACE("Checking %s\n", args);
164
165     if (yylex() != '(') {WINE_WARN("missing (\n");return -1;}
166
167     if (*args)
168     {
169         len = strlen(args);
170         for (;;)
171         {
172             t = yylex();
173             WINE_TRACE("Got %s <=> %c\n", ts(t), *args);
174
175             switch (*args)
176             {
177             case 'S': 
178                 if (t != STRING)
179                 {WINE_WARN("missing S\n");return -1;}
180                 pa[idx] = (void*)yylval.string;  
181                 break;
182             case 'U':
183             case 'I':
184                 if (t != INTEGER)
185                 {WINE_WARN("missing U\n");return -1;}   
186                 pa[idx] = LongToPtr(yylval.integer);
187                 break;
188             case 'B':
189                 if (t != BOOL_FUNCTION) 
190                 {WINE_WARN("missing B\n");return -1;}   
191                 if (MACRO_CallBoolFunc(yylval.function, yylval.proto, &pa[idx]) == 0)
192                     return -1;
193                 break;
194             default: 
195                 WINE_WARN("unexpected %s while args is %c\n", ts(t), *args);
196                 return -1;
197             }
198             idx++;
199             if (*++args == '\0') break;
200             t = yylex();
201             if (t == ')') goto CheckArgs_end;
202             if (t != ',') {WINE_WARN("missing ,\n");return -1;}
203             if (idx >= max) {WINE_FIXME("stack overflow (%d)\n", max);return -1;}
204         }
205     }
206     if (yylex() != ')') {WINE_WARN("missing )\n");return -1;}
207
208 CheckArgs_end:
209     while (len > idx) pa[--len] = NULL;
210     return idx;
211 }
212
213 /******************************************************************
214  *              MACRO_CallBoolFunc
215  *
216  * Invokes boolean function fn, which arguments are defined by args
217  * stores bool result into ret
218  */
219 static int MACRO_CallBoolFunc(FARPROC fn, const char* args, void** ret)
220 {
221     void*       pa[2];
222     int         idx = MACRO_CheckArgs(pa, sizeof(pa)/sizeof(pa[0]), args);
223
224     if (idx < 0) return 0;
225     if (!fn)     return 1;
226
227     WINE_TRACE("calling with %u pmts\n", idx);
228
229     switch (strlen(args))
230     {
231     case 0: *ret = (void*)(fn)();          break;
232     case 1: *ret = (void*)(fn)(pa[0]);     break;
233     default: WINE_FIXME("NIY\n");
234     }
235
236     return 1;
237 }
238
239 /******************************************************************
240  *              MACRO_CallVoidFunc
241  *
242  *
243  */
244 static int MACRO_CallVoidFunc(FARPROC fn, const char* args)
245 {
246     void*       pa[6];
247     int         idx = MACRO_CheckArgs(pa, sizeof(pa)/sizeof(pa[0]), args);
248
249     if (idx < 0) return 0;
250     if (!fn)     return 1;
251
252     WINE_TRACE("calling %p with %u pmts\n", fn, idx);
253
254     switch (strlen(args))
255     {
256     case 0: (fn)();                                     break;
257     case 1: (fn)(pa[0]);                                break;
258     case 2: (fn)(pa[0],pa[1]);                          break;
259     case 3: (fn)(pa[0],pa[1],pa[2]);                    break;
260     case 4: (fn)(pa[0],pa[1],pa[2],pa[3]);              break;
261     case 5: (fn)(pa[0],pa[1],pa[2],pa[3],pa[4]);        break;
262     case 6: (fn)(pa[0],pa[1],pa[2],pa[3],pa[4],pa[5]);  break;
263     default: WINE_FIXME("NIY\n");
264     }
265
266     return 1;
267 }
268
269 BOOL MACRO_ExecuteMacro(LPCSTR macro)
270 {
271     struct lex_data     curr_lex_data, *prev_lex_data;
272     BOOL ret = TRUE;
273     int t;
274
275     WINE_TRACE("%s\n", wine_dbgstr_a(macro));
276
277     prev_lex_data = lex_data;
278     lex_data = &curr_lex_data;
279
280     memset(lex_data, 0, sizeof(*lex_data));
281     lex_data->macroptr = macro;
282
283     while ((t = yylex()) != EMPTY)
284     {
285         switch (t)
286         {
287         case VOID_FUNCTION:
288             WINE_TRACE("got type void func(%s)\n", yylval.proto);
289             MACRO_CallVoidFunc(yylval.function, yylval.proto);
290             break;
291         case BOOL_FUNCTION:
292             WINE_WARN("got type bool func(%s)\n", yylval.proto);
293             break;
294         default:
295             WINE_WARN("got unexpected type %s\n", ts(t));
296             YY_FLUSH_BUFFER;
297             ret = FALSE;
298             goto done;
299         }
300         switch (t = yylex())
301         {
302         case EMPTY:     goto done;
303         case ';':       break;
304         default:        ret = FALSE; YY_FLUSH_BUFFER; goto done;
305         }
306     }
307
308 done:
309     for (t = 0; t < lex_data->cache_used; t++)
310         HeapFree(GetProcessHeap(), 0, lex_data->cache_string[t]);
311     lex_data = prev_lex_data;
312
313     return ret;
314 }
315
316 #ifndef yywrap
317 int yywrap(void) { return 1; }
318 #endif