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