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