Added support for loading .res files for 16-bit resources.
[wine] / tools / winebuild / parser.c
1 /*
2  * Spec file parser
3  *
4  * Copyright 1993 Robert J. Amstadt
5  * Copyright 1995 Martin von Loewis
6  * Copyright 1995, 1996, 1997 Alexandre Julliard
7  * Copyright 1997 Eric Youngdale
8  * Copyright 1999 Ulrich Weigand
9  */
10
11 #include <assert.h>
12 #include <ctype.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <unistd.h>
17
18 #include "build.h"
19
20 int current_line = 0;
21
22 static SPEC_TYPE SpecType = SPEC_INVALID;
23
24 static char ParseBuffer[512];
25 static char *ParseNext = ParseBuffer;
26 static char ParseSaveChar;
27 static FILE *input_file;
28
29 static const char * const TypeNames[TYPE_NBTYPES] =
30 {
31     "byte",         /* TYPE_BYTE */
32     "word",         /* TYPE_WORD */
33     "long",         /* TYPE_LONG */
34     "pascal16",     /* TYPE_PASCAL_16 */
35     "pascal",       /* TYPE_PASCAL */
36     "equate",       /* TYPE_ABS */
37     "register",     /* TYPE_REGISTER */
38     "interrupt",    /* TYPE_INTERRUPT */
39     "stub",         /* TYPE_STUB */
40     "stdcall",      /* TYPE_STDCALL */
41     "stdcall64",    /* TYPE_STDCALL64 */
42     "cdecl",        /* TYPE_CDECL */
43     "varargs",      /* TYPE_VARARGS */
44     "extern",       /* TYPE_EXTERN */
45     "forward"       /* TYPE_FORWARD */
46 };
47
48 /* callback function used for stub functions */
49 #define STUB_CALLBACK \
50   ((SpecType == SPEC_WIN16) ? "RELAY_Unimplemented16": "RELAY_Unimplemented32")
51
52 static int IsNumberString(char *s)
53 {
54     while (*s) if (!isdigit(*s++)) return 0;
55     return 1;
56 }
57
58 static char * GetTokenInLine(void)
59 {
60     char *p;
61     char *token;
62
63     if (ParseNext != ParseBuffer)
64     {
65         if (ParseSaveChar == '\0')
66             return NULL;
67         *ParseNext = ParseSaveChar;
68     }
69     
70     /*
71      * Remove initial white space.
72      */
73     for (p = ParseNext; isspace(*p); p++)
74         ;
75     
76     if ((*p == '\0') || (*p == '#'))
77         return NULL;
78     
79     /*
80      * Find end of token.
81      */
82     token = p++;
83     if (*token != '(' && *token != ')')
84         while (*p != '\0' && *p != '(' && *p != ')' && !isspace(*p))
85             p++;
86     
87     ParseSaveChar = *p;
88     ParseNext = p;
89     *p = '\0';
90
91     return token;
92 }
93
94 static char * GetToken(void)
95 {
96     char *token;
97
98     while ((token = GetTokenInLine()) == NULL)
99     {
100         ParseNext = ParseBuffer;
101         while (1)
102         {
103             current_line++;
104             if (fgets(ParseBuffer, sizeof(ParseBuffer), input_file) == NULL)
105                 return NULL;
106             if (ParseBuffer[0] != '#')
107                 break;
108         }
109     }
110     return token;
111 }
112
113
114 /*******************************************************************
115  *         ParseVariable
116  *
117  * Parse a variable definition.
118  */
119 static void ParseVariable( ORDDEF *odp )
120 {
121     char *endptr;
122     int *value_array;
123     int n_values;
124     int value_array_size;
125     
126     char *token = GetToken();
127     if (*token != '(') fatal_error( "Expected '(' got '%s'\n", token );
128
129     n_values = 0;
130     value_array_size = 25;
131     value_array = xmalloc(sizeof(*value_array) * value_array_size);
132     
133     while ((token = GetToken()) != NULL)
134     {
135         if (*token == ')')
136             break;
137
138         value_array[n_values++] = strtol(token, &endptr, 0);
139         if (n_values == value_array_size)
140         {
141             value_array_size += 25;
142             value_array = xrealloc(value_array, 
143                                    sizeof(*value_array) * value_array_size);
144         }
145         
146         if (endptr == NULL || *endptr != '\0')
147             fatal_error( "Expected number value, got '%s'\n", token );
148     }
149     
150     if (token == NULL)
151         fatal_error( "End of file in variable declaration\n" );
152
153     odp->u.var.n_values = n_values;
154     odp->u.var.values = xrealloc(value_array, sizeof(*value_array) * n_values);
155 }
156
157
158 /*******************************************************************
159  *         ParseExportFunction
160  *
161  * Parse a function definition.
162  */
163 static void ParseExportFunction( ORDDEF *odp )
164 {
165     char *token;
166     int i;
167
168     switch(SpecType)
169     {
170     case SPEC_WIN16:
171         if (odp->type == TYPE_STDCALL || odp->type == TYPE_STDCALL64)
172             fatal_error( "'stdcall' not supported for Win16\n" );
173         if (odp->type == TYPE_VARARGS)
174             fatal_error( "'varargs' not supported for Win16\n" );
175         break;
176     case SPEC_WIN32:
177         if ((odp->type == TYPE_PASCAL) || (odp->type == TYPE_PASCAL_16))
178             fatal_error( "'pascal' not supported for Win32\n" );
179         break;
180     default:
181         break;
182     }
183
184     token = GetToken();
185     if (*token != '(') fatal_error( "Expected '(' got '%s'\n", token );
186
187     for (i = 0; i < sizeof(odp->u.func.arg_types); i++)
188     {
189         token = GetToken();
190         if (*token == ')')
191             break;
192
193         if (!strcmp(token, "word"))
194             odp->u.func.arg_types[i] = 'w';
195         else if (!strcmp(token, "s_word"))
196             odp->u.func.arg_types[i] = 's';
197         else if (!strcmp(token, "long") || !strcmp(token, "segptr"))
198             odp->u.func.arg_types[i] = 'l';
199         else if (!strcmp(token, "ptr"))
200             odp->u.func.arg_types[i] = 'p';
201         else if (!strcmp(token, "str"))
202             odp->u.func.arg_types[i] = 't';
203         else if (!strcmp(token, "wstr"))
204             odp->u.func.arg_types[i] = 'W';
205         else if (!strcmp(token, "segstr"))
206             odp->u.func.arg_types[i] = 'T';
207         else if (!strcmp(token, "double"))
208         {
209             odp->u.func.arg_types[i++] = 'l';
210             if (i < sizeof(odp->u.func.arg_types)) odp->u.func.arg_types[i] = 'l';
211         }
212         else fatal_error( "Unknown variable type '%s'\n", token );
213
214         if (SpecType == SPEC_WIN32)
215         {
216             if (strcmp(token, "long") &&
217                 strcmp(token, "ptr") &&
218                 strcmp(token, "str") &&
219                 strcmp(token, "wstr") &&
220                 strcmp(token, "double"))
221             {
222                 fatal_error( "Type '%s' not supported for Win32\n", token );
223             }
224         }
225     }
226     if ((*token != ')') || (i >= sizeof(odp->u.func.arg_types)))
227         fatal_error( "Too many arguments\n" );
228
229     odp->u.func.arg_types[i] = '\0';
230     if ((odp->type == TYPE_STDCALL) && !i)
231         odp->type = TYPE_CDECL; /* stdcall is the same as cdecl for 0 args */
232     strcpy(odp->u.func.link_name, GetToken());
233 }
234
235
236 /*******************************************************************
237  *         ParseEquate
238  *
239  * Parse an 'equate' definition.
240  */
241 static void ParseEquate( ORDDEF *odp )
242 {
243     char *endptr;
244     
245     char *token = GetToken();
246     int value = strtol(token, &endptr, 0);
247     if (endptr == NULL || *endptr != '\0')
248         fatal_error( "Expected number value, got '%s'\n", token );
249     if (SpecType == SPEC_WIN32)
250         fatal_error( "'equate' not supported for Win32\n" );
251     odp->u.abs.value = value;
252 }
253
254
255 /*******************************************************************
256  *         ParseStub
257  *
258  * Parse a 'stub' definition.
259  */
260 static void ParseStub( ORDDEF *odp )
261 {
262     odp->u.func.arg_types[0] = '\0';
263     strcpy( odp->u.func.link_name, STUB_CALLBACK );
264 }
265
266
267 /*******************************************************************
268  *         ParseInterrupt
269  *
270  * Parse an 'interrupt' definition.
271  */
272 static void ParseInterrupt( ORDDEF *odp )
273 {
274     char *token;
275
276     if (SpecType == SPEC_WIN32)
277         fatal_error( "'interrupt' not supported for Win32\n" );
278
279     token = GetToken();
280     if (*token != '(') fatal_error( "Expected '(' got '%s'\n", token );
281
282     token = GetToken();
283     if (*token != ')') fatal_error( "Expected ')' got '%s'\n", token );
284
285     odp->u.func.arg_types[0] = '\0';
286     strcpy( odp->u.func.link_name, GetToken() );
287 }
288
289
290 /*******************************************************************
291  *         ParseExtern
292  *
293  * Parse an 'extern' definition.
294  */
295 static void ParseExtern( ORDDEF *odp )
296 {
297     if (SpecType == SPEC_WIN16) fatal_error( "'extern' not supported for Win16\n" );
298     strcpy( odp->u.ext.link_name, GetToken() );
299 }
300
301
302 /*******************************************************************
303  *         ParseForward
304  *
305  * Parse a 'forward' definition.
306  */
307 static void ParseForward( ORDDEF *odp )
308 {
309     if (SpecType == SPEC_WIN16) fatal_error( "'forward' not supported for Win16\n" );
310     strcpy( odp->u.fwd.link_name, GetToken() );
311 }
312
313
314 /*******************************************************************
315  *         fix_export_name
316  *
317  * Fix an exported function name by removing a possible @xx suffix
318  */
319 static void fix_export_name( char *name )
320 {
321     char *p, *end = strrchr( name, '@' );
322     if (!end || !end[1] || end == name) return;
323     /* make sure all the rest is digits */
324     for (p = end + 1; *p; p++) if (!isdigit(*p)) return;
325     *end = 0;
326 }
327
328 /*******************************************************************
329  *         ParseOrdinal
330  *
331  * Parse an ordinal definition.
332  */
333 static void ParseOrdinal(int ordinal)
334 {
335     char *token;
336
337     ORDDEF *odp = &EntryPoints[nb_entry_points++];
338
339     if (!(token = GetToken())) fatal_error( "Expected type after ordinal\n" );
340
341     for (odp->type = 0; odp->type < TYPE_NBTYPES; odp->type++)
342         if (TypeNames[odp->type] && !strcmp( token, TypeNames[odp->type] ))
343             break;
344
345     if (odp->type >= TYPE_NBTYPES)
346         fatal_error( "Expected type after ordinal, found '%s' instead\n", token );
347
348     if (!(token = GetToken())) fatal_error( "Expected name after type\n" );
349
350     strcpy( odp->name, token );
351     fix_export_name( odp->name );
352     odp->lineno = current_line;
353     odp->ordinal = ordinal;
354
355     switch(odp->type)
356     {
357     case TYPE_BYTE:
358     case TYPE_WORD:
359     case TYPE_LONG:
360         ParseVariable( odp );
361         break;
362     case TYPE_REGISTER:
363         ParseExportFunction( odp );
364 #ifndef __i386__
365         /* ignore Win32 'register' routines on non-Intel archs */
366         if (SpecType == SPEC_WIN32)
367         {
368             nb_entry_points--;
369             return;
370         }
371 #endif
372         break;
373     case TYPE_PASCAL_16:
374     case TYPE_PASCAL:
375     case TYPE_STDCALL:
376     case TYPE_STDCALL64:
377     case TYPE_VARARGS:
378     case TYPE_CDECL:
379         ParseExportFunction( odp );
380         break;
381     case TYPE_INTERRUPT:
382         ParseInterrupt( odp );
383         break;
384     case TYPE_ABS:
385         ParseEquate( odp );
386         break;
387     case TYPE_STUB:
388         ParseStub( odp );
389         break;
390     case TYPE_EXTERN:
391         ParseExtern( odp );
392         break;
393     case TYPE_FORWARD:
394         ParseForward( odp );
395         break;
396     default:
397         assert( 0 );
398     }
399
400     if (ordinal != -1)
401     {
402         if (ordinal >= MAX_ORDINALS) fatal_error( "Ordinal number %d too large\n", ordinal );
403         if (ordinal > Limit) Limit = ordinal;
404         if (ordinal < Base) Base = ordinal;
405         odp->ordinal = ordinal;
406         Ordinals[ordinal] = odp;
407     }
408
409     if (!strcmp( odp->name, "@" ))
410     {
411         if (ordinal == -1)
412             fatal_error( "Nameless function needs an explicit ordinal number\n" );
413         if (SpecType != SPEC_WIN32)
414             fatal_error( "Nameless functions not supported for Win16\n" );
415         odp->name[0] = 0;
416     }
417     else Names[nb_names++] = odp;
418 }
419
420
421 /*******************************************************************
422  *         ParseTopLevel
423  *
424  * Parse a spec file.
425  */
426 SPEC_TYPE ParseTopLevel( FILE *file )
427 {
428     char *token;
429
430     input_file = file;
431     current_line = 1;
432     while ((token = GetToken()) != NULL)
433     {
434         if (strcmp(token, "name") == 0)
435         {
436             strcpy(DLLName, GetToken());
437         }
438         else if (strcmp(token, "file") == 0)
439         {
440             strcpy(DLLFileName, GetToken());
441             strupper(DLLFileName);
442         }
443         else if (strcmp(token, "type") == 0)
444         {
445             token = GetToken();
446             if (!strcmp(token, "win16" )) SpecType = SPEC_WIN16;
447             else if (!strcmp(token, "win32" )) SpecType = SPEC_WIN32;
448             else fatal_error( "Type must be 'win16' or 'win32'\n" );
449         }
450         else if (strcmp(token, "mode") == 0)
451         {
452             token = GetToken();
453             if (!strcmp(token, "dll" )) SpecMode = SPEC_MODE_DLL;
454             else if (!strcmp(token, "guiexe" )) SpecMode = SPEC_MODE_GUIEXE;
455             else if (!strcmp(token, "cuiexe" )) SpecMode = SPEC_MODE_CUIEXE;
456             else if (!strcmp(token, "guiexe_no_main" )) SpecMode = SPEC_MODE_GUIEXE_NO_MAIN;
457             else if (!strcmp(token, "cuiexe_no_main" )) SpecMode = SPEC_MODE_CUIEXE_NO_MAIN;
458             else fatal_error( "Mode must be 'dll', 'guiexe', 'cuiexe', 'guiexe_no_main' or 'cuiexe_no_main'\n" );
459         }
460         else if (strcmp(token, "heap") == 0)
461         {
462             token = GetToken();
463             if (!IsNumberString(token)) fatal_error( "Expected number after heap\n" );
464             DLLHeapSize = atoi(token);
465         }
466         else if (strcmp(token, "init") == 0)
467         {
468             strcpy(DLLInitFunc, GetToken());
469             if (SpecType == SPEC_WIN16)
470                 fatal_error( "init cannot be used for Win16 spec files\n" );
471             if (!DLLInitFunc[0])
472                 fatal_error( "Expected function name after init\n" );
473             if (!strcmp(DLLInitFunc, "main"))
474                 fatal_error( "The init function cannot be named 'main'\n" );
475         }
476         else if (strcmp(token, "import") == 0)
477         {
478             if (SpecType != SPEC_WIN32)
479                 fatal_error( "Imports not supported for Win16\n" );
480             add_import_dll( GetToken() );
481         }
482         else if (strcmp(token, "rsrc") == 0)
483         {
484             if (SpecType != SPEC_WIN16) load_res32_file( GetToken() );
485             else load_res16_file( GetToken() );
486         }
487         else if (strcmp(token, "owner") == 0)
488         {
489             if (SpecType != SPEC_WIN16)
490                 fatal_error( "Owner only supported for Win16 spec files\n" );
491             strcpy( owner_name, GetToken() );
492         }
493         else if (strcmp(token, "@") == 0)
494         {
495             if (SpecType != SPEC_WIN32)
496                 fatal_error( "'@' ordinals not supported for Win16\n" );
497             ParseOrdinal( -1 );
498         }
499         else if (IsNumberString(token))
500         {
501             ParseOrdinal( atoi(token) );
502         }
503         else
504             fatal_error( "Expected name, id, length or ordinal\n" );
505     }
506
507     if (!DLLFileName[0])
508     {
509         if (SpecMode == SPEC_MODE_DLL)
510             sprintf( DLLFileName, "%s.dll", DLLName );
511         else
512             sprintf( DLLFileName, "%s.exe", DLLName );
513     }
514
515     if (SpecType == SPEC_INVALID) fatal_error( "Missing 'type' declaration\n" );
516     if (SpecType == SPEC_WIN16 && !owner_name[0])
517         fatal_error( "'owner' not specified for Win16 dll\n" );
518
519     current_line = 0;  /* no longer parsing the input file */
520     return SpecType;
521 }