| /* |
| * Spec file parser |
| * |
| * Copyright 1993 Robert J. Amstadt |
| * Copyright 1995 Martin von Loewis |
| * Copyright 1995, 1996, 1997 Alexandre Julliard |
| * Copyright 1997 Eric Youngdale |
| * Copyright 1999 Ulrich Weigand |
| */ |
| |
| #include <assert.h> |
| #include <ctype.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include "config.h" |
| #include "winbase.h" |
| #include "build.h" |
| |
| int current_line = 0; |
| |
| static SPEC_TYPE SpecType = SPEC_INVALID; |
| |
| static char ParseBuffer[512]; |
| static char *ParseNext = ParseBuffer; |
| static char ParseSaveChar; |
| static FILE *input_file; |
| |
| static const char * const TypeNames[TYPE_NBTYPES] = |
| { |
| "variable", /* TYPE_VARIABLE */ |
| "pascal16", /* TYPE_PASCAL_16 */ |
| "pascal", /* TYPE_PASCAL */ |
| "equate", /* TYPE_ABS */ |
| "register", /* TYPE_REGISTER */ |
| "interrupt", /* TYPE_INTERRUPT */ |
| "stub", /* TYPE_STUB */ |
| "stdcall", /* TYPE_STDCALL */ |
| "cdecl", /* TYPE_CDECL */ |
| "varargs", /* TYPE_VARARGS */ |
| "extern", /* TYPE_EXTERN */ |
| "forward" /* TYPE_FORWARD */ |
| }; |
| |
| static const char * const FlagNames[] = |
| { |
| "noimport", /* FLAG_NOIMPORT */ |
| "norelay", /* FLAG_NORELAY */ |
| "ret64", /* FLAG_RET64 */ |
| "i386", /* FLAG_I386 */ |
| NULL |
| }; |
| |
| static int IsNumberString(char *s) |
| { |
| while (*s) if (!isdigit(*s++)) return 0; |
| return 1; |
| } |
| |
| inline static int is_token_separator( char ch ) |
| { |
| return (ch == '(' || ch == ')' || ch == '-'); |
| } |
| |
| static char * GetTokenInLine(void) |
| { |
| char *p; |
| char *token; |
| |
| if (ParseNext != ParseBuffer) |
| { |
| if (ParseSaveChar == '\0') |
| return NULL; |
| *ParseNext = ParseSaveChar; |
| } |
| |
| /* |
| * Remove initial white space. |
| */ |
| for (p = ParseNext; isspace(*p); p++) |
| ; |
| |
| if ((*p == '\0') || (*p == '#')) |
| return NULL; |
| |
| /* |
| * Find end of token. |
| */ |
| token = p++; |
| if (!is_token_separator(*token)) |
| while (*p != '\0' && !is_token_separator(*p) && !isspace(*p)) |
| p++; |
| |
| ParseSaveChar = *p; |
| ParseNext = p; |
| *p = '\0'; |
| |
| return token; |
| } |
| |
| static char * GetToken( int allow_eof ) |
| { |
| char *token; |
| |
| while ((token = GetTokenInLine()) == NULL) |
| { |
| ParseNext = ParseBuffer; |
| while (1) |
| { |
| current_line++; |
| if (fgets(ParseBuffer, sizeof(ParseBuffer), input_file) == NULL) |
| { |
| if (!allow_eof) fatal_error( "Unexpected end of file\n" ); |
| return NULL; |
| } |
| if (ParseBuffer[0] != '#') |
| break; |
| } |
| } |
| return token; |
| } |
| |
| |
| /******************************************************************* |
| * ParseDebug |
| * |
| * Parse a debug channel definition. |
| */ |
| static void ParseDebug(void) |
| { |
| char *token = GetToken(0); |
| if (*token != '(') fatal_error( "Expected '(' got '%s'\n", token ); |
| for (;;) |
| { |
| token = GetToken(0); |
| if (*token == ')') break; |
| debug_channels = xrealloc( debug_channels, |
| (nb_debug_channels + 1) * sizeof(*debug_channels)); |
| debug_channels[nb_debug_channels++] = xstrdup(token); |
| } |
| } |
| |
| |
| /******************************************************************* |
| * ParseIgnore |
| * |
| * Parse an 'ignore' definition. |
| */ |
| static void ParseIgnore(void) |
| { |
| char *token = GetToken(0); |
| if (*token != '(') fatal_error( "Expected '(' got '%s'\n", token ); |
| for (;;) |
| { |
| token = GetToken(0); |
| if (*token == ')') break; |
| add_ignore_symbol( token ); |
| } |
| } |
| |
| |
| /******************************************************************* |
| * ParseVariable |
| * |
| * Parse a variable definition. |
| */ |
| static void ParseVariable( ORDDEF *odp ) |
| { |
| char *endptr; |
| int *value_array; |
| int n_values; |
| int value_array_size; |
| |
| char *token = GetToken(0); |
| if (*token != '(') fatal_error( "Expected '(' got '%s'\n", token ); |
| |
| n_values = 0; |
| value_array_size = 25; |
| value_array = xmalloc(sizeof(*value_array) * value_array_size); |
| |
| for (;;) |
| { |
| token = GetToken(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') |
| fatal_error( "Expected number value, got '%s'\n", token ); |
| } |
| |
| odp->u.var.n_values = n_values; |
| odp->u.var.values = xrealloc(value_array, sizeof(*value_array) * n_values); |
| } |
| |
| |
| /******************************************************************* |
| * ParseExportFunction |
| * |
| * Parse a function definition. |
| */ |
| static void ParseExportFunction( ORDDEF *odp ) |
| { |
| char *token; |
| unsigned int i; |
| |
| switch(SpecType) |
| { |
| case SPEC_WIN16: |
| if (odp->type == TYPE_STDCALL) |
| fatal_error( "'stdcall' not supported for Win16\n" ); |
| if (odp->type == TYPE_VARARGS) |
| fatal_error( "'varargs' not supported for Win16\n" ); |
| break; |
| case SPEC_WIN32: |
| if ((odp->type == TYPE_PASCAL) || (odp->type == TYPE_PASCAL_16)) |
| fatal_error( "'pascal' not supported for Win32\n" ); |
| break; |
| default: |
| break; |
| } |
| |
| token = GetToken(0); |
| if (*token != '(') fatal_error( "Expected '(' got '%s'\n", token ); |
| |
| for (i = 0; i < sizeof(odp->u.func.arg_types); i++) |
| { |
| token = GetToken(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 (i < sizeof(odp->u.func.arg_types)) odp->u.func.arg_types[i] = 'l'; |
| } |
| else fatal_error( "Unknown variable type '%s'\n", token ); |
| |
| if (SpecType == SPEC_WIN32) |
| { |
| if (strcmp(token, "long") && |
| strcmp(token, "ptr") && |
| strcmp(token, "str") && |
| strcmp(token, "wstr") && |
| strcmp(token, "double")) |
| { |
| fatal_error( "Type '%s' not supported for Win32\n", token ); |
| } |
| } |
| } |
| if ((*token != ')') || (i >= sizeof(odp->u.func.arg_types))) |
| fatal_error( "Too many arguments\n" ); |
| |
| odp->u.func.arg_types[i] = '\0'; |
| if ((odp->type == TYPE_STDCALL) && !i) |
| odp->type = TYPE_CDECL; /* stdcall is the same as cdecl for 0 args */ |
| if (odp->type == TYPE_VARARGS) |
| odp->flags |= FLAG_NORELAY; /* no relay debug possible for varags entry point */ |
| odp->link_name = xstrdup( GetToken(0) ); |
| } |
| |
| |
| /******************************************************************* |
| * ParseEquate |
| * |
| * Parse an 'equate' definition. |
| */ |
| static void ParseEquate( ORDDEF *odp ) |
| { |
| char *endptr; |
| |
| char *token = GetToken(0); |
| int value = strtol(token, &endptr, 0); |
| if (endptr == NULL || *endptr != '\0') |
| fatal_error( "Expected number value, got '%s'\n", token ); |
| if (SpecType == SPEC_WIN32) |
| fatal_error( "'equate' not supported for Win32\n" ); |
| odp->u.abs.value = value; |
| } |
| |
| |
| /******************************************************************* |
| * ParseStub |
| * |
| * Parse a 'stub' definition. |
| */ |
| static void ParseStub( ORDDEF *odp ) |
| { |
| odp->u.func.arg_types[0] = '\0'; |
| odp->link_name = xstrdup(""); |
| } |
| |
| |
| /******************************************************************* |
| * ParseInterrupt |
| * |
| * Parse an 'interrupt' definition. |
| */ |
| static void ParseInterrupt( ORDDEF *odp ) |
| { |
| char *token; |
| |
| if (SpecType == SPEC_WIN32) |
| fatal_error( "'interrupt' not supported for Win32\n" ); |
| |
| token = GetToken(0); |
| if (*token != '(') fatal_error( "Expected '(' got '%s'\n", token ); |
| |
| token = GetToken(0); |
| if (*token != ')') fatal_error( "Expected ')' got '%s'\n", token ); |
| |
| odp->u.func.arg_types[0] = '\0'; |
| odp->link_name = xstrdup( GetToken(0) ); |
| } |
| |
| |
| /******************************************************************* |
| * ParseExtern |
| * |
| * Parse an 'extern' definition. |
| */ |
| static void ParseExtern( ORDDEF *odp ) |
| { |
| if (SpecType == SPEC_WIN16) fatal_error( "'extern' not supported for Win16\n" ); |
| odp->link_name = xstrdup( GetToken(0) ); |
| /* 'extern' definitions are not available for implicit import */ |
| odp->flags |= FLAG_NOIMPORT; |
| } |
| |
| |
| /******************************************************************* |
| * ParseForward |
| * |
| * Parse a 'forward' definition. |
| */ |
| static void ParseForward( ORDDEF *odp ) |
| { |
| if (SpecType == SPEC_WIN16) fatal_error( "'forward' not supported for Win16\n" ); |
| odp->link_name = xstrdup( GetToken(0) ); |
| } |
| |
| |
| /******************************************************************* |
| * ParseFlags |
| * |
| * Parse the optional flags for an entry point |
| */ |
| static char *ParseFlags( ORDDEF *odp ) |
| { |
| unsigned int i; |
| char *token; |
| |
| do |
| { |
| token = GetToken(0); |
| for (i = 0; FlagNames[i]; i++) |
| if (!strcmp( FlagNames[i], token )) break; |
| if (!FlagNames[i]) fatal_error( "Unknown flag '%s'\n", token ); |
| odp->flags |= 1 << i; |
| token = GetToken(0); |
| } while (*token == '-'); |
| |
| return token; |
| } |
| |
| /******************************************************************* |
| * fix_export_name |
| * |
| * Fix an exported function name by removing a possible @xx suffix |
| */ |
| static void fix_export_name( char *name ) |
| { |
| char *p, *end = strrchr( name, '@' ); |
| if (!end || !end[1] || end == name) return; |
| /* make sure all the rest is digits */ |
| for (p = end + 1; *p; p++) if (!isdigit(*p)) return; |
| *end = 0; |
| } |
| |
| /******************************************************************* |
| * ParseOrdinal |
| * |
| * Parse an ordinal definition. |
| */ |
| static void ParseOrdinal(int ordinal) |
| { |
| char *token; |
| |
| ORDDEF *odp = xmalloc( sizeof(*odp) ); |
| memset( odp, 0, sizeof(*odp) ); |
| EntryPoints[nb_entry_points++] = odp; |
| |
| token = GetToken(0); |
| |
| 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) |
| fatal_error( "Expected type after ordinal, found '%s' instead\n", token ); |
| |
| token = GetToken(0); |
| if (*token == '-') token = ParseFlags( odp ); |
| |
| odp->name = xstrdup( token ); |
| fix_export_name( odp->name ); |
| odp->lineno = current_line; |
| odp->ordinal = ordinal; |
| |
| switch(odp->type) |
| { |
| case TYPE_VARIABLE: |
| ParseVariable( odp ); |
| break; |
| case TYPE_REGISTER: |
| case TYPE_PASCAL_16: |
| case TYPE_PASCAL: |
| case TYPE_STDCALL: |
| case TYPE_VARARGS: |
| case TYPE_CDECL: |
| ParseExportFunction( odp ); |
| break; |
| case TYPE_INTERRUPT: |
| ParseInterrupt( odp ); |
| break; |
| case TYPE_ABS: |
| ParseEquate( odp ); |
| break; |
| case TYPE_STUB: |
| ParseStub( odp ); |
| break; |
| case TYPE_EXTERN: |
| ParseExtern( odp ); |
| break; |
| case TYPE_FORWARD: |
| ParseForward( odp ); |
| break; |
| default: |
| assert( 0 ); |
| } |
| |
| #ifndef __i386__ |
| if (odp->flags & FLAG_I386) |
| { |
| /* ignore this entry point on non-Intel archs */ |
| EntryPoints[--nb_entry_points] = NULL; |
| free( odp ); |
| return; |
| } |
| #endif |
| |
| if (ordinal != -1) |
| { |
| if (ordinal >= MAX_ORDINALS) fatal_error( "Ordinal number %d too large\n", ordinal ); |
| if (ordinal > Limit) Limit = ordinal; |
| if (ordinal < Base) Base = ordinal; |
| odp->ordinal = ordinal; |
| Ordinals[ordinal] = odp; |
| } |
| |
| if (!strcmp( odp->name, "@" )) |
| { |
| if (ordinal == -1) |
| fatal_error( "Nameless function needs an explicit ordinal number\n" ); |
| if (SpecType != SPEC_WIN32) |
| fatal_error( "Nameless functions not supported for Win16\n" ); |
| odp->name[0] = 0; |
| } |
| else Names[nb_names++] = odp; |
| } |
| |
| |
| static int name_compare( const void *name1, const void *name2 ) |
| { |
| ORDDEF *odp1 = *(ORDDEF **)name1; |
| ORDDEF *odp2 = *(ORDDEF **)name2; |
| return strcmp( odp1->name, odp2->name ); |
| } |
| |
| /******************************************************************* |
| * sort_names |
| * |
| * Sort the name array and catch duplicates. |
| */ |
| static void sort_names(void) |
| { |
| int i; |
| |
| if (!nb_names) return; |
| |
| /* sort the list of names */ |
| qsort( Names, nb_names, sizeof(Names[0]), name_compare ); |
| |
| /* check for duplicate names */ |
| for (i = 0; i < nb_names - 1; i++) |
| { |
| if (!strcmp( Names[i]->name, Names[i+1]->name )) |
| { |
| current_line = max( Names[i]->lineno, Names[i+1]->lineno ); |
| fatal_error( "'%s' redefined (previous definition at line %d)\n", |
| Names[i]->name, min( Names[i]->lineno, Names[i+1]->lineno ) ); |
| } |
| } |
| } |
| |
| |
| /******************************************************************* |
| * ParseTopLevel |
| * |
| * Parse a spec file. |
| */ |
| SPEC_TYPE ParseTopLevel( FILE *file ) |
| { |
| char *token; |
| |
| input_file = file; |
| current_line = 1; |
| while ((token = GetToken(1)) != NULL) |
| { |
| if (strcmp(token, "name") == 0) |
| { |
| strcpy(DLLName, GetToken(0)); |
| } |
| else if (strcmp(token, "file") == 0) |
| { |
| strcpy(DLLFileName, GetToken(0)); |
| strupper(DLLFileName); |
| } |
| else if (strcmp(token, "type") == 0) |
| { |
| token = GetToken(0); |
| if (!strcmp(token, "win16" )) SpecType = SPEC_WIN16; |
| else if (!strcmp(token, "win32" )) SpecType = SPEC_WIN32; |
| else fatal_error( "Type must be 'win16' or 'win32'\n" ); |
| } |
| else if (strcmp(token, "mode") == 0) |
| { |
| token = GetToken(0); |
| if (!strcmp(token, "dll" )) SpecMode = SPEC_MODE_DLL; |
| else if (!strcmp(token, "guiexe" )) SpecMode = SPEC_MODE_GUIEXE; |
| else if (!strcmp(token, "cuiexe" )) SpecMode = SPEC_MODE_CUIEXE; |
| else if (!strcmp(token, "guiexe_unicode" )) SpecMode = SPEC_MODE_GUIEXE_UNICODE; |
| else if (!strcmp(token, "cuiexe_unicode" )) SpecMode = SPEC_MODE_CUIEXE_UNICODE; |
| else fatal_error( "Mode must be 'dll', 'guiexe', 'cuiexe', 'guiexe_unicode' or 'cuiexe_unicode'\n" ); |
| } |
| else if (strcmp(token, "heap") == 0) |
| { |
| token = GetToken(0); |
| if (!IsNumberString(token)) fatal_error( "Expected number after heap\n" ); |
| DLLHeapSize = atoi(token); |
| } |
| else if (strcmp(token, "init") == 0) |
| { |
| if (SpecType == SPEC_WIN16) |
| fatal_error( "init cannot be used for Win16 spec files\n" ); |
| init_func = xstrdup( GetToken(0) ); |
| } |
| else if (strcmp(token, "import") == 0) |
| { |
| const char* name; |
| int delay = 0; |
| |
| if (SpecType != SPEC_WIN32) |
| fatal_error( "Imports not supported for Win16\n" ); |
| name = GetToken(0); |
| if (*name == '-') |
| { |
| name = GetToken(0); |
| if (!strcmp(name, "delay")) |
| { |
| name = GetToken(0); |
| delay = 1; |
| } |
| else fatal_error( "Unknown option '%s' for import directive\n", name ); |
| } |
| add_import_dll( name, delay ); |
| } |
| else if (strcmp(token, "rsrc") == 0) |
| { |
| if (SpecType != SPEC_WIN16) load_res32_file( GetToken(0) ); |
| else load_res16_file( GetToken(0) ); |
| } |
| else if (strcmp(token, "owner") == 0) |
| { |
| if (SpecType != SPEC_WIN16) |
| fatal_error( "Owner only supported for Win16 spec files\n" ); |
| strcpy( owner_name, GetToken(0) ); |
| } |
| else if (strcmp(token, "debug_channels") == 0) |
| { |
| if (SpecType != SPEC_WIN32) |
| fatal_error( "debug channels only supported for Win32 spec files\n" ); |
| ParseDebug(); |
| } |
| else if (strcmp(token, "ignore") == 0) |
| { |
| if (SpecType != SPEC_WIN32) |
| fatal_error( "'ignore' only supported for Win32 spec files\n" ); |
| ParseIgnore(); |
| } |
| else if (strcmp(token, "@") == 0) |
| { |
| if (SpecType != SPEC_WIN32) |
| fatal_error( "'@' ordinals not supported for Win16\n" ); |
| ParseOrdinal( -1 ); |
| } |
| else if (IsNumberString(token)) |
| { |
| ParseOrdinal( atoi(token) ); |
| } |
| else |
| fatal_error( "Expected name, id, length or ordinal\n" ); |
| } |
| |
| if (!DLLFileName[0]) |
| { |
| if (SpecMode == SPEC_MODE_DLL) |
| sprintf( DLLFileName, "%s.dll", DLLName ); |
| else |
| sprintf( DLLFileName, "%s.exe", DLLName ); |
| } |
| |
| if (SpecType == SPEC_INVALID) fatal_error( "Missing 'type' declaration\n" ); |
| if (SpecType == SPEC_WIN16 && !owner_name[0]) |
| fatal_error( "'owner' not specified for Win16 dll\n" ); |
| |
| current_line = 0; /* no longer parsing the input file */ |
| sort_names(); |
| return SpecType; |
| } |