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