Added entry point flags -noimport, -norelay, -ret64 and -i386.
[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 "config.h"
19 #include "winbase.h"
20 #include "build.h"
21
22 int current_line = 0;
23
24 static SPEC_TYPE SpecType = SPEC_INVALID;
25
26 static char ParseBuffer[512];
27 static char *ParseNext = ParseBuffer;
28 static char ParseSaveChar;
29 static FILE *input_file;
30
31 static const char * const TypeNames[TYPE_NBTYPES] =
32 {
33     "variable",     /* TYPE_VARIABLE */
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 static const char * const FlagNames[] =
48 {
49     "noimport",    /* FLAG_NOIMPORT */
50     "norelay",     /* FLAG_NORELAY */
51     "ret64",       /* FLAG_RET64 */
52     "i386",        /* FLAG_I386 */
53     NULL
54 };
55
56 static int IsNumberString(char *s)
57 {
58     while (*s) if (!isdigit(*s++)) return 0;
59     return 1;
60 }
61
62 inline static int is_token_separator( char ch )
63 {
64     return (ch == '(' || ch == ')' || ch == '-');
65 }
66
67 static char * GetTokenInLine(void)
68 {
69     char *p;
70     char *token;
71
72     if (ParseNext != ParseBuffer)
73     {
74         if (ParseSaveChar == '\0')
75             return NULL;
76         *ParseNext = ParseSaveChar;
77     }
78     
79     /*
80      * Remove initial white space.
81      */
82     for (p = ParseNext; isspace(*p); p++)
83         ;
84     
85     if ((*p == '\0') || (*p == '#'))
86         return NULL;
87     
88     /*
89      * Find end of token.
90      */
91     token = p++;
92     if (!is_token_separator(*token))
93         while (*p != '\0' && !is_token_separator(*p) && !isspace(*p))
94             p++;
95     
96     ParseSaveChar = *p;
97     ParseNext = p;
98     *p = '\0';
99
100     return token;
101 }
102
103 static char * GetToken( int allow_eof )
104 {
105     char *token;
106
107     while ((token = GetTokenInLine()) == NULL)
108     {
109         ParseNext = ParseBuffer;
110         while (1)
111         {
112             current_line++;
113             if (fgets(ParseBuffer, sizeof(ParseBuffer), input_file) == NULL)
114             {
115                 if (!allow_eof) fatal_error( "Unexpected end of file\n" );
116                 return NULL;
117             }
118             if (ParseBuffer[0] != '#')
119                 break;
120         }
121     }
122     return token;
123 }
124
125
126 /*******************************************************************
127  *         ParseDebug
128  *
129  * Parse a debug channel definition.
130  */
131 static void ParseDebug(void)
132 {
133     char *token = GetToken(0);
134     if (*token != '(') fatal_error( "Expected '(' got '%s'\n", token );
135     for (;;)
136     {
137         token = GetToken(0);
138         if (*token == ')') break;
139         debug_channels = xrealloc( debug_channels,
140                                    (nb_debug_channels + 1) * sizeof(*debug_channels));
141         debug_channels[nb_debug_channels++] = xstrdup(token);
142     }
143 }
144
145
146 /*******************************************************************
147  *         ParseVariable
148  *
149  * Parse a variable definition.
150  */
151 static void ParseVariable( ORDDEF *odp )
152 {
153     char *endptr;
154     int *value_array;
155     int n_values;
156     int value_array_size;
157     
158     char *token = GetToken(0);
159     if (*token != '(') fatal_error( "Expected '(' got '%s'\n", token );
160
161     n_values = 0;
162     value_array_size = 25;
163     value_array = xmalloc(sizeof(*value_array) * value_array_size);
164     
165     for (;;)
166     {
167         token = GetToken(0);
168         if (*token == ')')
169             break;
170
171         value_array[n_values++] = strtol(token, &endptr, 0);
172         if (n_values == value_array_size)
173         {
174             value_array_size += 25;
175             value_array = xrealloc(value_array, 
176                                    sizeof(*value_array) * value_array_size);
177         }
178         
179         if (endptr == NULL || *endptr != '\0')
180             fatal_error( "Expected number value, got '%s'\n", token );
181     }
182
183     odp->u.var.n_values = n_values;
184     odp->u.var.values = xrealloc(value_array, sizeof(*value_array) * n_values);
185 }
186
187
188 /*******************************************************************
189  *         ParseExportFunction
190  *
191  * Parse a function definition.
192  */
193 static void ParseExportFunction( ORDDEF *odp )
194 {
195     char *token;
196     unsigned int i;
197
198     switch(SpecType)
199     {
200     case SPEC_WIN16:
201         if (odp->type == TYPE_STDCALL)
202             fatal_error( "'stdcall' not supported for Win16\n" );
203         if (odp->type == TYPE_VARARGS)
204             fatal_error( "'varargs' not supported for Win16\n" );
205         break;
206     case SPEC_WIN32:
207         if ((odp->type == TYPE_PASCAL) || (odp->type == TYPE_PASCAL_16))
208             fatal_error( "'pascal' not supported for Win32\n" );
209         break;
210     default:
211         break;
212     }
213
214     token = GetToken(0);
215     if (*token != '(') fatal_error( "Expected '(' got '%s'\n", token );
216
217     for (i = 0; i < sizeof(odp->u.func.arg_types); i++)
218     {
219         token = GetToken(0);
220         if (*token == ')')
221             break;
222
223         if (!strcmp(token, "word"))
224             odp->u.func.arg_types[i] = 'w';
225         else if (!strcmp(token, "s_word"))
226             odp->u.func.arg_types[i] = 's';
227         else if (!strcmp(token, "long") || !strcmp(token, "segptr"))
228             odp->u.func.arg_types[i] = 'l';
229         else if (!strcmp(token, "ptr"))
230             odp->u.func.arg_types[i] = 'p';
231         else if (!strcmp(token, "str"))
232             odp->u.func.arg_types[i] = 't';
233         else if (!strcmp(token, "wstr"))
234             odp->u.func.arg_types[i] = 'W';
235         else if (!strcmp(token, "segstr"))
236             odp->u.func.arg_types[i] = 'T';
237         else if (!strcmp(token, "double"))
238         {
239             odp->u.func.arg_types[i++] = 'l';
240             if (i < sizeof(odp->u.func.arg_types)) odp->u.func.arg_types[i] = 'l';
241         }
242         else fatal_error( "Unknown variable type '%s'\n", token );
243
244         if (SpecType == SPEC_WIN32)
245         {
246             if (strcmp(token, "long") &&
247                 strcmp(token, "ptr") &&
248                 strcmp(token, "str") &&
249                 strcmp(token, "wstr") &&
250                 strcmp(token, "double"))
251             {
252                 fatal_error( "Type '%s' not supported for Win32\n", token );
253             }
254         }
255     }
256     if ((*token != ')') || (i >= sizeof(odp->u.func.arg_types)))
257         fatal_error( "Too many arguments\n" );
258
259     odp->u.func.arg_types[i] = '\0';
260     if ((odp->type == TYPE_STDCALL) && !i)
261         odp->type = TYPE_CDECL; /* stdcall is the same as cdecl for 0 args */
262     if (odp->type == TYPE_VARARGS)
263         odp->flags |= FLAG_NORELAY;  /* no relay debug possible for varags entry point */
264     strcpy(odp->u.func.link_name, GetToken(0));
265 }
266
267
268 /*******************************************************************
269  *         ParseEquate
270  *
271  * Parse an 'equate' definition.
272  */
273 static void ParseEquate( ORDDEF *odp )
274 {
275     char *endptr;
276     
277     char *token = GetToken(0);
278     int value = strtol(token, &endptr, 0);
279     if (endptr == NULL || *endptr != '\0')
280         fatal_error( "Expected number value, got '%s'\n", token );
281     if (SpecType == SPEC_WIN32)
282         fatal_error( "'equate' not supported for Win32\n" );
283     odp->u.abs.value = value;
284 }
285
286
287 /*******************************************************************
288  *         ParseStub
289  *
290  * Parse a 'stub' definition.
291  */
292 static void ParseStub( ORDDEF *odp )
293 {
294     odp->u.func.arg_types[0] = '\0';
295     odp->u.func.link_name[0] = '\0';
296 }
297
298
299 /*******************************************************************
300  *         ParseInterrupt
301  *
302  * Parse an 'interrupt' definition.
303  */
304 static void ParseInterrupt( ORDDEF *odp )
305 {
306     char *token;
307
308     if (SpecType == SPEC_WIN32)
309         fatal_error( "'interrupt' not supported for Win32\n" );
310
311     token = GetToken(0);
312     if (*token != '(') fatal_error( "Expected '(' got '%s'\n", token );
313
314     token = GetToken(0);
315     if (*token != ')') fatal_error( "Expected ')' got '%s'\n", token );
316
317     odp->u.func.arg_types[0] = '\0';
318     strcpy( odp->u.func.link_name, GetToken(0) );
319 }
320
321
322 /*******************************************************************
323  *         ParseExtern
324  *
325  * Parse an 'extern' definition.
326  */
327 static void ParseExtern( ORDDEF *odp )
328 {
329     if (SpecType == SPEC_WIN16) fatal_error( "'extern' not supported for Win16\n" );
330     strcpy( odp->u.ext.link_name, GetToken(0) );
331     /* 'extern' definitions are not available for implicit import */
332     odp->flags |= FLAG_NOIMPORT;
333 }
334
335
336 /*******************************************************************
337  *         ParseForward
338  *
339  * Parse a 'forward' definition.
340  */
341 static void ParseForward( ORDDEF *odp )
342 {
343     if (SpecType == SPEC_WIN16) fatal_error( "'forward' not supported for Win16\n" );
344     strcpy( odp->u.fwd.link_name, GetToken(0) );
345 }
346
347
348 /*******************************************************************
349  *         ParseFlags
350  *
351  * Parse the optional flags for an entry point
352  */
353 static char *ParseFlags( ORDDEF *odp )
354 {
355     unsigned int i;
356     char *token;
357
358     do
359     {
360         token = GetToken(0);
361         for (i = 0; FlagNames[i]; i++)
362             if (!strcmp( FlagNames[i], token )) break;
363         if (!FlagNames[i]) fatal_error( "Unknown flag '%s'\n", token );
364         odp->flags |= 1 << i;
365         token = GetToken(0);
366     } while (*token == '-');
367
368     return token;
369 }
370
371 /*******************************************************************
372  *         fix_export_name
373  *
374  * Fix an exported function name by removing a possible @xx suffix
375  */
376 static void fix_export_name( char *name )
377 {
378     char *p, *end = strrchr( name, '@' );
379     if (!end || !end[1] || end == name) return;
380     /* make sure all the rest is digits */
381     for (p = end + 1; *p; p++) if (!isdigit(*p)) return;
382     *end = 0;
383 }
384
385 /*******************************************************************
386  *         ParseOrdinal
387  *
388  * Parse an ordinal definition.
389  */
390 static void ParseOrdinal(int ordinal)
391 {
392     char *token;
393
394     ORDDEF *odp = &EntryPoints[nb_entry_points++];
395
396     token = GetToken(0);
397
398     for (odp->type = 0; odp->type < TYPE_NBTYPES; odp->type++)
399         if (TypeNames[odp->type] && !strcmp( token, TypeNames[odp->type] ))
400             break;
401
402     if (odp->type >= TYPE_NBTYPES)
403         fatal_error( "Expected type after ordinal, found '%s' instead\n", token );
404
405     token = GetToken(0);
406     if (*token == '-') token = ParseFlags( odp );
407
408     strcpy( odp->name, token );
409     fix_export_name( odp->name );
410     odp->lineno = current_line;
411     odp->ordinal = ordinal;
412
413     switch(odp->type)
414     {
415     case TYPE_VARIABLE:
416         ParseVariable( odp );
417         break;
418     case TYPE_REGISTER:
419     case TYPE_PASCAL_16:
420     case TYPE_PASCAL:
421     case TYPE_STDCALL:
422     case TYPE_VARARGS:
423     case TYPE_CDECL:
424         ParseExportFunction( odp );
425         break;
426     case TYPE_INTERRUPT:
427         ParseInterrupt( odp );
428         break;
429     case TYPE_ABS:
430         ParseEquate( odp );
431         break;
432     case TYPE_STUB:
433         ParseStub( odp );
434         break;
435     case TYPE_EXTERN:
436         ParseExtern( odp );
437         break;
438     case TYPE_FORWARD:
439         ParseForward( odp );
440         break;
441     default:
442         assert( 0 );
443     }
444
445 #ifndef __i386__
446     if (odp->flags & FLAG_I386)
447     {
448         /* ignore this entry point on non-Intel archs */
449         nb_entry_points--;
450         memset( odp, 0, sizeof(*odp) );
451         return;
452     }
453 #endif
454
455     if (ordinal != -1)
456     {
457         if (ordinal >= MAX_ORDINALS) fatal_error( "Ordinal number %d too large\n", ordinal );
458         if (ordinal > Limit) Limit = ordinal;
459         if (ordinal < Base) Base = ordinal;
460         odp->ordinal = ordinal;
461         Ordinals[ordinal] = odp;
462     }
463
464     if (!strcmp( odp->name, "@" ))
465     {
466         if (ordinal == -1)
467             fatal_error( "Nameless function needs an explicit ordinal number\n" );
468         if (SpecType != SPEC_WIN32)
469             fatal_error( "Nameless functions not supported for Win16\n" );
470         odp->name[0] = 0;
471     }
472     else Names[nb_names++] = odp;
473 }
474
475
476 static int name_compare( const void *name1, const void *name2 )
477 {
478     ORDDEF *odp1 = *(ORDDEF **)name1;
479     ORDDEF *odp2 = *(ORDDEF **)name2;
480     return strcmp( odp1->name, odp2->name );
481 }
482
483 /*******************************************************************
484  *         sort_names
485  *
486  * Sort the name array and catch duplicates.
487  */
488 static void sort_names(void)
489 {
490     int i;
491
492     if (!nb_names) return;
493
494     /* sort the list of names */
495     qsort( Names, nb_names, sizeof(Names[0]), name_compare );
496
497     /* check for duplicate names */
498     for (i = 0; i < nb_names - 1; i++)
499     {
500         if (!strcmp( Names[i]->name, Names[i+1]->name ))
501         {
502             current_line = max( Names[i]->lineno, Names[i+1]->lineno );
503             fatal_error( "'%s' redefined (previous definition at line %d)\n",
504                          Names[i]->name, min( Names[i]->lineno, Names[i+1]->lineno ) );
505         }
506     }
507 }
508
509
510 /*******************************************************************
511  *         ParseTopLevel
512  *
513  * Parse a spec file.
514  */
515 SPEC_TYPE ParseTopLevel( FILE *file )
516 {
517     char *token;
518
519     input_file = file;
520     current_line = 1;
521     while ((token = GetToken(1)) != NULL)
522     {
523         if (strcmp(token, "name") == 0)
524         {
525             strcpy(DLLName, GetToken(0));
526         }
527         else if (strcmp(token, "file") == 0)
528         {
529             strcpy(DLLFileName, GetToken(0));
530             strupper(DLLFileName);
531         }
532         else if (strcmp(token, "type") == 0)
533         {
534             token = GetToken(0);
535             if (!strcmp(token, "win16" )) SpecType = SPEC_WIN16;
536             else if (!strcmp(token, "win32" )) SpecType = SPEC_WIN32;
537             else fatal_error( "Type must be 'win16' or 'win32'\n" );
538         }
539         else if (strcmp(token, "mode") == 0)
540         {
541             token = GetToken(0);
542             if (!strcmp(token, "dll" )) SpecMode = SPEC_MODE_DLL;
543             else if (!strcmp(token, "guiexe" )) SpecMode = SPEC_MODE_GUIEXE;
544             else if (!strcmp(token, "cuiexe" )) SpecMode = SPEC_MODE_CUIEXE;
545             else if (!strcmp(token, "guiexe_no_main" )) SpecMode = SPEC_MODE_GUIEXE_NO_MAIN;
546             else if (!strcmp(token, "cuiexe_no_main" )) SpecMode = SPEC_MODE_CUIEXE_NO_MAIN;
547             else fatal_error( "Mode must be 'dll', 'guiexe', 'cuiexe', 'guiexe_no_main' or 'cuiexe_no_main'\n" );
548         }
549         else if (strcmp(token, "heap") == 0)
550         {
551             token = GetToken(0);
552             if (!IsNumberString(token)) fatal_error( "Expected number after heap\n" );
553             DLLHeapSize = atoi(token);
554         }
555         else if (strcmp(token, "init") == 0)
556         {
557             strcpy(DLLInitFunc, GetToken(0));
558             if (SpecType == SPEC_WIN16)
559                 fatal_error( "init cannot be used for Win16 spec files\n" );
560             if (!DLLInitFunc[0])
561                 fatal_error( "Expected function name after init\n" );
562             if (!strcmp(DLLInitFunc, "main"))
563                 fatal_error( "The init function cannot be named 'main'\n" );
564         }
565         else if (strcmp(token, "import") == 0)
566         {
567             if (SpecType != SPEC_WIN32)
568                 fatal_error( "Imports not supported for Win16\n" );
569             add_import_dll( GetToken(0) );
570         }
571         else if (strcmp(token, "rsrc") == 0)
572         {
573             if (SpecType != SPEC_WIN16) load_res32_file( GetToken(0) );
574             else load_res16_file( GetToken(0) );
575         }
576         else if (strcmp(token, "owner") == 0)
577         {
578             if (SpecType != SPEC_WIN16)
579                 fatal_error( "Owner only supported for Win16 spec files\n" );
580             strcpy( owner_name, GetToken(0) );
581         }
582         else if (strcmp(token, "debug_channels") == 0)
583         {
584             if (SpecType != SPEC_WIN32)
585                 fatal_error( "debug channels only supported for Win32 spec files\n" );
586             ParseDebug();
587         }
588         else if (strcmp(token, "@") == 0)
589         {
590             if (SpecType != SPEC_WIN32)
591                 fatal_error( "'@' ordinals not supported for Win16\n" );
592             ParseOrdinal( -1 );
593         }
594         else if (IsNumberString(token))
595         {
596             ParseOrdinal( atoi(token) );
597         }
598         else
599             fatal_error( "Expected name, id, length or ordinal\n" );
600     }
601
602     if (!DLLFileName[0])
603     {
604         if (SpecMode == SPEC_MODE_DLL)
605             sprintf( DLLFileName, "%s.dll", DLLName );
606         else
607             sprintf( DLLFileName, "%s.exe", DLLName );
608     }
609
610     if (SpecType == SPEC_INVALID) fatal_error( "Missing 'type' declaration\n" );
611     if (SpecType == SPEC_WIN16 && !owner_name[0])
612         fatal_error( "'owner' not specified for Win16 dll\n" );
613
614     current_line = 0;  /* no longer parsing the input file */
615     sort_names();
616     return SpecType;
617 }