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