comctl32: Fix an Nx1 in ImageList_DrawIndirect().
[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 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 (odp->type == TYPE_STDCALL && !(odp->flags & FLAG_PRIVATE))
522     {
523         if (!strcmp( odp->name, "DllRegisterServer" ) ||
524             !strcmp( odp->name, "DllUnregisterServer" ) ||
525             !strcmp( odp->name, "DllGetClassObject" ) ||
526             !strcmp( odp->name, "DllGetVersion" ) ||
527             !strcmp( odp->name, "DllInstall" ) ||
528             !strcmp( odp->name, "DllCanUnloadNow" ))
529         {
530             warning( "Function %s should be marked private\n", odp->name );
531             if (strcmp( odp->name, odp->link_name ))
532                 warning( "Function %s should not use a different internal name (%s)\n",
533                          odp->name, odp->link_name );
534         }
535     }
536
537     if (!strcmp( odp->name, "@" ) || odp->flags & FLAG_NONAME)
538     {
539         if (ordinal == -1)
540         {
541             error( "Nameless function needs an explicit ordinal number\n" );
542             goto error;
543         }
544         if (spec->type != SPEC_WIN32)
545         {
546             error( "Nameless functions not supported for Win16\n" );
547             goto error;
548         }
549         if (!strcmp( odp->name, "@" )) free( odp->name );
550         else odp->export_name = odp->name;
551         odp->name = NULL;
552     }
553     return 1;
554
555 error:
556     spec->nb_entry_points--;
557     free( odp->name );
558     return 0;
559 }
560
561
562 static int name_compare( const void *ptr1, const void *ptr2 )
563 {
564     const ORDDEF *odp1 = *(const ORDDEF * const *)ptr1;
565     const ORDDEF *odp2 = *(const ORDDEF * const *)ptr2;
566     const char *name1 = odp1->name ? odp1->name : odp1->export_name;
567     const char *name2 = odp2->name ? odp2->name : odp2->export_name;
568     return strcmp( name1, name2 );
569 }
570
571 /*******************************************************************
572  *         assign_names
573  *
574  * Build the name array and catch duplicates.
575  */
576 static void assign_names( DLLSPEC *spec )
577 {
578     int i, j, nb_exp_names = 0;
579     ORDDEF **all_names;
580
581     spec->nb_names = 0;
582     for (i = 0; i < spec->nb_entry_points; i++)
583         if (spec->entry_points[i].name) spec->nb_names++;
584         else if (spec->entry_points[i].export_name) nb_exp_names++;
585
586     if (!spec->nb_names && !nb_exp_names) return;
587
588     /* check for duplicates */
589
590     all_names = xmalloc( (spec->nb_names + nb_exp_names) * sizeof(all_names[0]) );
591     for (i = j = 0; i < spec->nb_entry_points; i++)
592         if (spec->entry_points[i].name || spec->entry_points[i].export_name)
593             all_names[j++] = &spec->entry_points[i];
594
595     qsort( all_names, j, sizeof(all_names[0]), name_compare );
596
597     for (i = 0; i < j - 1; i++)
598     {
599         const char *name1 = all_names[i]->name ? all_names[i]->name : all_names[i]->export_name;
600         const char *name2 = all_names[i+1]->name ? all_names[i+1]->name : all_names[i+1]->export_name;
601         if (!strcmp( name1, name2 ))
602         {
603             current_line = max( all_names[i]->lineno, all_names[i+1]->lineno );
604             error( "'%s' redefined\n%s:%d: First defined here\n",
605                    name1, input_file_name,
606                    min( all_names[i]->lineno, all_names[i+1]->lineno ) );
607         }
608     }
609     free( all_names );
610
611     if (spec->nb_names)
612     {
613         spec->names = xmalloc( spec->nb_names * sizeof(spec->names[0]) );
614         for (i = j = 0; i < spec->nb_entry_points; i++)
615             if (spec->entry_points[i].name) spec->names[j++] = &spec->entry_points[i];
616
617         /* sort the list of names */
618         qsort( spec->names, spec->nb_names, sizeof(spec->names[0]), name_compare );
619     }
620 }
621
622 /*******************************************************************
623  *         assign_ordinals
624  *
625  * Build the ordinal array.
626  */
627 static void assign_ordinals( DLLSPEC *spec )
628 {
629     int i, count, ordinal;
630
631     /* start assigning from base, or from 1 if no ordinal defined yet */
632
633     spec->base = MAX_ORDINALS;
634     spec->limit = 0;
635     for (i = 0; i < spec->nb_entry_points; i++)
636     {
637         ordinal = spec->entry_points[i].ordinal;
638         if (ordinal == -1) continue;
639         if (ordinal > spec->limit) spec->limit = ordinal;
640         if (ordinal < spec->base) spec->base = ordinal;
641     }
642     if (spec->base == MAX_ORDINALS) spec->base = 1;
643     if (spec->limit < spec->base) spec->limit = spec->base;
644
645     count = max( spec->limit + 1, spec->base + spec->nb_entry_points );
646     spec->ordinals = xmalloc( count * sizeof(spec->ordinals[0]) );
647     memset( spec->ordinals, 0, count * sizeof(spec->ordinals[0]) );
648
649     /* fill in all explicitly specified ordinals */
650     for (i = 0; i < spec->nb_entry_points; i++)
651     {
652         ordinal = spec->entry_points[i].ordinal;
653         if (ordinal == -1) continue;
654         if (spec->ordinals[ordinal])
655         {
656             current_line = max( spec->entry_points[i].lineno, spec->ordinals[ordinal]->lineno );
657             error( "ordinal %d redefined\n%s:%d: First defined here\n",
658                    ordinal, input_file_name,
659                    min( spec->entry_points[i].lineno, spec->ordinals[ordinal]->lineno ) );
660         }
661         else spec->ordinals[ordinal] = &spec->entry_points[i];
662     }
663
664     /* now assign ordinals to the rest */
665     for (i = 0, ordinal = spec->base; i < spec->nb_entry_points; i++)
666     {
667         if (spec->entry_points[i].ordinal != -1) continue;
668         while (spec->ordinals[ordinal]) ordinal++;
669         if (ordinal >= MAX_ORDINALS)
670         {
671             current_line = spec->entry_points[i].lineno;
672             fatal_error( "Too many functions defined (max %d)\n", MAX_ORDINALS );
673         }
674         spec->entry_points[i].ordinal = ordinal;
675         spec->ordinals[ordinal] = &spec->entry_points[i];
676     }
677     if (ordinal > spec->limit) spec->limit = ordinal;
678 }
679
680
681 /*******************************************************************
682  *         parse_spec_file
683  *
684  * Parse a .spec file.
685  */
686 int parse_spec_file( FILE *file, DLLSPEC *spec )
687 {
688     const char *token;
689
690     input_file = file;
691     current_line = 0;
692
693     comment_chars = "#;";
694     separator_chars = "()-";
695
696     while (get_next_line())
697     {
698         if (!(token = GetToken(1))) continue;
699         if (strcmp(token, "@") == 0)
700         {
701             if (spec->type != SPEC_WIN32)
702             {
703                 error( "'@' ordinals not supported for Win16\n" );
704                 continue;
705             }
706             if (!parse_spec_ordinal( -1, spec )) continue;
707         }
708         else if (IsNumberString(token))
709         {
710             if (!parse_spec_ordinal( atoi(token), spec )) continue;
711         }
712         else
713         {
714             error( "Expected ordinal declaration, got '%s'\n", token );
715             continue;
716         }
717         if ((token = GetToken(1))) error( "Syntax error near '%s'\n", token );
718     }
719
720     current_line = 0;  /* no longer parsing the input file */
721     assign_names( spec );
722     assign_ordinals( spec );
723     return !nb_errors;
724 }
725
726
727 /*******************************************************************
728  *         parse_def_library
729  *
730  * Parse a LIBRARY declaration in a .def file.
731  */
732 static int parse_def_library( DLLSPEC *spec )
733 {
734     const char *token = GetToken(1);
735
736     if (!token) return 1;
737     if (strcmp( token, "BASE" ))
738     {
739         free( spec->file_name );
740         spec->file_name = xstrdup( token );
741         if (!(token = GetToken(1))) return 1;
742     }
743     if (strcmp( token, "BASE" ))
744     {
745         error( "Expected library name or BASE= declaration, got '%s'\n", token );
746         return 0;
747     }
748     if (!(token = GetToken(0))) return 0;
749     if (strcmp( token, "=" ))
750     {
751         error( "Expected '=' after BASE, got '%s'\n", token );
752         return 0;
753     }
754     if (!(token = GetToken(0))) return 0;
755     /* FIXME: do something with base address */
756
757     return 1;
758 }
759
760
761 /*******************************************************************
762  *         parse_def_stack_heap_size
763  *
764  * Parse a STACKSIZE or HEAPSIZE declaration in a .def file.
765  */
766 static int parse_def_stack_heap_size( int is_stack, DLLSPEC *spec )
767 {
768     const char *token = GetToken(0);
769     char *end;
770     unsigned long size;
771
772     if (!token) return 0;
773     size = strtoul( token, &end, 0 );
774     if (*end)
775     {
776         error( "Invalid number '%s'\n", token );
777         return 0;
778     }
779     if (is_stack) spec->stack_size = size / 1024;
780     else spec->heap_size = size / 1024;
781     if (!(token = GetToken(1))) return 1;
782     if (strcmp( token, "," ))
783     {
784         error( "Expected ',' after size, got '%s'\n", token );
785         return 0;
786     }
787     if (!(token = GetToken(0))) return 0;
788     /* FIXME: do something with reserve size */
789     return 1;
790 }
791
792
793 /*******************************************************************
794  *         parse_def_export
795  *
796  * Parse an export declaration in a .def file.
797  */
798 static int parse_def_export( char *name, DLLSPEC *spec )
799 {
800     int i, args;
801     const char *token = GetToken(1);
802
803     ORDDEF *odp = add_entry_point( spec );
804     memset( odp, 0, sizeof(*odp) );
805
806     odp->lineno = current_line;
807     odp->ordinal = -1;
808     odp->name = name;
809     args = remove_stdcall_decoration( odp->name );
810     if (args == -1) odp->type = TYPE_CDECL;
811     else
812     {
813         odp->type = TYPE_STDCALL;
814         args /= get_ptr_size();
815         if (args >= sizeof(odp->u.func.arg_types))
816         {
817             error( "Too many arguments in stdcall function '%s'\n", odp->name );
818             return 0;
819         }
820         for (i = 0; i < args; i++) odp->u.func.arg_types[i] = 'l';
821     }
822
823     /* check for optional internal name */
824
825     if (token && !strcmp( token, "=" ))
826     {
827         if (!(token = GetToken(0))) goto error;
828         odp->link_name = xstrdup( token );
829         remove_stdcall_decoration( odp->link_name );
830         token = GetToken(1);
831     }
832     else
833     {
834       odp->link_name = xstrdup( name );
835     }
836
837     /* check for optional ordinal */
838
839     if (token && token[0] == '@')
840     {
841         int ordinal;
842
843         if (!IsNumberString( token+1 ))
844         {
845             error( "Expected number after '@', got '%s'\n", token+1 );
846             goto error;
847         }
848         ordinal = atoi( token+1 );
849         if (!ordinal)
850         {
851             error( "Ordinal 0 is not valid\n" );
852             goto error;
853         }
854         if (ordinal >= MAX_ORDINALS)
855         {
856             error( "Ordinal number %d too large\n", ordinal );
857             goto error;
858         }
859         odp->ordinal = ordinal;
860         token = GetToken(1);
861     }
862
863     /* check for other optional keywords */
864
865     if (token && !strcmp( token, "NONAME" ))
866     {
867         if (odp->ordinal == -1)
868         {
869             error( "NONAME requires an ordinal\n" );
870             goto error;
871         }
872         odp->export_name = odp->name;
873         odp->name = NULL;
874         odp->flags |= FLAG_NONAME;
875         token = GetToken(1);
876     }
877     if (token && !strcmp( token, "PRIVATE" ))
878     {
879         odp->flags |= FLAG_PRIVATE;
880         token = GetToken(1);
881     }
882     if (token && !strcmp( token, "DATA" ))
883     {
884         odp->type = TYPE_EXTERN;
885         token = GetToken(1);
886     }
887     if (token)
888     {
889         error( "Garbage text '%s' found at end of export declaration\n", token );
890         goto error;
891     }
892     return 1;
893
894 error:
895     spec->nb_entry_points--;
896     free( odp->name );
897     return 0;
898 }
899
900
901 /*******************************************************************
902  *         parse_def_file
903  *
904  * Parse a .def file.
905  */
906 int parse_def_file( FILE *file, DLLSPEC *spec )
907 {
908     const char *token;
909     int in_exports = 0;
910
911     input_file = file;
912     current_line = 0;
913
914     comment_chars = ";";
915     separator_chars = ",=";
916
917     while (get_next_line())
918     {
919         if (!(token = GetToken(1))) continue;
920
921         if (!strcmp( token, "LIBRARY" ) || !strcmp( token, "NAME" ))
922         {
923             if (!parse_def_library( spec )) continue;
924             goto end_of_line;
925         }
926         else if (!strcmp( token, "STACKSIZE" ))
927         {
928             if (!parse_def_stack_heap_size( 1, spec )) continue;
929             goto end_of_line;
930         }
931         else if (!strcmp( token, "HEAPSIZE" ))
932         {
933             if (!parse_def_stack_heap_size( 0, spec )) continue;
934             goto end_of_line;
935         }
936         else if (!strcmp( token, "EXPORTS" ))
937         {
938             in_exports = 1;
939             if (!(token = GetToken(1))) continue;
940         }
941         else if (!strcmp( token, "IMPORTS" ))
942         {
943             in_exports = 0;
944             if (!(token = GetToken(1))) continue;
945         }
946         else if (!strcmp( token, "SECTIONS" ))
947         {
948             in_exports = 0;
949             if (!(token = GetToken(1))) continue;
950         }
951
952         if (!in_exports) continue;  /* ignore this line */
953         if (!parse_def_export( xstrdup(token), spec )) continue;
954
955     end_of_line:
956         if ((token = GetToken(1))) error( "Syntax error near '%s'\n", token );
957     }
958
959     current_line = 0;  /* no longer parsing the input file */
960     assign_names( spec );
961     assign_ordinals( spec );
962     return !nb_errors;
963 }