| /* |
| * Spec file parser |
| * |
| * Copyright 1993 Robert J. Amstadt |
| * Copyright 1995 Martin von Loewis |
| * Copyright 1995, 1996, 1997, 2004 Alexandre Julliard |
| * Copyright 1997 Eric Youngdale |
| * Copyright 1999 Ulrich Weigand |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| #include "config.h" |
| #include "wine/port.h" |
| |
| #include <assert.h> |
| #include <ctype.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "build.h" |
| |
| int current_line = 0; |
| |
| static char ParseBuffer[512]; |
| static char TokenBuffer[512]; |
| static char *ParseNext = ParseBuffer; |
| static FILE *input_file; |
| |
| static const char *separator_chars; |
| static const char *comment_chars; |
| |
| static const char * const TypeNames[TYPE_NBTYPES] = |
| { |
| "variable", /* TYPE_VARIABLE */ |
| "pascal", /* TYPE_PASCAL */ |
| "equate", /* TYPE_ABS */ |
| "stub", /* TYPE_STUB */ |
| "stdcall", /* TYPE_STDCALL */ |
| "cdecl", /* TYPE_CDECL */ |
| "varargs", /* TYPE_VARARGS */ |
| "extern" /* TYPE_EXTERN */ |
| }; |
| |
| static const char * const FlagNames[] = |
| { |
| "norelay", /* FLAG_NORELAY */ |
| "noname", /* FLAG_NONAME */ |
| "ret16", /* FLAG_RET16 */ |
| "ret64", /* FLAG_RET64 */ |
| "i386", /* FLAG_I386 */ |
| "register", /* FLAG_REGISTER */ |
| "private", /* FLAG_PRIVATE */ |
| NULL |
| }; |
| |
| static int IsNumberString(const char *s) |
| { |
| while (*s) if (!isdigit(*s++)) return 0; |
| return 1; |
| } |
| |
| inline static int is_token_separator( char ch ) |
| { |
| return strchr( separator_chars, ch ) != NULL; |
| } |
| |
| inline static int is_token_comment( char ch ) |
| { |
| return strchr( comment_chars, ch ) != NULL; |
| } |
| |
| /* get the next line from the input file, or return 0 if at eof */ |
| static int get_next_line(void) |
| { |
| ParseNext = ParseBuffer; |
| current_line++; |
| return (fgets(ParseBuffer, sizeof(ParseBuffer), input_file) != NULL); |
| } |
| |
| static const char * GetToken( int allow_eol ) |
| { |
| char *p = ParseNext; |
| char *token = TokenBuffer; |
| |
| for (;;) |
| { |
| /* remove initial white space */ |
| p = ParseNext; |
| while (isspace(*p)) p++; |
| |
| if (*p == '\\' && p[1] == '\n') /* line continuation */ |
| { |
| if (!get_next_line()) |
| { |
| if (!allow_eol) error( "Unexpected end of file\n" ); |
| return NULL; |
| } |
| } |
| else break; |
| } |
| |
| if ((*p == '\0') || is_token_comment(*p)) |
| { |
| if (!allow_eol) error( "Declaration not terminated properly\n" ); |
| return NULL; |
| } |
| |
| /* |
| * Find end of token. |
| */ |
| if (is_token_separator(*p)) |
| { |
| /* a separator is always a complete token */ |
| *token++ = *p++; |
| } |
| else while (*p != '\0' && !is_token_separator(*p) && !isspace(*p)) |
| { |
| if (*p == '\\') p++; |
| if (*p) *token++ = *p++; |
| } |
| *token = '\0'; |
| ParseNext = p; |
| return TokenBuffer; |
| } |
| |
| |
| static ORDDEF *add_entry_point( DLLSPEC *spec ) |
| { |
| if (spec->nb_entry_points == spec->alloc_entry_points) |
| { |
| spec->alloc_entry_points += 128; |
| spec->entry_points = xrealloc( spec->entry_points, |
| spec->alloc_entry_points * sizeof(*spec->entry_points) ); |
| } |
| return &spec->entry_points[spec->nb_entry_points++]; |
| } |
| |
| /******************************************************************* |
| * parse_spec_variable |
| * |
| * Parse a variable definition in a .spec file. |
| */ |
| static int parse_spec_variable( ORDDEF *odp, DLLSPEC *spec ) |
| { |
| char *endptr; |
| int *value_array; |
| int n_values; |
| int value_array_size; |
| const char *token; |
| |
| if (spec->type == SPEC_WIN32) |
| { |
| error( "'variable' not supported in Win32, use 'extern' instead\n" ); |
| return 0; |
| } |
| |
| if (!(token = GetToken(0))) return 0; |
| if (*token != '(') |
| { |
| error( "Expected '(' got '%s'\n", token ); |
| return 0; |
| } |
| |
| n_values = 0; |
| value_array_size = 25; |
| value_array = xmalloc(sizeof(*value_array) * value_array_size); |
| |
| for (;;) |
| { |
| if (!(token = GetToken(0))) |
| { |
| free( value_array ); |
| return 0; |
| } |
| if (*token == ')') |
| break; |
| |
| value_array[n_values++] = strtol(token, &endptr, 0); |
| if (n_values == value_array_size) |
| { |
| value_array_size += 25; |
| value_array = xrealloc(value_array, |
| sizeof(*value_array) * value_array_size); |
| } |
| |
| if (endptr == NULL || *endptr != '\0') |
| { |
| error( "Expected number value, got '%s'\n", token ); |
| free( value_array ); |
| return 0; |
| } |
| } |
| |
| odp->u.var.n_values = n_values; |
| odp->u.var.values = xrealloc(value_array, sizeof(*value_array) * n_values); |
| return 1; |
| } |
| |
| |
| /******************************************************************* |
| * parse_spec_export |
| * |
| * Parse an exported function definition in a .spec file. |
| */ |
| static int parse_spec_export( ORDDEF *odp, DLLSPEC *spec ) |
| { |
| const char *token; |
| unsigned int i; |
| |
| switch(spec->type) |
| { |
| case SPEC_WIN16: |
| if (odp->type == TYPE_STDCALL) |
| { |
| error( "'stdcall' not supported for Win16\n" ); |
| return 0; |
| } |
| break; |
| case SPEC_WIN32: |
| if (odp->type == TYPE_PASCAL) |
| { |
| error( "'pascal' not supported for Win32\n" ); |
| return 0; |
| } |
| break; |
| default: |
| break; |
| } |
| |
| if (!(token = GetToken(0))) return 0; |
| if (*token != '(') |
| { |
| error( "Expected '(' got '%s'\n", token ); |
| return 0; |
| } |
| |
| for (i = 0; i < sizeof(odp->u.func.arg_types); i++) |
| { |
| if (!(token = GetToken(0))) return 0; |
| if (*token == ')') |
| break; |
| |
| if (!strcmp(token, "word")) |
| odp->u.func.arg_types[i] = 'w'; |
| else if (!strcmp(token, "s_word")) |
| odp->u.func.arg_types[i] = 's'; |
| else if (!strcmp(token, "long") || !strcmp(token, "segptr")) |
| odp->u.func.arg_types[i] = 'l'; |
| else if (!strcmp(token, "ptr")) |
| odp->u.func.arg_types[i] = 'p'; |
| else if (!strcmp(token, "str")) |
| odp->u.func.arg_types[i] = 't'; |
| else if (!strcmp(token, "wstr")) |
| odp->u.func.arg_types[i] = 'W'; |
| else if (!strcmp(token, "segstr")) |
| odp->u.func.arg_types[i] = 'T'; |
| else if (!strcmp(token, "double")) |
| { |
| odp->u.func.arg_types[i++] = 'l'; |
| if (get_ptr_size() == 4 && i < sizeof(odp->u.func.arg_types)) |
| odp->u.func.arg_types[i] = 'l'; |
| } |
| else |
| { |
| error( "Unknown argument type '%s'\n", token ); |
| return 0; |
| } |
| |
| if (spec->type == SPEC_WIN32) |
| { |
| if (strcmp(token, "long") && |
| strcmp(token, "ptr") && |
| strcmp(token, "str") && |
| strcmp(token, "wstr") && |
| strcmp(token, "double")) |
| { |
| error( "Type '%s' not supported for Win32\n", token ); |
| return 0; |
| } |
| } |
| } |
| if ((*token != ')') || (i >= sizeof(odp->u.func.arg_types))) |
| { |
| error( "Too many arguments\n" ); |
| return 0; |
| } |
| |
| odp->u.func.arg_types[i] = '\0'; |
| if (odp->type == TYPE_VARARGS) |
| odp->flags |= FLAG_NORELAY; /* no relay debug possible for varags entry point */ |
| |
| if (!(token = GetToken(1))) |
| { |
| if (!strcmp( odp->name, "@" )) |
| { |
| error( "Missing handler name for anonymous function\n" ); |
| return 0; |
| } |
| odp->link_name = xstrdup( odp->name ); |
| } |
| else |
| { |
| odp->link_name = xstrdup( token ); |
| if (strchr( odp->link_name, '.' )) |
| { |
| if (spec->type == SPEC_WIN16) |
| { |
| error( "Forwarded functions not supported for Win16\n" ); |
| return 0; |
| } |
| odp->flags |= FLAG_FORWARD; |
| } |
| } |
| return 1; |
| } |
| |
| |
| /******************************************************************* |
| * parse_spec_equate |
| * |
| * Parse an 'equate' definition in a .spec file. |
| */ |
| static int parse_spec_equate( ORDDEF *odp, DLLSPEC *spec ) |
| { |
| char *endptr; |
| int value; |
| const char *token; |
| |
| if (spec->type == SPEC_WIN32) |
| { |
| error( "'equate' not supported for Win32\n" ); |
| return 0; |
| } |
| if (!(token = GetToken(0))) return 0; |
| value = strtol(token, &endptr, 0); |
| if (endptr == NULL || *endptr != '\0') |
| { |
| error( "Expected number value, got '%s'\n", token ); |
| return 0; |
| } |
| if (value < -0x8000 || value > 0xffff) |
| { |
| error( "Value %d for absolute symbol doesn't fit in 16 bits\n", value ); |
| value = 0; |
| } |
| odp->u.abs.value = value; |
| return 1; |
| } |
| |
| |
| /******************************************************************* |
| * parse_spec_stub |
| * |
| * Parse a 'stub' definition in a .spec file |
| */ |
| static int parse_spec_stub( ORDDEF *odp, DLLSPEC *spec ) |
| { |
| odp->u.func.arg_types[0] = '\0'; |
| odp->link_name = xstrdup(""); |
| odp->flags |= FLAG_I386; /* don't bother generating stubs for Winelib */ |
| return 1; |
| } |
| |
| |
| /******************************************************************* |
| * parse_spec_extern |
| * |
| * Parse an 'extern' definition in a .spec file. |
| */ |
| static int parse_spec_extern( ORDDEF *odp, DLLSPEC *spec ) |
| { |
| const char *token; |
| |
| if (spec->type == SPEC_WIN16) |
| { |
| error( "'extern' not supported for Win16, use 'variable' instead\n" ); |
| return 0; |
| } |
| if (!(token = GetToken(1))) |
| { |
| if (!strcmp( odp->name, "@" )) |
| { |
| error( "Missing handler name for anonymous extern\n" ); |
| return 0; |
| } |
| odp->link_name = xstrdup( odp->name ); |
| } |
| else |
| { |
| odp->link_name = xstrdup( token ); |
| if (strchr( odp->link_name, '.' )) odp->flags |= FLAG_FORWARD; |
| } |
| return 1; |
| } |
| |
| |
| /******************************************************************* |
| * parse_spec_flags |
| * |
| * Parse the optional flags for an entry point in a .spec file. |
| */ |
| static const char *parse_spec_flags( ORDDEF *odp ) |
| { |
| unsigned int i; |
| const char *token; |
| |
| do |
| { |
| if (!(token = GetToken(0))) break; |
| for (i = 0; FlagNames[i]; i++) |
| if (!strcmp( FlagNames[i], token )) break; |
| if (!FlagNames[i]) |
| { |
| error( "Unknown flag '%s'\n", token ); |
| return NULL; |
| } |
| odp->flags |= 1 << i; |
| token = GetToken(0); |
| } while (token && *token == '-'); |
| |
| return token; |
| } |
| |
| |
| /******************************************************************* |
| * parse_spec_ordinal |
| * |
| * Parse an ordinal definition in a .spec file. |
| */ |
| static int parse_spec_ordinal( int ordinal, DLLSPEC *spec ) |
| { |
| const char *token; |
| |
| ORDDEF *odp = add_entry_point( spec ); |
| memset( odp, 0, sizeof(*odp) ); |
| |
| if (!(token = GetToken(0))) goto error; |
| |
| for (odp->type = 0; odp->type < TYPE_NBTYPES; odp->type++) |
| if (TypeNames[odp->type] && !strcmp( token, TypeNames[odp->type] )) |
| break; |
| |
| if (odp->type >= TYPE_NBTYPES) |
| { |
| error( "Expected type after ordinal, found '%s' instead\n", token ); |
| goto error; |
| } |
| |
| if (!(token = GetToken(0))) goto error; |
| if (*token == '-' && !(token = parse_spec_flags( odp ))) goto error; |
| |
| odp->name = xstrdup( token ); |
| odp->lineno = current_line; |
| odp->ordinal = ordinal; |
| |
| switch(odp->type) |
| { |
| case TYPE_VARIABLE: |
| if (!parse_spec_variable( odp, spec )) goto error; |
| break; |
| case TYPE_PASCAL: |
| case TYPE_STDCALL: |
| case TYPE_VARARGS: |
| case TYPE_CDECL: |
| if (!parse_spec_export( odp, spec )) goto error; |
| break; |
| case TYPE_ABS: |
| if (!parse_spec_equate( odp, spec )) goto error; |
| break; |
| case TYPE_STUB: |
| if (!parse_spec_stub( odp, spec )) goto error; |
| break; |
| case TYPE_EXTERN: |
| if (!parse_spec_extern( odp, spec )) goto error; |
| break; |
| default: |
| assert( 0 ); |
| } |
| |
| if ((target_cpu != CPU_x86) && (odp->flags & FLAG_I386)) |
| { |
| /* ignore this entry point on non-Intel archs */ |
| spec->nb_entry_points--; |
| return 1; |
| } |
| |
| if (ordinal != -1) |
| { |
| if (!ordinal) |
| { |
| error( "Ordinal 0 is not valid\n" ); |
| goto error; |
| } |
| if (ordinal >= MAX_ORDINALS) |
| { |
| error( "Ordinal number %d too large\n", ordinal ); |
| goto error; |
| } |
| if (ordinal > spec->limit) spec->limit = ordinal; |
| if (ordinal < spec->base) spec->base = ordinal; |
| odp->ordinal = ordinal; |
| } |
| |
| if (spec->type == SPEC_WIN32 && odp->flags & FLAG_REGISTER) |
| { |
| error( "-register flag not supported for Win32 entry points\n" ); |
| goto error; |
| } |
| |
| if (odp->type == TYPE_STDCALL && !(odp->flags & FLAG_PRIVATE)) |
| { |
| if (!strcmp( odp->name, "DllRegisterServer" ) || |
| !strcmp( odp->name, "DllUnregisterServer" ) || |
| !strcmp( odp->name, "DllGetClassObject" ) || |
| !strcmp( odp->name, "DllGetVersion" ) || |
| !strcmp( odp->name, "DllInstall" ) || |
| !strcmp( odp->name, "DllCanUnloadNow" )) |
| { |
| warning( "Function %s should be marked private\n", odp->name ); |
| if (strcmp( odp->name, odp->link_name )) |
| warning( "Function %s should not use a different internal name (%s)\n", |
| odp->name, odp->link_name ); |
| } |
| } |
| |
| if (!strcmp( odp->name, "@" ) || odp->flags & FLAG_NONAME) |
| { |
| if (ordinal == -1) |
| { |
| error( "Nameless function needs an explicit ordinal number\n" ); |
| goto error; |
| } |
| if (spec->type != SPEC_WIN32) |
| { |
| error( "Nameless functions not supported for Win16\n" ); |
| goto error; |
| } |
| if (!strcmp( odp->name, "@" )) free( odp->name ); |
| else odp->export_name = odp->name; |
| odp->name = NULL; |
| } |
| return 1; |
| |
| error: |
| spec->nb_entry_points--; |
| free( odp->name ); |
| return 0; |
| } |
| |
| |
| static int name_compare( const void *ptr1, const void *ptr2 ) |
| { |
| const ORDDEF *odp1 = *(const ORDDEF * const *)ptr1; |
| const ORDDEF *odp2 = *(const ORDDEF * const *)ptr2; |
| const char *name1 = odp1->name ? odp1->name : odp1->export_name; |
| const char *name2 = odp2->name ? odp2->name : odp2->export_name; |
| return strcmp( name1, name2 ); |
| } |
| |
| /******************************************************************* |
| * assign_names |
| * |
| * Build the name array and catch duplicates. |
| */ |
| static void assign_names( DLLSPEC *spec ) |
| { |
| int i, j, nb_exp_names = 0; |
| ORDDEF **all_names; |
| |
| spec->nb_names = 0; |
| for (i = 0; i < spec->nb_entry_points; i++) |
| if (spec->entry_points[i].name) spec->nb_names++; |
| else if (spec->entry_points[i].export_name) nb_exp_names++; |
| |
| if (!spec->nb_names && !nb_exp_names) return; |
| |
| /* check for duplicates */ |
| |
| all_names = xmalloc( (spec->nb_names + nb_exp_names) * sizeof(all_names[0]) ); |
| for (i = j = 0; i < spec->nb_entry_points; i++) |
| if (spec->entry_points[i].name || spec->entry_points[i].export_name) |
| all_names[j++] = &spec->entry_points[i]; |
| |
| qsort( all_names, j, sizeof(all_names[0]), name_compare ); |
| |
| for (i = 0; i < j - 1; i++) |
| { |
| const char *name1 = all_names[i]->name ? all_names[i]->name : all_names[i]->export_name; |
| const char *name2 = all_names[i+1]->name ? all_names[i+1]->name : all_names[i+1]->export_name; |
| if (!strcmp( name1, name2 )) |
| { |
| current_line = max( all_names[i]->lineno, all_names[i+1]->lineno ); |
| error( "'%s' redefined\n%s:%d: First defined here\n", |
| name1, input_file_name, |
| min( all_names[i]->lineno, all_names[i+1]->lineno ) ); |
| } |
| } |
| free( all_names ); |
| |
| if (spec->nb_names) |
| { |
| spec->names = xmalloc( spec->nb_names * sizeof(spec->names[0]) ); |
| for (i = j = 0; i < spec->nb_entry_points; i++) |
| if (spec->entry_points[i].name) spec->names[j++] = &spec->entry_points[i]; |
| |
| /* sort the list of names */ |
| qsort( spec->names, spec->nb_names, sizeof(spec->names[0]), name_compare ); |
| } |
| } |
| |
| /******************************************************************* |
| * assign_ordinals |
| * |
| * Build the ordinal array. |
| */ |
| static void assign_ordinals( DLLSPEC *spec ) |
| { |
| int i, count, ordinal; |
| |
| /* start assigning from base, or from 1 if no ordinal defined yet */ |
| |
| spec->base = MAX_ORDINALS; |
| spec->limit = 0; |
| for (i = 0; i < spec->nb_entry_points; i++) |
| { |
| ordinal = spec->entry_points[i].ordinal; |
| if (ordinal == -1) continue; |
| if (ordinal > spec->limit) spec->limit = ordinal; |
| if (ordinal < spec->base) spec->base = ordinal; |
| } |
| if (spec->base == MAX_ORDINALS) spec->base = 1; |
| if (spec->limit < spec->base) spec->limit = spec->base; |
| |
| count = max( spec->limit + 1, spec->base + spec->nb_entry_points ); |
| spec->ordinals = xmalloc( count * sizeof(spec->ordinals[0]) ); |
| memset( spec->ordinals, 0, count * sizeof(spec->ordinals[0]) ); |
| |
| /* fill in all explicitly specified ordinals */ |
| for (i = 0; i < spec->nb_entry_points; i++) |
| { |
| ordinal = spec->entry_points[i].ordinal; |
| if (ordinal == -1) continue; |
| if (spec->ordinals[ordinal]) |
| { |
| current_line = max( spec->entry_points[i].lineno, spec->ordinals[ordinal]->lineno ); |
| error( "ordinal %d redefined\n%s:%d: First defined here\n", |
| ordinal, input_file_name, |
| min( spec->entry_points[i].lineno, spec->ordinals[ordinal]->lineno ) ); |
| } |
| else spec->ordinals[ordinal] = &spec->entry_points[i]; |
| } |
| |
| /* now assign ordinals to the rest */ |
| for (i = 0, ordinal = spec->base; i < spec->nb_entry_points; i++) |
| { |
| if (spec->entry_points[i].ordinal != -1) continue; |
| while (spec->ordinals[ordinal]) ordinal++; |
| if (ordinal >= MAX_ORDINALS) |
| { |
| current_line = spec->entry_points[i].lineno; |
| fatal_error( "Too many functions defined (max %d)\n", MAX_ORDINALS ); |
| } |
| spec->entry_points[i].ordinal = ordinal; |
| spec->ordinals[ordinal] = &spec->entry_points[i]; |
| } |
| if (ordinal > spec->limit) spec->limit = ordinal; |
| } |
| |
| |
| /******************************************************************* |
| * parse_spec_file |
| * |
| * Parse a .spec file. |
| */ |
| int parse_spec_file( FILE *file, DLLSPEC *spec ) |
| { |
| const char *token; |
| |
| input_file = file; |
| current_line = 0; |
| |
| comment_chars = "#;"; |
| separator_chars = "()-"; |
| |
| while (get_next_line()) |
| { |
| if (!(token = GetToken(1))) continue; |
| if (strcmp(token, "@") == 0) |
| { |
| if (spec->type != SPEC_WIN32) |
| { |
| error( "'@' ordinals not supported for Win16\n" ); |
| continue; |
| } |
| if (!parse_spec_ordinal( -1, spec )) continue; |
| } |
| else if (IsNumberString(token)) |
| { |
| if (!parse_spec_ordinal( atoi(token), spec )) continue; |
| } |
| else |
| { |
| error( "Expected ordinal declaration, got '%s'\n", token ); |
| continue; |
| } |
| if ((token = GetToken(1))) error( "Syntax error near '%s'\n", token ); |
| } |
| |
| current_line = 0; /* no longer parsing the input file */ |
| assign_names( spec ); |
| assign_ordinals( spec ); |
| return !nb_errors; |
| } |
| |
| |
| /******************************************************************* |
| * parse_def_library |
| * |
| * Parse a LIBRARY declaration in a .def file. |
| */ |
| static int parse_def_library( DLLSPEC *spec ) |
| { |
| const char *token = GetToken(1); |
| |
| if (!token) return 1; |
| if (strcmp( token, "BASE" )) |
| { |
| free( spec->file_name ); |
| spec->file_name = xstrdup( token ); |
| if (!(token = GetToken(1))) return 1; |
| } |
| if (strcmp( token, "BASE" )) |
| { |
| error( "Expected library name or BASE= declaration, got '%s'\n", token ); |
| return 0; |
| } |
| if (!(token = GetToken(0))) return 0; |
| if (strcmp( token, "=" )) |
| { |
| error( "Expected '=' after BASE, got '%s'\n", token ); |
| return 0; |
| } |
| if (!(token = GetToken(0))) return 0; |
| /* FIXME: do something with base address */ |
| |
| return 1; |
| } |
| |
| |
| /******************************************************************* |
| * parse_def_stack_heap_size |
| * |
| * Parse a STACKSIZE or HEAPSIZE declaration in a .def file. |
| */ |
| static int parse_def_stack_heap_size( int is_stack, DLLSPEC *spec ) |
| { |
| const char *token = GetToken(0); |
| char *end; |
| unsigned long size; |
| |
| if (!token) return 0; |
| size = strtoul( token, &end, 0 ); |
| if (*end) |
| { |
| error( "Invalid number '%s'\n", token ); |
| return 0; |
| } |
| if (is_stack) spec->stack_size = size / 1024; |
| else spec->heap_size = size / 1024; |
| if (!(token = GetToken(1))) return 1; |
| if (strcmp( token, "," )) |
| { |
| error( "Expected ',' after size, got '%s'\n", token ); |
| return 0; |
| } |
| if (!(token = GetToken(0))) return 0; |
| /* FIXME: do something with reserve size */ |
| return 1; |
| } |
| |
| |
| /******************************************************************* |
| * parse_def_export |
| * |
| * Parse an export declaration in a .def file. |
| */ |
| static int parse_def_export( char *name, DLLSPEC *spec ) |
| { |
| int i, args; |
| const char *token = GetToken(1); |
| |
| ORDDEF *odp = add_entry_point( spec ); |
| memset( odp, 0, sizeof(*odp) ); |
| |
| odp->lineno = current_line; |
| odp->ordinal = -1; |
| odp->name = name; |
| args = remove_stdcall_decoration( odp->name ); |
| if (args == -1) odp->type = TYPE_CDECL; |
| else |
| { |
| odp->type = TYPE_STDCALL; |
| args /= get_ptr_size(); |
| if (args >= sizeof(odp->u.func.arg_types)) |
| { |
| error( "Too many arguments in stdcall function '%s'\n", odp->name ); |
| return 0; |
| } |
| for (i = 0; i < args; i++) odp->u.func.arg_types[i] = 'l'; |
| } |
| |
| /* check for optional internal name */ |
| |
| if (token && !strcmp( token, "=" )) |
| { |
| if (!(token = GetToken(0))) goto error; |
| odp->link_name = xstrdup( token ); |
| remove_stdcall_decoration( odp->link_name ); |
| token = GetToken(1); |
| } |
| else |
| { |
| odp->link_name = xstrdup( name ); |
| } |
| |
| /* check for optional ordinal */ |
| |
| if (token && token[0] == '@') |
| { |
| int ordinal; |
| |
| if (!IsNumberString( token+1 )) |
| { |
| error( "Expected number after '@', got '%s'\n", token+1 ); |
| goto error; |
| } |
| ordinal = atoi( token+1 ); |
| if (!ordinal) |
| { |
| error( "Ordinal 0 is not valid\n" ); |
| goto error; |
| } |
| if (ordinal >= MAX_ORDINALS) |
| { |
| error( "Ordinal number %d too large\n", ordinal ); |
| goto error; |
| } |
| odp->ordinal = ordinal; |
| token = GetToken(1); |
| } |
| |
| /* check for other optional keywords */ |
| |
| if (token && !strcmp( token, "NONAME" )) |
| { |
| if (odp->ordinal == -1) |
| { |
| error( "NONAME requires an ordinal\n" ); |
| goto error; |
| } |
| odp->export_name = odp->name; |
| odp->name = NULL; |
| odp->flags |= FLAG_NONAME; |
| token = GetToken(1); |
| } |
| if (token && !strcmp( token, "PRIVATE" )) |
| { |
| odp->flags |= FLAG_PRIVATE; |
| token = GetToken(1); |
| } |
| if (token && !strcmp( token, "DATA" )) |
| { |
| odp->type = TYPE_EXTERN; |
| token = GetToken(1); |
| } |
| if (token) |
| { |
| error( "Garbage text '%s' found at end of export declaration\n", token ); |
| goto error; |
| } |
| return 1; |
| |
| error: |
| spec->nb_entry_points--; |
| free( odp->name ); |
| return 0; |
| } |
| |
| |
| /******************************************************************* |
| * parse_def_file |
| * |
| * Parse a .def file. |
| */ |
| int parse_def_file( FILE *file, DLLSPEC *spec ) |
| { |
| const char *token; |
| int in_exports = 0; |
| |
| input_file = file; |
| current_line = 0; |
| |
| comment_chars = ";"; |
| separator_chars = ",="; |
| |
| while (get_next_line()) |
| { |
| if (!(token = GetToken(1))) continue; |
| |
| if (!strcmp( token, "LIBRARY" ) || !strcmp( token, "NAME" )) |
| { |
| if (!parse_def_library( spec )) continue; |
| goto end_of_line; |
| } |
| else if (!strcmp( token, "STACKSIZE" )) |
| { |
| if (!parse_def_stack_heap_size( 1, spec )) continue; |
| goto end_of_line; |
| } |
| else if (!strcmp( token, "HEAPSIZE" )) |
| { |
| if (!parse_def_stack_heap_size( 0, spec )) continue; |
| goto end_of_line; |
| } |
| else if (!strcmp( token, "EXPORTS" )) |
| { |
| in_exports = 1; |
| if (!(token = GetToken(1))) continue; |
| } |
| else if (!strcmp( token, "IMPORTS" )) |
| { |
| in_exports = 0; |
| if (!(token = GetToken(1))) continue; |
| } |
| else if (!strcmp( token, "SECTIONS" )) |
| { |
| in_exports = 0; |
| if (!(token = GetToken(1))) continue; |
| } |
| |
| if (!in_exports) continue; /* ignore this line */ |
| if (!parse_def_export( xstrdup(token), spec )) continue; |
| |
| end_of_line: |
| if ((token = GetToken(1))) error( "Syntax error near '%s'\n", token ); |
| } |
| |
| current_line = 0; /* no longer parsing the input file */ |
| assign_names( spec ); |
| assign_ordinals( spec ); |
| return !nb_errors; |
| } |
| |
| |
| /******************************************************************* |
| * add_debug_channel |
| */ |
| static void add_debug_channel( const char *name ) |
| { |
| int i; |
| |
| for (i = 0; i < nb_debug_channels; i++) |
| if (!strcmp( debug_channels[i], name )) return; |
| |
| debug_channels = xrealloc( debug_channels, (nb_debug_channels + 1) * sizeof(*debug_channels)); |
| debug_channels[nb_debug_channels++] = xstrdup(name); |
| } |
| |
| |
| /******************************************************************* |
| * parse_debug_channels |
| * |
| * Parse a source file and extract the debug channel definitions. |
| */ |
| int parse_debug_channels( const char *srcdir, const char *filename ) |
| { |
| FILE *file; |
| int eol_seen = 1; |
| |
| file = open_input_file( srcdir, filename ); |
| while (fgets( ParseBuffer, sizeof(ParseBuffer), file )) |
| { |
| char *channel, *end, *p = ParseBuffer; |
| |
| p = ParseBuffer + strlen(ParseBuffer) - 1; |
| if (!eol_seen) /* continuation line */ |
| { |
| eol_seen = (*p == '\n'); |
| continue; |
| } |
| if ((eol_seen = (*p == '\n'))) *p = 0; |
| |
| p = ParseBuffer; |
| while (isspace(*p)) p++; |
| if (!memcmp( p, "WINE_DECLARE_DEBUG_CHANNEL", 26 ) || |
| !memcmp( p, "WINE_DEFAULT_DEBUG_CHANNEL", 26 )) |
| { |
| p += 26; |
| while (isspace(*p)) p++; |
| if (*p != '(') |
| { |
| error( "invalid debug channel specification '%s'\n", ParseBuffer ); |
| goto next; |
| } |
| p++; |
| while (isspace(*p)) p++; |
| if (!isalpha(*p)) |
| { |
| error( "invalid debug channel specification '%s'\n", ParseBuffer ); |
| goto next; |
| } |
| channel = p; |
| while (isalnum(*p) || *p == '_') p++; |
| end = p; |
| while (isspace(*p)) p++; |
| if (*p != ')') |
| { |
| error( "invalid debug channel specification '%s'\n", ParseBuffer ); |
| goto next; |
| } |
| *end = 0; |
| add_debug_channel( channel ); |
| } |
| next: |
| current_line++; |
| } |
| close_input_file( file ); |
| return !nb_errors; |
| } |