winebuild: Make the cpu flag more generic to allow supporting a given entry point...
[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     "register",    /* FLAG_REGISTER */
71     "private",     /* FLAG_PRIVATE */
72     "ordinal",     /* FLAG_ORDINAL */
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 static inline int is_token_separator( char ch )
83 {
84     return strchr( separator_chars, ch ) != NULL;
85 }
86
87 static inline 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_CPU(CPU_x86); /* 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         if (!strncmp( token, "arch=", 5))
431         {
432             char *args = xstrdup( token + 5 );
433             char *cpu_name = strtok( args, "," );
434             while (cpu_name)
435             {
436                 enum target_cpu cpu = get_cpu_from_name( cpu_name );
437                 if (cpu == -1)
438                 {
439                     error( "Unknown architecture '%s'\n", cpu_name );
440                     return NULL;
441                 }
442                 odp->flags |= FLAG_CPU( cpu );
443                 cpu_name = strtok( NULL, "," );
444             }
445             free( args );
446         }
447         else if (!strcmp( token, "i386" ))  /* backwards compatibility */
448         {
449             odp->flags |= FLAG_CPU(CPU_x86);
450         }
451         else
452         {
453             for (i = 0; FlagNames[i]; i++)
454                 if (!strcmp( FlagNames[i], token )) break;
455             if (!FlagNames[i])
456             {
457                 error( "Unknown flag '%s'\n", token );
458                 return NULL;
459             }
460             odp->flags |= 1 << i;
461         }
462         token = GetToken(0);
463     } while (token && *token == '-');
464
465     return token;
466 }
467
468
469 /*******************************************************************
470  *         parse_spec_ordinal
471  *
472  * Parse an ordinal definition in a .spec file.
473  */
474 static int parse_spec_ordinal( int ordinal, DLLSPEC *spec )
475 {
476     const char *token;
477     size_t len;
478
479     ORDDEF *odp = add_entry_point( spec );
480     memset( odp, 0, sizeof(*odp) );
481
482     if (!(token = GetToken(0))) goto error;
483
484     for (odp->type = 0; odp->type < TYPE_NBTYPES; odp->type++)
485         if (TypeNames[odp->type] && !strcmp( token, TypeNames[odp->type] ))
486             break;
487
488     if (odp->type >= TYPE_NBTYPES)
489     {
490         error( "Expected type after ordinal, found '%s' instead\n", token );
491         goto error;
492     }
493
494     if (!(token = GetToken(0))) goto error;
495     if (*token == '-' && !(token = parse_spec_flags( odp ))) goto error;
496
497     odp->name = xstrdup( token );
498     odp->lineno = current_line;
499     odp->ordinal = ordinal;
500
501     len = strspn( odp->name, valid_ordname_chars );
502     if (len < strlen( odp->name ))
503     {
504         error( "Character '%c' is not allowed in exported name '%s'\n", odp->name[len], odp->name );
505         goto error;
506     }
507
508     switch(odp->type)
509     {
510     case TYPE_VARIABLE:
511         if (!parse_spec_variable( odp, spec )) goto error;
512         break;
513     case TYPE_PASCAL:
514     case TYPE_STDCALL:
515     case TYPE_VARARGS:
516     case TYPE_CDECL:
517         if (!parse_spec_export( odp, spec )) goto error;
518         break;
519     case TYPE_ABS:
520         if (!parse_spec_equate( odp, spec )) goto error;
521         break;
522     case TYPE_STUB:
523         if (!parse_spec_stub( odp, spec )) goto error;
524         break;
525     case TYPE_EXTERN:
526         if (!parse_spec_extern( odp, spec )) goto error;
527         break;
528     default:
529         assert( 0 );
530     }
531
532     if ((odp->flags & FLAG_CPU_MASK) && !(odp->flags & FLAG_CPU(target_cpu)))
533     {
534         /* ignore this entry point */
535         spec->nb_entry_points--;
536         return 1;
537     }
538
539     if (ordinal != -1)
540     {
541         if (!ordinal)
542         {
543             error( "Ordinal 0 is not valid\n" );
544             goto error;
545         }
546         if (ordinal >= MAX_ORDINALS)
547         {
548             error( "Ordinal number %d too large\n", ordinal );
549             goto error;
550         }
551         if (ordinal > spec->limit) spec->limit = ordinal;
552         if (ordinal < spec->base) spec->base = ordinal;
553         odp->ordinal = ordinal;
554     }
555
556     if (odp->type == TYPE_STDCALL && !(odp->flags & FLAG_PRIVATE))
557     {
558         if (!strcmp( odp->name, "DllRegisterServer" ) ||
559             !strcmp( odp->name, "DllUnregisterServer" ) ||
560             !strcmp( odp->name, "DllGetClassObject" ) ||
561             !strcmp( odp->name, "DllGetVersion" ) ||
562             !strcmp( odp->name, "DllInstall" ) ||
563             !strcmp( odp->name, "DllCanUnloadNow" ))
564         {
565             warning( "Function %s should be marked private\n", odp->name );
566             if (strcmp( odp->name, odp->link_name ))
567                 warning( "Function %s should not use a different internal name (%s)\n",
568                          odp->name, odp->link_name );
569         }
570     }
571
572     if (!strcmp( odp->name, "@" ) || odp->flags & (FLAG_NONAME | FLAG_ORDINAL))
573     {
574         if (ordinal == -1)
575         {
576             if (!strcmp( odp->name, "@" ))
577                 error( "Nameless function needs an explicit ordinal number\n" );
578             else
579                 error( "Function imported by ordinal needs an explicit ordinal number\n" );
580             goto error;
581         }
582         if (spec->type != SPEC_WIN32)
583         {
584             error( "Nameless functions not supported for Win16\n" );
585             goto error;
586         }
587         if (!strcmp( odp->name, "@" ))
588         {
589             free( odp->name );
590             odp->name = NULL;
591         }
592         else if (!(odp->flags & FLAG_ORDINAL))  /* -ordinal only affects the import library */
593         {
594             odp->export_name = odp->name;
595             odp->name = NULL;
596         }
597     }
598     return 1;
599
600 error:
601     spec->nb_entry_points--;
602     free( odp->name );
603     return 0;
604 }
605
606
607 static int name_compare( const void *ptr1, const void *ptr2 )
608 {
609     const ORDDEF *odp1 = *(const ORDDEF * const *)ptr1;
610     const ORDDEF *odp2 = *(const ORDDEF * const *)ptr2;
611     const char *name1 = odp1->name ? odp1->name : odp1->export_name;
612     const char *name2 = odp2->name ? odp2->name : odp2->export_name;
613     return strcmp( name1, name2 );
614 }
615
616 /*******************************************************************
617  *         assign_names
618  *
619  * Build the name array and catch duplicates.
620  */
621 static void assign_names( DLLSPEC *spec )
622 {
623     int i, j, nb_exp_names = 0;
624     ORDDEF **all_names;
625
626     spec->nb_names = 0;
627     for (i = 0; i < spec->nb_entry_points; i++)
628         if (spec->entry_points[i].name) spec->nb_names++;
629         else if (spec->entry_points[i].export_name) nb_exp_names++;
630
631     if (!spec->nb_names && !nb_exp_names) return;
632
633     /* check for duplicates */
634
635     all_names = xmalloc( (spec->nb_names + nb_exp_names) * sizeof(all_names[0]) );
636     for (i = j = 0; i < spec->nb_entry_points; i++)
637         if (spec->entry_points[i].name || spec->entry_points[i].export_name)
638             all_names[j++] = &spec->entry_points[i];
639
640     qsort( all_names, j, sizeof(all_names[0]), name_compare );
641
642     for (i = 0; i < j - 1; i++)
643     {
644         const char *name1 = all_names[i]->name ? all_names[i]->name : all_names[i]->export_name;
645         const char *name2 = all_names[i+1]->name ? all_names[i+1]->name : all_names[i+1]->export_name;
646         if (!strcmp( name1, name2 ))
647         {
648             current_line = max( all_names[i]->lineno, all_names[i+1]->lineno );
649             error( "'%s' redefined\n%s:%d: First defined here\n",
650                    name1, input_file_name,
651                    min( all_names[i]->lineno, all_names[i+1]->lineno ) );
652         }
653     }
654     free( all_names );
655
656     if (spec->nb_names)
657     {
658         spec->names = xmalloc( spec->nb_names * sizeof(spec->names[0]) );
659         for (i = j = 0; i < spec->nb_entry_points; i++)
660             if (spec->entry_points[i].name) spec->names[j++] = &spec->entry_points[i];
661
662         /* sort the list of names */
663         qsort( spec->names, spec->nb_names, sizeof(spec->names[0]), name_compare );
664     }
665 }
666
667 /*******************************************************************
668  *         assign_ordinals
669  *
670  * Build the ordinal array.
671  */
672 static void assign_ordinals( DLLSPEC *spec )
673 {
674     int i, count, ordinal;
675
676     /* start assigning from base, or from 1 if no ordinal defined yet */
677
678     spec->base = MAX_ORDINALS;
679     spec->limit = 0;
680     for (i = 0; i < spec->nb_entry_points; i++)
681     {
682         ordinal = spec->entry_points[i].ordinal;
683         if (ordinal == -1) continue;
684         if (ordinal > spec->limit) spec->limit = ordinal;
685         if (ordinal < spec->base) spec->base = ordinal;
686     }
687     if (spec->base == MAX_ORDINALS) spec->base = 1;
688     if (spec->limit < spec->base) spec->limit = spec->base;
689
690     count = max( spec->limit + 1, spec->base + spec->nb_entry_points );
691     spec->ordinals = xmalloc( count * sizeof(spec->ordinals[0]) );
692     memset( spec->ordinals, 0, count * sizeof(spec->ordinals[0]) );
693
694     /* fill in all explicitly specified ordinals */
695     for (i = 0; i < spec->nb_entry_points; i++)
696     {
697         ordinal = spec->entry_points[i].ordinal;
698         if (ordinal == -1) continue;
699         if (spec->ordinals[ordinal])
700         {
701             current_line = max( spec->entry_points[i].lineno, spec->ordinals[ordinal]->lineno );
702             error( "ordinal %d redefined\n%s:%d: First defined here\n",
703                    ordinal, input_file_name,
704                    min( spec->entry_points[i].lineno, spec->ordinals[ordinal]->lineno ) );
705         }
706         else spec->ordinals[ordinal] = &spec->entry_points[i];
707     }
708
709     /* now assign ordinals to the rest */
710     for (i = 0, ordinal = spec->base; i < spec->nb_entry_points; i++)
711     {
712         if (spec->entry_points[i].ordinal != -1) continue;
713         while (spec->ordinals[ordinal]) ordinal++;
714         if (ordinal >= MAX_ORDINALS)
715         {
716             current_line = spec->entry_points[i].lineno;
717             fatal_error( "Too many functions defined (max %d)\n", MAX_ORDINALS );
718         }
719         spec->entry_points[i].ordinal = ordinal;
720         spec->ordinals[ordinal] = &spec->entry_points[i];
721     }
722     if (ordinal > spec->limit) spec->limit = ordinal;
723 }
724
725
726 /*******************************************************************
727  *         parse_spec_file
728  *
729  * Parse a .spec file.
730  */
731 int parse_spec_file( FILE *file, DLLSPEC *spec )
732 {
733     const char *token;
734
735     input_file = file;
736     current_line = 0;
737
738     comment_chars = "#;";
739     separator_chars = "()-";
740
741     while (get_next_line())
742     {
743         if (!(token = GetToken(1))) continue;
744         if (strcmp(token, "@") == 0)
745         {
746             if (spec->type != SPEC_WIN32)
747             {
748                 error( "'@' ordinals not supported for Win16\n" );
749                 continue;
750             }
751             if (!parse_spec_ordinal( -1, spec )) continue;
752         }
753         else if (IsNumberString(token))
754         {
755             if (!parse_spec_ordinal( atoi(token), spec )) continue;
756         }
757         else
758         {
759             error( "Expected ordinal declaration, got '%s'\n", token );
760             continue;
761         }
762         if ((token = GetToken(1))) error( "Syntax error near '%s'\n", token );
763     }
764
765     current_line = 0;  /* no longer parsing the input file */
766     assign_names( spec );
767     assign_ordinals( spec );
768     return !nb_errors;
769 }
770
771
772 /*******************************************************************
773  *         parse_def_library
774  *
775  * Parse a LIBRARY declaration in a .def file.
776  */
777 static int parse_def_library( DLLSPEC *spec )
778 {
779     const char *token = GetToken(1);
780
781     if (!token) return 1;
782     if (strcmp( token, "BASE" ))
783     {
784         free( spec->file_name );
785         spec->file_name = xstrdup( token );
786         if (!(token = GetToken(1))) return 1;
787     }
788     if (strcmp( token, "BASE" ))
789     {
790         error( "Expected library name or BASE= declaration, got '%s'\n", token );
791         return 0;
792     }
793     if (!(token = GetToken(0))) return 0;
794     if (strcmp( token, "=" ))
795     {
796         error( "Expected '=' after BASE, got '%s'\n", token );
797         return 0;
798     }
799     if (!(token = GetToken(0))) return 0;
800     /* FIXME: do something with base address */
801
802     return 1;
803 }
804
805
806 /*******************************************************************
807  *         parse_def_stack_heap_size
808  *
809  * Parse a STACKSIZE or HEAPSIZE declaration in a .def file.
810  */
811 static int parse_def_stack_heap_size( int is_stack, DLLSPEC *spec )
812 {
813     const char *token = GetToken(0);
814     char *end;
815     unsigned long size;
816
817     if (!token) return 0;
818     size = strtoul( token, &end, 0 );
819     if (*end)
820     {
821         error( "Invalid number '%s'\n", token );
822         return 0;
823     }
824     if (is_stack) spec->stack_size = size / 1024;
825     else spec->heap_size = size / 1024;
826     if (!(token = GetToken(1))) return 1;
827     if (strcmp( token, "," ))
828     {
829         error( "Expected ',' after size, got '%s'\n", token );
830         return 0;
831     }
832     if (!(token = GetToken(0))) return 0;
833     /* FIXME: do something with reserve size */
834     return 1;
835 }
836
837
838 /*******************************************************************
839  *         parse_def_export
840  *
841  * Parse an export declaration in a .def file.
842  */
843 static int parse_def_export( char *name, DLLSPEC *spec )
844 {
845     int i, args;
846     const char *token = GetToken(1);
847
848     ORDDEF *odp = add_entry_point( spec );
849     memset( odp, 0, sizeof(*odp) );
850
851     odp->lineno = current_line;
852     odp->ordinal = -1;
853     odp->name = name;
854     args = remove_stdcall_decoration( odp->name );
855     if (args == -1) odp->type = TYPE_CDECL;
856     else
857     {
858         odp->type = TYPE_STDCALL;
859         args /= get_ptr_size();
860         if (args >= sizeof(odp->u.func.arg_types))
861         {
862             error( "Too many arguments in stdcall function '%s'\n", odp->name );
863             return 0;
864         }
865         for (i = 0; i < args; i++) odp->u.func.arg_types[i] = 'l';
866     }
867
868     /* check for optional internal name */
869
870     if (token && !strcmp( token, "=" ))
871     {
872         if (!(token = GetToken(0))) goto error;
873         odp->link_name = xstrdup( token );
874         remove_stdcall_decoration( odp->link_name );
875         token = GetToken(1);
876     }
877     else
878     {
879       odp->link_name = xstrdup( name );
880     }
881
882     /* check for optional ordinal */
883
884     if (token && token[0] == '@')
885     {
886         int ordinal;
887
888         if (!IsNumberString( token+1 ))
889         {
890             error( "Expected number after '@', got '%s'\n", token+1 );
891             goto error;
892         }
893         ordinal = atoi( token+1 );
894         if (!ordinal)
895         {
896             error( "Ordinal 0 is not valid\n" );
897             goto error;
898         }
899         if (ordinal >= MAX_ORDINALS)
900         {
901             error( "Ordinal number %d too large\n", ordinal );
902             goto error;
903         }
904         odp->ordinal = ordinal;
905         token = GetToken(1);
906     }
907
908     /* check for other optional keywords */
909
910     if (token && !strcmp( token, "NONAME" ))
911     {
912         if (odp->ordinal == -1)
913         {
914             error( "NONAME requires an ordinal\n" );
915             goto error;
916         }
917         odp->export_name = odp->name;
918         odp->name = NULL;
919         odp->flags |= FLAG_NONAME;
920         token = GetToken(1);
921     }
922     if (token && !strcmp( token, "PRIVATE" ))
923     {
924         odp->flags |= FLAG_PRIVATE;
925         token = GetToken(1);
926     }
927     if (token && !strcmp( token, "DATA" ))
928     {
929         odp->type = TYPE_EXTERN;
930         token = GetToken(1);
931     }
932     if (token)
933     {
934         error( "Garbage text '%s' found at end of export declaration\n", token );
935         goto error;
936     }
937     return 1;
938
939 error:
940     spec->nb_entry_points--;
941     free( odp->name );
942     return 0;
943 }
944
945
946 /*******************************************************************
947  *         parse_def_file
948  *
949  * Parse a .def file.
950  */
951 int parse_def_file( FILE *file, DLLSPEC *spec )
952 {
953     const char *token;
954     int in_exports = 0;
955
956     input_file = file;
957     current_line = 0;
958
959     comment_chars = ";";
960     separator_chars = ",=";
961
962     while (get_next_line())
963     {
964         if (!(token = GetToken(1))) continue;
965
966         if (!strcmp( token, "LIBRARY" ) || !strcmp( token, "NAME" ))
967         {
968             if (!parse_def_library( spec )) continue;
969             goto end_of_line;
970         }
971         else if (!strcmp( token, "STACKSIZE" ))
972         {
973             if (!parse_def_stack_heap_size( 1, spec )) continue;
974             goto end_of_line;
975         }
976         else if (!strcmp( token, "HEAPSIZE" ))
977         {
978             if (!parse_def_stack_heap_size( 0, spec )) continue;
979             goto end_of_line;
980         }
981         else if (!strcmp( token, "EXPORTS" ))
982         {
983             in_exports = 1;
984             if (!(token = GetToken(1))) continue;
985         }
986         else if (!strcmp( token, "IMPORTS" ))
987         {
988             in_exports = 0;
989             if (!(token = GetToken(1))) continue;
990         }
991         else if (!strcmp( token, "SECTIONS" ))
992         {
993             in_exports = 0;
994             if (!(token = GetToken(1))) continue;
995         }
996
997         if (!in_exports) continue;  /* ignore this line */
998         if (!parse_def_export( xstrdup(token), spec )) continue;
999
1000     end_of_line:
1001         if ((token = GetToken(1))) error( "Syntax error near '%s'\n", token );
1002     }
1003
1004     current_line = 0;  /* no longer parsing the input file */
1005     assign_names( spec );
1006     assign_ordinals( spec );
1007     return !nb_errors;
1008 }