Added a few more Unicode digits from Unicode version 4.1.
[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, 2004 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 <stdarg.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 #include "windef.h"
36 #include "winbase.h"
37 #include "build.h"
38
39 int current_line = 0;
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 *separator_chars;
47 static const char *comment_chars;
48
49 static const char * const TypeNames[TYPE_NBTYPES] =
50 {
51     "variable",     /* TYPE_VARIABLE */
52     "pascal",       /* TYPE_PASCAL */
53     "equate",       /* TYPE_ABS */
54     "stub",         /* TYPE_STUB */
55     "stdcall",      /* TYPE_STDCALL */
56     "cdecl",        /* TYPE_CDECL */
57     "varargs",      /* TYPE_VARARGS */
58     "extern"        /* TYPE_EXTERN */
59 };
60
61 static const char * const FlagNames[] =
62 {
63     "norelay",     /* FLAG_NORELAY */
64     "noname",      /* FLAG_NONAME */
65     "ret16",       /* FLAG_RET16 */
66     "ret64",       /* FLAG_RET64 */
67     "i386",        /* FLAG_I386 */
68     "register",    /* FLAG_REGISTER */
69     "private",     /* FLAG_PRIVATE */
70     NULL
71 };
72
73 static int IsNumberString(const char *s)
74 {
75     while (*s) if (!isdigit(*s++)) return 0;
76     return 1;
77 }
78
79 inline static int is_token_separator( char ch )
80 {
81     return strchr( separator_chars, ch ) != NULL;
82 }
83
84 inline static int is_token_comment( char ch )
85 {
86     return strchr( comment_chars, ch ) != NULL;
87 }
88
89 /* get the next line from the input file, or return 0 if at eof */
90 static int get_next_line(void)
91 {
92     ParseNext = ParseBuffer;
93     current_line++;
94     return (fgets(ParseBuffer, sizeof(ParseBuffer), input_file) != NULL);
95 }
96
97 static const char * GetToken( int allow_eol )
98 {
99     char *p = ParseNext;
100     char *token = TokenBuffer;
101
102     for (;;)
103     {
104         /* remove initial white space */
105         p = ParseNext;
106         while (isspace(*p)) p++;
107
108         if (*p == '\\' && p[1] == '\n')  /* line continuation */
109         {
110             if (!get_next_line())
111             {
112                 if (!allow_eol) error( "Unexpected end of file\n" );
113                 return NULL;
114             }
115         }
116         else break;
117     }
118
119     if ((*p == '\0') || is_token_comment(*p))
120     {
121         if (!allow_eol) error( "Declaration not terminated properly\n" );
122         return NULL;
123     }
124
125     /*
126      * Find end of token.
127      */
128     if (is_token_separator(*p))
129     {
130         /* a separator is always a complete token */
131         *token++ = *p++;
132     }
133     else while (*p != '\0' && !is_token_separator(*p) && !isspace(*p))
134     {
135         if (*p == '\\') p++;
136         if (*p) *token++ = *p++;
137     }
138     *token = '\0';
139     ParseNext = p;
140     return TokenBuffer;
141 }
142
143
144 static ORDDEF *add_entry_point( DLLSPEC *spec )
145 {
146     if (spec->nb_entry_points == spec->alloc_entry_points)
147     {
148         spec->alloc_entry_points += 128;
149         spec->entry_points = xrealloc( spec->entry_points,
150                                        spec->alloc_entry_points * sizeof(*spec->entry_points) );
151     }
152     return &spec->entry_points[spec->nb_entry_points++];
153 }
154
155 /*******************************************************************
156  *         parse_spec_variable
157  *
158  * Parse a variable definition in a .spec file.
159  */
160 static int parse_spec_variable( ORDDEF *odp, DLLSPEC *spec )
161 {
162     char *endptr;
163     int *value_array;
164     int n_values;
165     int value_array_size;
166     const char *token;
167
168     if (spec->type == SPEC_WIN32)
169     {
170         error( "'variable' not supported in Win32, use 'extern' instead\n" );
171         return 0;
172     }
173
174     if (!(token = GetToken(0))) return 0;
175     if (*token != '(')
176     {
177         error( "Expected '(' got '%s'\n", token );
178         return 0;
179     }
180
181     n_values = 0;
182     value_array_size = 25;
183     value_array = xmalloc(sizeof(*value_array) * value_array_size);
184
185     for (;;)
186     {
187         if (!(token = GetToken(0)))
188         {
189             free( value_array );
190             return 0;
191         }
192         if (*token == ')')
193             break;
194
195         value_array[n_values++] = strtol(token, &endptr, 0);
196         if (n_values == value_array_size)
197         {
198             value_array_size += 25;
199             value_array = xrealloc(value_array,
200                                    sizeof(*value_array) * value_array_size);
201         }
202
203         if (endptr == NULL || *endptr != '\0')
204         {
205             error( "Expected number value, got '%s'\n", token );
206             free( value_array );
207             return 0;
208         }
209     }
210
211     odp->u.var.n_values = n_values;
212     odp->u.var.values = xrealloc(value_array, sizeof(*value_array) * n_values);
213     return 1;
214 }
215
216
217 /*******************************************************************
218  *         parse_spec_export
219  *
220  * Parse an exported function definition in a .spec file.
221  */
222 static int parse_spec_export( ORDDEF *odp, DLLSPEC *spec )
223 {
224     const char *token;
225     unsigned int i;
226
227     switch(spec->type)
228     {
229     case SPEC_WIN16:
230         if (odp->type == TYPE_STDCALL)
231         {
232             error( "'stdcall' not supported for Win16\n" );
233             return 0;
234         }
235         break;
236     case SPEC_WIN32:
237         if (odp->type == TYPE_PASCAL)
238         {
239             error( "'pascal' not supported for Win32\n" );
240             return 0;
241         }
242         break;
243     default:
244         break;
245     }
246
247     if (!(token = GetToken(0))) return 0;
248     if (*token != '(')
249     {
250         error( "Expected '(' got '%s'\n", token );
251         return 0;
252     }
253
254     for (i = 0; i < sizeof(odp->u.func.arg_types); i++)
255     {
256         if (!(token = GetToken(0))) return 0;
257         if (*token == ')')
258             break;
259
260         if (!strcmp(token, "word"))
261             odp->u.func.arg_types[i] = 'w';
262         else if (!strcmp(token, "s_word"))
263             odp->u.func.arg_types[i] = 's';
264         else if (!strcmp(token, "long") || !strcmp(token, "segptr"))
265             odp->u.func.arg_types[i] = 'l';
266         else if (!strcmp(token, "ptr"))
267             odp->u.func.arg_types[i] = 'p';
268         else if (!strcmp(token, "str"))
269             odp->u.func.arg_types[i] = 't';
270         else if (!strcmp(token, "wstr"))
271             odp->u.func.arg_types[i] = 'W';
272         else if (!strcmp(token, "segstr"))
273             odp->u.func.arg_types[i] = 'T';
274         else if (!strcmp(token, "double"))
275         {
276             odp->u.func.arg_types[i++] = 'l';
277             if (get_ptr_size() == 4 && i < sizeof(odp->u.func.arg_types))
278                 odp->u.func.arg_types[i] = 'l';
279         }
280         else
281         {
282             error( "Unknown argument type '%s'\n", token );
283             return 0;
284         }
285
286         if (spec->type == SPEC_WIN32)
287         {
288             if (strcmp(token, "long") &&
289                 strcmp(token, "ptr") &&
290                 strcmp(token, "str") &&
291                 strcmp(token, "wstr") &&
292                 strcmp(token, "double"))
293             {
294                 error( "Type '%s' not supported for Win32\n", token );
295                 return 0;
296             }
297         }
298     }
299     if ((*token != ')') || (i >= sizeof(odp->u.func.arg_types)))
300     {
301         error( "Too many arguments\n" );
302         return 0;
303     }
304
305     odp->u.func.arg_types[i] = '\0';
306     if (odp->type == TYPE_VARARGS)
307         odp->flags |= FLAG_NORELAY;  /* no relay debug possible for varags entry point */
308
309     if (!(token = GetToken(1)))
310     {
311         if (!strcmp( odp->name, "@" ))
312         {
313             error( "Missing handler name for anonymous function\n" );
314             return 0;
315         }
316         odp->link_name = xstrdup( odp->name );
317     }
318     else
319     {
320         odp->link_name = xstrdup( token );
321         if (strchr( odp->link_name, '.' ))
322         {
323             if (spec->type == SPEC_WIN16)
324             {
325                 error( "Forwarded functions not supported for Win16\n" );
326                 return 0;
327             }
328             odp->flags |= FLAG_FORWARD;
329         }
330     }
331     return 1;
332 }
333
334
335 /*******************************************************************
336  *         parse_spec_equate
337  *
338  * Parse an 'equate' definition in a .spec file.
339  */
340 static int parse_spec_equate( ORDDEF *odp, DLLSPEC *spec )
341 {
342     char *endptr;
343     int value;
344     const char *token;
345
346     if (spec->type == SPEC_WIN32)
347     {
348         error( "'equate' not supported for Win32\n" );
349         return 0;
350     }
351     if (!(token = GetToken(0))) return 0;
352     value = strtol(token, &endptr, 0);
353     if (endptr == NULL || *endptr != '\0')
354     {
355         error( "Expected number value, got '%s'\n", token );
356         return 0;
357     }
358     if (value < -0x8000 || value > 0xffff)
359     {
360         error( "Value %d for absolute symbol doesn't fit in 16 bits\n", value );
361         value = 0;
362     }
363     odp->u.abs.value = value;
364     return 1;
365 }
366
367
368 /*******************************************************************
369  *         parse_spec_stub
370  *
371  * Parse a 'stub' definition in a .spec file
372  */
373 static int parse_spec_stub( ORDDEF *odp, DLLSPEC *spec )
374 {
375     odp->u.func.arg_types[0] = '\0';
376     odp->link_name = xstrdup("");
377     odp->flags |= FLAG_I386;  /* don't bother generating stubs for Winelib */
378     return 1;
379 }
380
381
382 /*******************************************************************
383  *         parse_spec_extern
384  *
385  * Parse an 'extern' definition in a .spec file.
386  */
387 static int parse_spec_extern( ORDDEF *odp, DLLSPEC *spec )
388 {
389     const char *token;
390
391     if (spec->type == SPEC_WIN16)
392     {
393         error( "'extern' not supported for Win16, use 'variable' instead\n" );
394         return 0;
395     }
396     if (!(token = GetToken(1)))
397     {
398         if (!strcmp( odp->name, "@" ))
399         {
400             error( "Missing handler name for anonymous extern\n" );
401             return 0;
402         }
403         odp->link_name = xstrdup( odp->name );
404     }
405     else
406     {
407         odp->link_name = xstrdup( token );
408         if (strchr( odp->link_name, '.' )) odp->flags |= FLAG_FORWARD;
409     }
410     return 1;
411 }
412
413
414 /*******************************************************************
415  *         parse_spec_flags
416  *
417  * Parse the optional flags for an entry point in a .spec file.
418  */
419 static const char *parse_spec_flags( ORDDEF *odp )
420 {
421     unsigned int i;
422     const char *token;
423
424     do
425     {
426         if (!(token = GetToken(0))) break;
427         for (i = 0; FlagNames[i]; i++)
428             if (!strcmp( FlagNames[i], token )) break;
429         if (!FlagNames[i])
430         {
431             error( "Unknown flag '%s'\n", token );
432             return NULL;
433         }
434         odp->flags |= 1 << i;
435         token = GetToken(0);
436     } while (token && *token == '-');
437
438     return token;
439 }
440
441
442 /*******************************************************************
443  *         parse_spec_ordinal
444  *
445  * Parse an ordinal definition in a .spec file.
446  */
447 static int parse_spec_ordinal( int ordinal, DLLSPEC *spec )
448 {
449     const char *token;
450
451     ORDDEF *odp = add_entry_point( spec );
452     memset( odp, 0, sizeof(*odp) );
453
454     if (!(token = GetToken(0))) goto error;
455
456     for (odp->type = 0; odp->type < TYPE_NBTYPES; odp->type++)
457         if (TypeNames[odp->type] && !strcmp( token, TypeNames[odp->type] ))
458             break;
459
460     if (odp->type >= TYPE_NBTYPES)
461     {
462         error( "Expected type after ordinal, found '%s' instead\n", token );
463         goto error;
464     }
465
466     if (!(token = GetToken(0))) goto error;
467     if (*token == '-' && !(token = parse_spec_flags( odp ))) goto error;
468
469     odp->name = xstrdup( token );
470     odp->lineno = current_line;
471     odp->ordinal = ordinal;
472
473     switch(odp->type)
474     {
475     case TYPE_VARIABLE:
476         if (!parse_spec_variable( odp, spec )) goto error;
477         break;
478     case TYPE_PASCAL:
479     case TYPE_STDCALL:
480     case TYPE_VARARGS:
481     case TYPE_CDECL:
482         if (!parse_spec_export( odp, spec )) goto error;
483         break;
484     case TYPE_ABS:
485         if (!parse_spec_equate( odp, spec )) goto error;
486         break;
487     case TYPE_STUB:
488         if (!parse_spec_stub( odp, spec )) goto error;
489         break;
490     case TYPE_EXTERN:
491         if (!parse_spec_extern( odp, spec )) goto error;
492         break;
493     default:
494         assert( 0 );
495     }
496
497     if ((target_cpu != CPU_x86) && (odp->flags & FLAG_I386))
498     {
499         /* ignore this entry point on non-Intel archs */
500         spec->nb_entry_points--;
501         return 1;
502     }
503
504     if (ordinal != -1)
505     {
506         if (!ordinal)
507         {
508             error( "Ordinal 0 is not valid\n" );
509             goto error;
510         }
511         if (ordinal >= MAX_ORDINALS)
512         {
513             error( "Ordinal number %d too large\n", ordinal );
514             goto error;
515         }
516         if (ordinal > spec->limit) spec->limit = ordinal;
517         if (ordinal < spec->base) spec->base = ordinal;
518         odp->ordinal = ordinal;
519     }
520
521     if (spec->type == SPEC_WIN32 && odp->flags & FLAG_REGISTER)
522     {
523         error( "-register flag not supported for Win32 entry points\n" );
524         goto error;
525     }
526
527     if (odp->type == TYPE_STDCALL && !(odp->flags & FLAG_PRIVATE))
528     {
529         if (!strcmp( odp->name, "DllRegisterServer" ) ||
530             !strcmp( odp->name, "DllUnregisterServer" ) ||
531             !strcmp( odp->name, "DllGetClassObject" ) ||
532             !strcmp( odp->name, "DllGetVersion" ) ||
533             !strcmp( odp->name, "DllInstall" ) ||
534             !strcmp( odp->name, "DllCanUnloadNow" ))
535         {
536             warning( "Function %s should be marked private\n", odp->name );
537             if (strcmp( odp->name, odp->link_name ))
538                 warning( "Function %s should not use a different internal name (%s)\n",
539                          odp->name, odp->link_name );
540         }
541     }
542
543     if (!strcmp( odp->name, "@" ) || odp->flags & FLAG_NONAME)
544     {
545         if (ordinal == -1)
546         {
547             error( "Nameless function needs an explicit ordinal number\n" );
548             goto error;
549         }
550         if (spec->type != SPEC_WIN32)
551         {
552             error( "Nameless functions not supported for Win16\n" );
553             goto error;
554         }
555         if (!strcmp( odp->name, "@" )) free( odp->name );
556         else odp->export_name = odp->name;
557         odp->name = NULL;
558     }
559     return 1;
560
561 error:
562     spec->nb_entry_points--;
563     free( odp->name );
564     return 0;
565 }
566
567
568 static int name_compare( const void *ptr1, const void *ptr2 )
569 {
570     const ORDDEF *odp1 = *(const ORDDEF * const *)ptr1;
571     const ORDDEF *odp2 = *(const ORDDEF * const *)ptr2;
572     const char *name1 = odp1->name ? odp1->name : odp1->export_name;
573     const char *name2 = odp2->name ? odp2->name : odp2->export_name;
574     return strcmp( name1, name2 );
575 }
576
577 /*******************************************************************
578  *         assign_names
579  *
580  * Build the name array and catch duplicates.
581  */
582 static void assign_names( DLLSPEC *spec )
583 {
584     int i, j, nb_exp_names = 0;
585     ORDDEF **all_names;
586
587     spec->nb_names = 0;
588     for (i = 0; i < spec->nb_entry_points; i++)
589         if (spec->entry_points[i].name) spec->nb_names++;
590         else if (spec->entry_points[i].export_name) nb_exp_names++;
591
592     if (!spec->nb_names && !nb_exp_names) return;
593
594     /* check for duplicates */
595
596     all_names = xmalloc( (spec->nb_names + nb_exp_names) * sizeof(all_names[0]) );
597     for (i = j = 0; i < spec->nb_entry_points; i++)
598         if (spec->entry_points[i].name || spec->entry_points[i].export_name)
599             all_names[j++] = &spec->entry_points[i];
600
601     qsort( all_names, j, sizeof(all_names[0]), name_compare );
602
603     for (i = 0; i < j - 1; i++)
604     {
605         const char *name1 = all_names[i]->name ? all_names[i]->name : all_names[i]->export_name;
606         const char *name2 = all_names[i+1]->name ? all_names[i+1]->name : all_names[i+1]->export_name;
607         if (!strcmp( name1, name2 ))
608         {
609             current_line = max( all_names[i]->lineno, all_names[i+1]->lineno );
610             error( "'%s' redefined\n%s:%d: First defined here\n",
611                    name1, input_file_name,
612                    min( all_names[i]->lineno, all_names[i+1]->lineno ) );
613         }
614     }
615     free( all_names );
616
617     if (spec->nb_names)
618     {
619         spec->names = xmalloc( spec->nb_names * sizeof(spec->names[0]) );
620         for (i = j = 0; i < spec->nb_entry_points; i++)
621             if (spec->entry_points[i].name) spec->names[j++] = &spec->entry_points[i];
622
623         /* sort the list of names */
624         qsort( spec->names, spec->nb_names, sizeof(spec->names[0]), name_compare );
625     }
626 }
627
628 /*******************************************************************
629  *         assign_ordinals
630  *
631  * Build the ordinal array.
632  */
633 static void assign_ordinals( DLLSPEC *spec )
634 {
635     int i, count, ordinal;
636
637     /* start assigning from base, or from 1 if no ordinal defined yet */
638
639     spec->base = MAX_ORDINALS;
640     spec->limit = 0;
641     for (i = 0; i < spec->nb_entry_points; i++)
642     {
643         ordinal = spec->entry_points[i].ordinal;
644         if (ordinal == -1) continue;
645         if (ordinal > spec->limit) spec->limit = ordinal;
646         if (ordinal < spec->base) spec->base = ordinal;
647     }
648     if (spec->base == MAX_ORDINALS) spec->base = 1;
649     if (spec->limit < spec->base) spec->limit = spec->base;
650
651     count = max( spec->limit + 1, spec->base + spec->nb_entry_points );
652     spec->ordinals = xmalloc( count * sizeof(spec->ordinals[0]) );
653     memset( spec->ordinals, 0, count * sizeof(spec->ordinals[0]) );
654
655     /* fill in all explicitly specified ordinals */
656     for (i = 0; i < spec->nb_entry_points; i++)
657     {
658         ordinal = spec->entry_points[i].ordinal;
659         if (ordinal == -1) continue;
660         if (spec->ordinals[ordinal])
661         {
662             current_line = max( spec->entry_points[i].lineno, spec->ordinals[ordinal]->lineno );
663             error( "ordinal %d redefined\n%s:%d: First defined here\n",
664                    ordinal, input_file_name,
665                    min( spec->entry_points[i].lineno, spec->ordinals[ordinal]->lineno ) );
666         }
667         else spec->ordinals[ordinal] = &spec->entry_points[i];
668     }
669
670     /* now assign ordinals to the rest */
671     for (i = 0, ordinal = spec->base; i < spec->nb_entry_points; i++)
672     {
673         if (spec->entry_points[i].ordinal != -1) continue;
674         while (spec->ordinals[ordinal]) ordinal++;
675         if (ordinal >= MAX_ORDINALS)
676         {
677             current_line = spec->entry_points[i].lineno;
678             fatal_error( "Too many functions defined (max %d)\n", MAX_ORDINALS );
679         }
680         spec->entry_points[i].ordinal = ordinal;
681         spec->ordinals[ordinal] = &spec->entry_points[i];
682     }
683     if (ordinal > spec->limit) spec->limit = ordinal;
684 }
685
686
687 /*******************************************************************
688  *         parse_spec_file
689  *
690  * Parse a .spec file.
691  */
692 int parse_spec_file( FILE *file, DLLSPEC *spec )
693 {
694     const char *token;
695
696     input_file = file;
697     current_line = 0;
698
699     comment_chars = "#;";
700     separator_chars = "()-";
701
702     while (get_next_line())
703     {
704         if (!(token = GetToken(1))) continue;
705         if (strcmp(token, "@") == 0)
706         {
707             if (spec->type != SPEC_WIN32)
708             {
709                 error( "'@' ordinals not supported for Win16\n" );
710                 continue;
711             }
712             if (!parse_spec_ordinal( -1, spec )) continue;
713         }
714         else if (IsNumberString(token))
715         {
716             if (!parse_spec_ordinal( atoi(token), spec )) continue;
717         }
718         else
719         {
720             error( "Expected ordinal declaration, got '%s'\n", token );
721             continue;
722         }
723         if ((token = GetToken(1))) error( "Syntax error near '%s'\n", token );
724     }
725
726     current_line = 0;  /* no longer parsing the input file */
727     assign_names( spec );
728     assign_ordinals( spec );
729     return !nb_errors;
730 }
731
732
733 /*******************************************************************
734  *         parse_def_library
735  *
736  * Parse a LIBRARY declaration in a .def file.
737  */
738 static int parse_def_library( DLLSPEC *spec )
739 {
740     const char *token = GetToken(1);
741
742     if (!token) return 1;
743     if (strcmp( token, "BASE" ))
744     {
745         free( spec->file_name );
746         spec->file_name = xstrdup( token );
747         if (!(token = GetToken(1))) return 1;
748     }
749     if (strcmp( token, "BASE" ))
750     {
751         error( "Expected library name or BASE= declaration, got '%s'\n", token );
752         return 0;
753     }
754     if (!(token = GetToken(0))) return 0;
755     if (strcmp( token, "=" ))
756     {
757         error( "Expected '=' after BASE, got '%s'\n", token );
758         return 0;
759     }
760     if (!(token = GetToken(0))) return 0;
761     /* FIXME: do something with base address */
762
763     return 1;
764 }
765
766
767 /*******************************************************************
768  *         parse_def_stack_heap_size
769  *
770  * Parse a STACKSIZE or HEAPSIZE declaration in a .def file.
771  */
772 static int parse_def_stack_heap_size( int is_stack, DLLSPEC *spec )
773 {
774     const char *token = GetToken(0);
775     char *end;
776     unsigned long size;
777
778     if (!token) return 0;
779     size = strtoul( token, &end, 0 );
780     if (*end)
781     {
782         error( "Invalid number '%s'\n", token );
783         return 0;
784     }
785     if (is_stack) spec->stack_size = size / 1024;
786     else spec->heap_size = size / 1024;
787     if (!(token = GetToken(1))) return 1;
788     if (strcmp( token, "," ))
789     {
790         error( "Expected ',' after size, got '%s'\n", token );
791         return 0;
792     }
793     if (!(token = GetToken(0))) return 0;
794     /* FIXME: do something with reserve size */
795     return 1;
796 }
797
798
799 /*******************************************************************
800  *         parse_def_export
801  *
802  * Parse an export declaration in a .def file.
803  */
804 static int parse_def_export( char *name, DLLSPEC *spec )
805 {
806     int i, args;
807     const char *token = GetToken(1);
808
809     ORDDEF *odp = add_entry_point( spec );
810     memset( odp, 0, sizeof(*odp) );
811
812     odp->lineno = current_line;
813     odp->ordinal = -1;
814     odp->name = name;
815     args = remove_stdcall_decoration( odp->name );
816     if (args == -1) odp->type = TYPE_CDECL;
817     else
818     {
819         odp->type = TYPE_STDCALL;
820         args /= get_ptr_size();
821         if (args >= sizeof(odp->u.func.arg_types))
822         {
823             error( "Too many arguments in stdcall function '%s'\n", odp->name );
824             return 0;
825         }
826         for (i = 0; i < args; i++) odp->u.func.arg_types[i] = 'l';
827     }
828
829     /* check for optional internal name */
830
831     if (token && !strcmp( token, "=" ))
832     {
833         if (!(token = GetToken(0))) goto error;
834         odp->link_name = xstrdup( token );
835         remove_stdcall_decoration( odp->link_name );
836         token = GetToken(1);
837     }
838     else
839     {
840       odp->link_name = xstrdup( name );
841     }
842
843     /* check for optional ordinal */
844
845     if (token && token[0] == '@')
846     {
847         int ordinal;
848
849         if (!IsNumberString( token+1 ))
850         {
851             error( "Expected number after '@', got '%s'\n", token+1 );
852             goto error;
853         }
854         ordinal = atoi( token+1 );
855         if (!ordinal)
856         {
857             error( "Ordinal 0 is not valid\n" );
858             goto error;
859         }
860         if (ordinal >= MAX_ORDINALS)
861         {
862             error( "Ordinal number %d too large\n", ordinal );
863             goto error;
864         }
865         odp->ordinal = ordinal;
866         token = GetToken(1);
867     }
868
869     /* check for other optional keywords */
870
871     if (token && !strcmp( token, "NONAME" ))
872     {
873         if (odp->ordinal == -1)
874         {
875             error( "NONAME requires an ordinal\n" );
876             goto error;
877         }
878         odp->export_name = odp->name;
879         odp->name = NULL;
880         odp->flags |= FLAG_NONAME;
881         token = GetToken(1);
882     }
883     if (token && !strcmp( token, "PRIVATE" ))
884     {
885         odp->flags |= FLAG_PRIVATE;
886         token = GetToken(1);
887     }
888     if (token && !strcmp( token, "DATA" ))
889     {
890         odp->type = TYPE_EXTERN;
891         token = GetToken(1);
892     }
893     if (token)
894     {
895         error( "Garbage text '%s' found at end of export declaration\n", token );
896         goto error;
897     }
898     return 1;
899
900 error:
901     spec->nb_entry_points--;
902     free( odp->name );
903     return 0;
904 }
905
906
907 /*******************************************************************
908  *         parse_def_file
909  *
910  * Parse a .def file.
911  */
912 int parse_def_file( FILE *file, DLLSPEC *spec )
913 {
914     const char *token;
915     int in_exports = 0;
916
917     input_file = file;
918     current_line = 0;
919
920     comment_chars = ";";
921     separator_chars = ",=";
922
923     while (get_next_line())
924     {
925         if (!(token = GetToken(1))) continue;
926
927         if (!strcmp( token, "LIBRARY" ) || !strcmp( token, "NAME" ))
928         {
929             if (!parse_def_library( spec )) continue;
930             goto end_of_line;
931         }
932         else if (!strcmp( token, "STACKSIZE" ))
933         {
934             if (!parse_def_stack_heap_size( 1, spec )) continue;
935             goto end_of_line;
936         }
937         else if (!strcmp( token, "HEAPSIZE" ))
938         {
939             if (!parse_def_stack_heap_size( 0, spec )) continue;
940             goto end_of_line;
941         }
942         else if (!strcmp( token, "EXPORTS" ))
943         {
944             in_exports = 1;
945             if (!(token = GetToken(1))) continue;
946         }
947         else if (!strcmp( token, "IMPORTS" ))
948         {
949             in_exports = 0;
950             if (!(token = GetToken(1))) continue;
951         }
952         else if (!strcmp( token, "SECTIONS" ))
953         {
954             in_exports = 0;
955             if (!(token = GetToken(1))) continue;
956         }
957
958         if (!in_exports) continue;  /* ignore this line */
959         if (!parse_def_export( xstrdup(token), spec )) continue;
960
961     end_of_line:
962         if ((token = GetToken(1))) error( "Syntax error near '%s'\n", token );
963     }
964
965     current_line = 0;  /* no longer parsing the input file */
966     assign_names( spec );
967     assign_ordinals( spec );
968     return !nb_errors;
969 }