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 never-interactive 8bit
24 %x quote
25 %{
26 #include "config.h"
27 #include <assert.h>
28 #include <stdarg.h>
29 
30 #define YY_NO_UNISTD_H
31 #include "windef.h"
32 #include "winbase.h"
33 #include "wingdi.h"
34 #include "winuser.h"
35 #include "winhelp.h"
36 
37 #include "wine/debug.h"
38 
39 WINE_DEFAULT_DEBUG_CHANNEL(winhelp);
40 
41 struct lex_data {
42     LPCSTR   macroptr;
43     LPSTR    strptr;
44     int      quote_stack[32];
45     unsigned quote_stk_idx;
46     LPSTR    cache_string[32];
47     int      cache_used;
48     WINHELP_WINDOW* window;
49 };
50 static struct lex_data* lex_data = NULL;
51 
52 struct lexret  yylval;
53 
54 #define YY_INPUT(buf,result,max_size)\
55   if ((result = *lex_data->macroptr ? 1 : 0)) buf[0] = *lex_data->macroptr++;
56 
57 %}
58 %%
59 
60 [-+]?[0-9]+             yylval.integer = strtol(yytext, NULL, 10);	return INTEGER;
61 [-+]?0[xX][0-9a-f]+	yylval.integer = strtol(yytext, NULL, 16);	return INTEGER;
62 
63 [a-zA-Z][_0-9a-zA-Z]*   return MACRO_Lookup(yytext, &yylval);
64 
65 \`	    |
66 \"	    |
67 \'          |
68 <quote>\`   |
69 <quote>\"   |
70 <quote>\'   {
71     if (lex_data->quote_stk_idx == 0 ||
72         (yytext[0] == '\"' && lex_data->quote_stack[lex_data->quote_stk_idx - 1] != '\"') ||
73         (yytext[0] == '`'))
74     {
75         /* opening a new one */
76         if (lex_data->quote_stk_idx == 0)
77         {
78             assert(lex_data->cache_used < ARRAY_SIZE(lex_data->cache_string));
79             lex_data->strptr = lex_data->cache_string[lex_data->cache_used] = HeapAlloc(GetProcessHeap(), 0, strlen(lex_data->macroptr) + 1);
80             yylval.string = lex_data->strptr;
81             lex_data->cache_used++;
82             BEGIN(quote);
83         }
84         else *lex_data->strptr++ = yytext[0];
85         lex_data->quote_stack[lex_data->quote_stk_idx++] = yytext[0];
86         assert(lex_data->quote_stk_idx < ARRAY_SIZE(lex_data->quote_stack));
87     }
88     else
89     {
90         if (yytext[0] == '`') assert(0);
91         /* close the current quote */
92         if (--lex_data->quote_stk_idx == 0)
93         {
94             BEGIN INITIAL;
95             *lex_data->strptr++ = '\0';
96             return STRING;
97         }
98         else *lex_data->strptr++ = yytext[0];
99     }
100 }
101 
102 <quote>.                *lex_data->strptr++ = yytext[0];
103 <quote>\\.	        *lex_data->strptr++ = yytext[1];
104 <quote><<EOF>>	        return 0;
105 
106 " "
107 .			return yytext[0];
108 %%
109 
110 #if 0
111 /* all code for testing macros */
112 #include "winhelp.h"
113 static CHAR szTestMacro[256];
114 
115 static LRESULT CALLBACK MACRO_TestDialogProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
116 {
117     if (msg == WM_COMMAND && wParam == IDOK)
118     {
119         GetDlgItemText(hDlg, 99, szTestMacro, sizeof(szTestMacro));
120         EndDialog(hDlg, IDOK);
121         return TRUE;
122     }
123     return FALSE;
124 }
125 
126 void macro_test(void)
127 {
128     WNDPROC lpfnDlg = MakeProcInstance(MACRO_TestDialogProc, Globals.hInstance);
129     DialogBox(Globals.hInstance, STRING_DIALOG_TEST, Globals.active_win->hMainWnd, (DLGPROC)lpfnDlg);
130     FreeProcInstance(lpfnDlg);
131     macro = szTestMacro;
132 }
133 #endif
134 
135 /* small helper function for debug messages */
136 static const char* ts(int t)
137 {
138     static char c[2] = {0,0};
139 
140     switch (t)
141     {
142     case EMPTY: return "EMPTY";
143     case VOID_FUNCTION: return "VOID_FUNCTION";
144     case BOOL_FUNCTION: return "BOOL_FUNCTION";
145     case INTEGER: return "INTEGER";
146     case STRING: return "STRING";
147     case IDENTIFIER: return "IDENTIFIER";
148     default: c[0] = (char)t; return c;
149     }
150 }
151 
152 static int MACRO_CallBoolFunc(void *fn, const char* args, void** ret);
153 
154 /******************************************************************
155  *		MACRO_CheckArgs
156  *
157  * checks number of arguments against prototype, and stores arguments on
158  * stack pa for later call
159  * returns -1 on error, otherwise the number of pushed parameters
160  */
161 static int MACRO_CheckArgs(void* pa[], unsigned max, const char* args)
162 {
163     int t;
164     unsigned int len = 0, idx = 0;
165 
166     WINE_TRACE("Checking %s\n", debugstr_a(args));
167 
168     if (yylex() != '(') {WINE_WARN("missing (\n");return -1;}
169 
170     if (*args)
171     {
172         len = strlen(args);
173         for (;;)
174         {
175             t = yylex();
176             WINE_TRACE("Got %s <=> %c\n", debugstr_a(ts(t)), *args);
177 
178             switch (*args)
179             {
180             case 'S':
181                 if (t != STRING)
182                 {WINE_WARN("missing S\n");return -1;}
183                 pa[idx] = (void*)yylval.string;
184                 break;
185             case 'U':
186             case 'I':
187                 if (t != INTEGER)
188                 {WINE_WARN("missing U\n");return -1;}
189                 pa[idx] = LongToPtr(yylval.integer);
190                 break;
191             case 'B':
192                 if (t != BOOL_FUNCTION)
193                 {WINE_WARN("missing B\n");return -1;}
194                 if (MACRO_CallBoolFunc(yylval.function, yylval.proto, &pa[idx]) == 0)
195                     return -1;
196                 break;
197             default:
198                 WINE_WARN("unexpected %s while args is %c\n", debugstr_a(ts(t)), *args);
199                 return -1;
200             }
201             idx++;
202             if (*++args == '\0') break;
203             t = yylex();
204             if (t == ')') goto CheckArgs_end;
205             if (t != ',') {WINE_WARN("missing ,\n");return -1;}
206             if (idx >= max) {WINE_FIXME("stack overflow (%d)\n", max);return -1;}
207         }
208     }
209     if (yylex() != ')') {WINE_WARN("missing )\n");return -1;}
210 
211 CheckArgs_end:
212     while (len > idx) pa[--len] = NULL;
213     return idx;
214 }
215 
216 /******************************************************************
217  *		MACRO_CallBoolFunc
218  *
219  * Invokes boolean function fn, which arguments are defined by args
220  * stores bool result into ret
221  */
222 static int MACRO_CallBoolFunc(void *fn, const char* args, void** ret)
223 {
224     void*       pa[2];
225     int         idx = MACRO_CheckArgs(pa, ARRAY_SIZE(pa), args);
226 
227     if (idx < 0) return 0;
228     if (!fn)     return 1;
229 
230     WINE_TRACE("calling with %u pmts\n", idx);
231 
232     switch (strlen(args))
233     {
234     case 0:
235     {
236         BOOL (WINAPI *func)(void) = fn;
237         *ret = (void *)(ULONG_PTR)func();
238         break;
239     }
240     case 1:
241     {
242         BOOL (WINAPI *func)(void *) = fn;
243         *ret = (void *)(ULONG_PTR)func( pa[0]);
244         break;
245     }
246     default: WINE_FIXME("NIY\n");
247     }
248 
249     return 1;
250 }
251 
252 /******************************************************************
253  *		MACRO_CallVoidFunc
254  *
255  *
256  */
257 static int MACRO_CallVoidFunc(void *fn, const char* args)
258 {
259     void*       pa[6];
260     int         idx = MACRO_CheckArgs(pa, ARRAY_SIZE(pa), args);
261 
262     if (idx < 0) return 0;
263     if (!fn)     return 1;
264 
265     WINE_TRACE("calling %p with %u pmts\n", fn, idx);
266 
267     switch (strlen(args))
268     {
269     case 0:
270     {
271         void (WINAPI *func)(void) = fn;
272         func();
273         break;
274     }
275     case 1:
276     {
277         void (WINAPI *func)(void*) = fn;
278         func( pa[0] );
279         break;
280     }
281     case 2:
282     {
283         void (WINAPI *func)(void*,void*) = fn;
284         func( pa[0], pa[1] );
285         break;
286     }
287     case 3:
288     {
289         void (WINAPI *func)(void*,void*,void*) = fn;
290         func( pa[0], pa[1], pa[2] );
291         break;
292     }
293     case 4:
294     {
295         void (WINAPI *func)(void*,void*,void*,void*) = fn;
296         func( pa[0], pa[1], pa[2], pa[3] );
297         break;
298     }
299     case 5:
300     {
301         void (WINAPI *func)(void*,void*,void*,void*,void*) = fn;
302         func( pa[0], pa[1], pa[2], pa[3], pa[4] );
303         break;
304     }
305     case 6:
306     {
307         void (WINAPI *func)(void*,void*,void*,void*,void*,void*) = fn;
308         func( pa[0], pa[1], pa[2], pa[3], pa[4], pa[5] );
309         break;
310     }
311     default: WINE_FIXME("NIY\n");
312     }
313 
314     return 1;
315 }
316 
317 BOOL MACRO_ExecuteMacro(WINHELP_WINDOW* window, LPCSTR macro)
318 {
319     struct lex_data     curr_lex_data, *prev_lex_data;
320     BOOL ret = TRUE;
321     int t;
322 
323     WINE_TRACE("%s\n", debugstr_a(macro));
324 
325     prev_lex_data = lex_data;
326     lex_data = &curr_lex_data;
327 
328     memset(lex_data, 0, sizeof(*lex_data));
329     lex_data->macroptr = macro;
330     lex_data->window = WINHELP_GrabWindow(window);
331 
332     while ((t = yylex()) != EMPTY)
333     {
334         switch (t)
335         {
336         case VOID_FUNCTION:
337             WINE_TRACE("got type void func(%s)\n", debugstr_a(yylval.proto));
338             MACRO_CallVoidFunc(yylval.function, yylval.proto);
339             break;
340         case BOOL_FUNCTION:
341             WINE_WARN("got type bool func(%s)\n", debugstr_a(yylval.proto));
342             break;
343         default:
344             WINE_WARN("got unexpected type %s\n", debugstr_a(ts(t)));
345             YY_FLUSH_BUFFER;
346             ret = FALSE;
347             goto done;
348         }
349         switch (t = yylex())
350         {
351         case EMPTY:     goto done;
352         case ';':       break;
353         default:        ret = FALSE; YY_FLUSH_BUFFER; goto done;
354         }
355     }
356 
357 done:
358     for (t = 0; t < lex_data->cache_used; t++)
359         HeapFree(GetProcessHeap(), 0, lex_data->cache_string[t]);
360     lex_data = prev_lex_data;
361     WINHELP_ReleaseWindow(window);
362 
363     return ret;
364 }
365 
366 WINHELP_WINDOW* MACRO_CurrentWindow(void)
367 {
368     return lex_data ? lex_data->window : Globals.active_win;
369 }
370 
371 #ifndef yywrap
372 int yywrap(void) { return 1; }
373 #endif
374