Commit c7a3fec5 authored by Jon Griffiths's avatar Jon Griffiths Committed by Alexandre Julliard

Added spec generation tool specmaker.

parent 8d91b501
......@@ -6966,6 +6966,7 @@ scheduler/Makefile
server/Makefile
tools/Makefile
tools/cvdump/Makefile
tools/specmaker/Makefile
tools/winebuild/Makefile
tools/winelauncher
tools/wmc/Makefile
......@@ -7209,6 +7210,7 @@ scheduler/Makefile
server/Makefile
tools/Makefile
tools/cvdump/Makefile
tools/specmaker/Makefile
tools/winebuild/Makefile
tools/winelauncher
tools/wmc/Makefile
......
......@@ -1284,6 +1284,7 @@ scheduler/Makefile
server/Makefile
tools/Makefile
tools/cvdump/Makefile
tools/specmaker/Makefile
tools/winebuild/Makefile
tools/winelauncher
tools/wmc/Makefile
......
......@@ -11,11 +11,13 @@ C_SRCS = makedep.c fnt2bdf.c bin2res.c
SUBDIRS = \
cvdump \
specmaker \
winebuild \
wmc \
wrc
INSTALLSUBDIRS = \
specmaker \
winebuild \
wmc \
wrc
......@@ -26,7 +28,7 @@ EXTRASUBDIRS = \
winapi_check/win32 \
wineconf.libs
all: $(PROGRAMS) winebuild wmc wrc
all: $(PROGRAMS) specmaker winebuild wmc wrc
@MAKE_RULES@
......
DEFS = -D__WINE__
TOPSRCDIR = @top_srcdir@
TOPOBJDIR = ../..
SRCDIR = @srcdir@
VPATH = @srcdir@
PROGRAMS = specmaker
MODULE = none
C_SRCS = \
dll.c \
main.c \
misc.c \
msmangle.c \
output.c \
search.c \
symbol.c
all: $(PROGRAMS)
@MAKE_RULES@
specmaker: $(OBJS)
$(CC) $(CFLAGS) -o specmaker $(OBJS) $(LDFLAGS)
install:: $(PROGRAMS)
[ -d $(bindir) ] || $(MKDIR) $(bindir)
$(INSTALL_PROGRAM) specmaker $(bindir)/specmaker
$(INSTALL_PROGRAM) function_grep.pl $(bindir)/function_grep.pl
uninstall::
$(RM) $(bindir)/specmaker
$(RM) $(bindir)/function_grep.pl
### Dependencies:
/*
* DLL symbol extraction
*
* Copyright 2000 Jon Griffiths
*/
#include "specmaker.h"
/* DOS/PE Header details */
#define DOS_HEADER_LEN 64
#define DOS_MAGIC 0x5a4d
#define DOS_PE_OFFSET 60
#define PE_HEADER_LEN 248
#define PE_MAGIC 0x4550
#define PE_COUNT_OFFSET 6
#define PE_EXPORTS_OFFSET 120
#define PE_EXPORTS_SIZE PE_EXPORTS_OFFSET + 4
#define SECTION_HEADER_LEN 40
#define SECTION_ADDR_OFFSET 12
#define SECTION_ADDR_SIZE SECTION_ADDR_OFFSET + 4
#define SECTION_POS_OFFSET SECTION_ADDR_SIZE + 4
#define EXPORT_COUNT_OFFSET 24
#define EXPORT_NAME_OFFSET EXPORT_COUNT_OFFSET + 8
/* Minimum memory needed to read both headers into a buffer */
#define MIN_HEADER_LEN (PE_HEADER_LEN * sizeof (unsigned char))
/* Normalise a pointer in the exports section */
#define REBASE(x) ((x) - exports)
/* Module globals */
static FILE *dll_file = NULL;
static char **dll_symbols = NULL;
static size_t dll_num_exports = 0;
/* Get a short from a memory block */
static inline size_t get_short (const char *mem)
{
return *((const unsigned char *)mem) +
(*((const unsigned char *)mem + 1) << 8);
}
/* Get an integer from a memory block */
static inline size_t get_int (const char *mem)
{
assert (sizeof (char) == (size_t)1);
return get_short (mem) + (get_short (mem + 2) << 16);
}
static void dll_close (void);
/*******************************************************************
* dll_open
*
* Open a DLL and read in exported symbols
*/
void dll_open (const char *dll_name)
{
size_t code = 0, code_len = 0, exports, exports_len, count, symbol_data;
char *buff = NULL;
dll_file = open_file (dll_name, ".dll", "r");
atexit (dll_close);
/* Read in the required DOS and PE Headers */
if (!(buff = (char *) malloc (MIN_HEADER_LEN)))
fatal ("Out of memory");
if (fread (buff, DOS_HEADER_LEN, 1, dll_file) != 1 ||
get_short (buff) != DOS_MAGIC)
fatal ("Error reading DOS header");
if (fseek (dll_file, get_int (buff + DOS_PE_OFFSET), SEEK_SET) == -1)
fatal ("Error seeking PE header");
if (fread (buff, PE_HEADER_LEN, 1, dll_file) != 1 ||
get_int (buff) != PE_MAGIC)
fatal ("Error reading PE header");
exports = get_int (buff + PE_EXPORTS_OFFSET);
exports_len = get_int (buff + PE_EXPORTS_SIZE);
if (!exports || !exports_len)
fatal ("No exports in DLL");
if (!(count = get_short (buff + PE_COUNT_OFFSET)))
fatal ("No sections in DLL");
if (VERBOSE)
printf ("DLL has %d sections\n", count);
/* Iterate through sections until we find exports */
while (count--)
{
if (fread (buff, SECTION_HEADER_LEN, 1, dll_file) != 1)
fatal ("Section read error");
code = get_int (buff + SECTION_ADDR_OFFSET);
code_len = get_int (buff + SECTION_ADDR_SIZE);
if (code <= exports && code + code_len > exports)
break;
}
if (!count)
fatal ("No export section");
code_len -= (exports - code);
if (code_len < exports_len)
fatal ("Corrupt exports");
/* Load exports section */
if (fseek (dll_file, get_int (buff + SECTION_POS_OFFSET)
+ exports - code, SEEK_SET) == -1)
fatal ("Export section seek error");
if (VERBOSE)
printf ("Export data size = %d bytes\n", code_len);
if (!(buff = (char *) realloc (buff, code_len)))
fatal ("Out of memory");
if (fread (buff, code_len, 1, dll_file) != 1)
fatal ("Read error");
dll_close();
/* Locate symbol names */
symbol_data = REBASE( get_int (buff + EXPORT_NAME_OFFSET));
if (symbol_data > code_len)
fatal ("Corrupt exports section");
if (!(dll_num_exports = get_int (buff + EXPORT_COUNT_OFFSET)))
fatal ("No export count");
if (!(dll_symbols = (char **) malloc (dll_num_exports * sizeof (char *))))
fatal ("Out of memory");
/* Read symbol names into 'dll_symbols' */
count = 0;
while (count < dll_num_exports)
{
const int symbol_offset = get_int (buff + symbol_data + count * 4);
const char *symbol_name_ptr = REBASE (buff + symbol_offset);
assert(symbol_name_ptr);
dll_symbols[count] = strdup (symbol_name_ptr);
assert(dll_symbols[count]);
count++;
}
if (NORMAL)
printf ("%d exported symbols in DLL\n", dll_num_exports);
free (buff);
/* Set DLL output names */
if ((buff = strrchr (globals.input_name, '/')))
globals.input_name = buff + 1; /* Strip path */
OUTPUT_UC_DLL_NAME = str_toupper( strdup (OUTPUT_DLL_NAME));
}
/*******************************************************************
* dll_next_symbol
*
* Get next exported symbol from dll
*/
char* dll_next_symbol ()
{
static unsigned int current_export = 0;
assert (current_export <= dll_num_exports);
if (current_export == dll_num_exports)
return NULL;
assert (dll_symbols);
assert (dll_symbols [current_export]);
return strdup (dll_symbols [current_export++]);
}
/*******************************************************************
* dll_close
*
* Free resources used by DLL
*/
static void dll_close (void)
{
size_t i;
if (dll_file)
{
fclose (dll_file);
dll_file = NULL;
}
if (dll_symbols)
{
for (i = 0; i < dll_num_exports; i++)
if (dll_symbols [i])
free (dll_symbols [i]);
free (dll_symbols);
dll_symbols = NULL;
}
}
#! /usr/bin/perl
# Copyright 2000 Patrik Stridvall
use strict;
my $invert = 0;
my $pattern;
my @files = ();
while(defined($_ = shift)) {
if(/^-/) {
if(/^-v$/) {
$invert = 1;
}
} else {
if(!defined($pattern)) {
$pattern = $_;
} else {
push @files, $_;
}
}
}
foreach my $file (@files) {
open(IN, "< $file");
my $level = 0;
my $extern_c = 0;
my $again = 0;
my $lookahead = 0;
while($again || defined(my $line = <IN>)) {
if(!$again) {
chomp $line;
if($lookahead) {
$lookahead = 0;
$_ .= "\n" . $line;
} else {
$_ = $line;
}
} else {
$again = 0;
}
# remove C comments
if(s/^(.*?)(\/\*.*?\*\/)(.*)$/$1 $3/s) {
$again = 1;
next;
} elsif(/^(.*?)\/\*/s) {
$lookahead = 1;
next;
}
# remove C++ comments
while(s/^(.*?)\/\/.*?$/$1\n/s) { $again = 1; }
if($again) { next; }
# remove empty rows
if(/^\s*$/) { next; }
# remove preprocessor directives
if(s/^\s*\#/\#/m) {
if(/^\#.*?\\$/m) {
$lookahead = 1;
next;
} elsif(s/^\#\s*(.*?)(\s+(.*?))?\s*$//m) {
next;
}
}
# Remove extern "C"
if(s/^\s*extern\s+"C"\s+\{//m) {
$extern_c = 1;
$again = 1;
next;
}
if($level > 0)
{
my $line = "";
while(/^[^\{\}]/) {
s/^([^\{\}\'\"]*)//s;
$line .= $1;
if(s/^\'//) {
$line .= "\'";
while(/^./ && !s/^\'//) {
s/^([^\'\\]*)//s;
$line .= $1;
if(s/^\\//) {
$line .= "\\";
if(s/^(.)//s) {
$line .= $1;
if($1 eq "0") {
s/^(\d{0,3})//s;
$line .= $1;
}
}
}
}
$line .= "\'";
} elsif(s/^\"//) {
$line .= "\"";
while(/^./ && !s/^\"//) {
s/^([^\"\\]*)//s;
$line .= $1;
if(s/^\\//) {
$line .= "\\";
if(s/^(.)//s) {
$line .= $1;
if($1 eq "0") {
s/^(\d{0,3})//s;
$line .= $1;
}
}
}
}
$line .= "\"";
}
}
if(s/^\{//) {
$_ = $'; $again = 1;
$line .= "{";
$level++;
} elsif(s/^\}//) {
$_ = $'; $again = 1;
$line .= "}" if $level > 1;
$level--;
if($level == -1 && $extern_c) {
$extern_c = 0;
$level = 0;
}
}
next;
} elsif(/^class[^\}]*{/) {
$_ = $'; $again = 1;
$level++;
next;
} elsif(/^class[^\}]*$/) {
$lookahead = 1;
next;
} elsif(/^typedef[^\}]*;/) {
next;
} elsif(/(extern\s+|static\s+)?
(?:__inline__\s+|__inline\s+|inline\s+)?
((struct\s+|union\s+|enum\s+)?(?:\w+(?:\:\:(?:\s*operator\s*[^\)\s]+)?)?)+((\s*(?:\*|\&))+\s*|\s+))
((__cdecl|__stdcall|CDECL|VFWAPIV|VFWAPI|WINAPIV|WINAPI|CALLBACK)\s+)?
((?:\w+(?:\:\:)?)+(\(\w+\))?)\s*\(([^\)]*)\)\s*
(?:\w+(?:\s*\([^\)]*\))?\s*)*\s*
(\{|\;)/sx)
{
$_ = $'; $again = 1;
if($11 eq "{") {
$level++;
}
my $linkage = $1;
my $return_type = $2;
my $calling_convention = $7;
my $name = $8;
my $arguments = $10;
if(!defined($linkage)) {
$linkage = "";
}
if(!defined($calling_convention)) {
$calling_convention = "";
}
$linkage =~ s/\s*$//;
$return_type =~ s/\s*$//;
$return_type =~ s/\s*\*\s*/*/g;
$return_type =~ s/(\*+)/ $1/g;
$arguments =~ y/\t\n/ /;
$arguments =~ s/^\s*(.*?)\s*$/$1/;
if($arguments eq "") { $arguments = "void" }
my @argument_types;
my @argument_names;
my @arguments = split(/,/, $arguments);
foreach my $n (0..$#arguments) {
my $argument_type = "";
my $argument_name = "";
my $argument = $arguments[$n];
$argument =~ s/^\s*(.*?)\s*$/$1/;
# print " " . ($n + 1) . ": '$argument'\n";
$argument =~ s/^(IN OUT(?=\s)|IN(?=\s)|OUT(?=\s)|\s*)\s*//;
$argument =~ s/^(const(?=\s)|CONST(?=\s)|__const(?=\s)|__restrict(?=\s)|\s*)\s*//;
if($argument =~ /^\.\.\.$/) {
$argument_type = "...";
$argument_name = "...";
} elsif($argument =~ /^
((?:struct\s+|union\s+|enum\s+|(?:signed\s+|unsigned\s+)
(?:short\s+(?=int)|long\s+(?=int))?)?(?:\w+(?:\:\:)?)+)\s*
((?:const(?=\s)|CONST(?=\s)|__const(?=\s)|__restrict(?=\s))?\s*(?:\*\s*?)*)\s*
(?:const(?=\s)|CONST(?=\s)|__const(?=\s)|__restrict(?=\s))?\s*
(\w*)\s*
(?:\[\]|\s+OPTIONAL)?/x)
{
$argument_type = "$1";
if($2 ne "") {
$argument_type .= " $2";
}
$argument_name = $3;
$argument_type =~ s/\s*const\s*/ /;
$argument_type =~ s/^\s*(.*?)\s*$/$1/;
$argument_name =~ s/^\s*(.*?)\s*$/$1/;
} else {
die "$file: $.: syntax error: '$argument'\n";
}
$argument_types[$n] = $argument_type;
$argument_names[$n] = $argument_name;
# print " " . ($n + 1) . ": '$argument_type': '$argument_name'\n";
}
if($#argument_types == 0 && $argument_types[0] =~ /^void$/i) {
$#argument_types = -1;
$#argument_names = -1;
}
@arguments = ();
foreach my $n (0..$#argument_types) {
if($argument_names[$n] && $argument_names[$n] ne "...") {
if($argument_types[$n] !~ /\*$/) {
$arguments[$n] = $argument_types[$n] . " " . $argument_names[$n];
} else {
$arguments[$n] = $argument_types[$n] . $argument_names[$n];
}
} else {
$arguments[$n] = $argument_types[$n];
}
}
$arguments = join(", ", @arguments);
if(!$arguments) { $arguments = "void"; }
if((!$invert && $name =~ /$pattern/) || ($invert && $name !~ /$pattern/)) {
if($calling_convention) {
print "$return_type $calling_convention $name($arguments)\n";
} else {
if($return_type =~ /\*$/) {
print "$return_type$name($arguments)\n";
} else {
print "$return_type $name($arguments)\n";
}
}
}
} elsif(/\'[^\']*\'/s) {
$_ = $'; $again = 1;
} elsif(/\"[^\"]*\"/s) {
$_ = $'; $again = 1;
} elsif(/;/s) {
$_ = $'; $again = 1;
} elsif(/extern\s+"C"\s+{/s) {
$_ = $'; $again = 1;
} elsif(/\{/s) {
$_ = $'; $again = 1;
$level++;
} else {
$lookahead = 1;
}
}
close(IN);
}
/*
* Option processing and main()
*
* Copyright 2000 Jon Griffiths
*/
#include "specmaker.h"
_globals globals; /* All global variables */
static void do_include (const char *arg)
{
globals.directory = arg;
globals.do_code = 1;
}
static inline const char* strip_ext (const char *str)
{
char *ext = strstr(str, ".dll");
if (ext)
return str_substring (str, ext);
else
return strdup (str);
}
static void do_name (const char *arg)
{
globals.dll_name = strip_ext (arg);
}
static void do_input (const char *arg)
{
globals.input_name = strip_ext (arg);
}
static void do_code (void)
{
globals.do_code = 1;
}
static void do_trace (void)
{
globals.do_trace = 1;
globals.do_code = 1;
}
static void do_forward (const char *arg)
{
globals.forward_dll = arg;
globals.do_trace = 1;
globals.do_code = 1;
}
static void do_document (void)
{
globals.do_documentation = 1;
}
static void do_cdecl (void)
{
globals.do_cdecl = 1;
}
static void do_quiet (void)
{
globals.do_quiet = 1;
}
static void do_start (const char *arg)
{
globals.start_ordinal = atoi (arg);
if (!globals.start_ordinal)
fatal ("Invalid -s option (must be numeric)");
}
static void do_end (const char *arg)
{
globals.end_ordinal = atoi (arg);
if (!globals.end_ordinal)
fatal ("Invalid -e option (must be numeric)");
}
static void do_verbose (void)
{
globals.do_verbose = 1;
}
struct option
{
const char *name;
int has_arg;
void (*func) ();
const char *usage;
};
static const struct option option_table[] = {
{"-d", 1, do_input, "-d dll Use dll for input file (mandatory)"},
{"-h", 0, do_usage, "-h Display this help message"},
{"-I", 1, do_include, "-I dir Look for prototypes in 'dir' (implies -c)"},
{"-o", 1, do_name, "-o name Set the output dll name (default: dll)"},
{"-c", 0, do_code, "-c Generate skeleton code (requires -I)"},
{"-t", 0, do_trace, "-t TRACE arguments (implies -c)"},
{"-f", 1, do_forward, "-f dll Forward calls to 'dll' (implies -t)"},
{"-D", 0, do_document, "-D Generate documentation"},
{"-C", 0, do_cdecl, "-C Assume __cdecl calls (default: __stdcall)"},
{"-s", 1, do_start, "-s num Start prototype search after symbol 'num'"},
{"-e", 1, do_end, "-e num End prototype search after symbol 'num'"},
{"-q", 0, do_quiet, "-q Don't show progress (quiet)."},
{"-v", 0, do_verbose, "-v Show lots of detail while working (verbose)."},
{NULL, 0, NULL, NULL}
};
void do_usage (void)
{
const struct option *opt;
printf ("Usage: specmaker [options] -d dll\n\nOptions:\n");
for (opt = option_table; opt->name; opt++)
printf (" %s\n", opt->usage);
puts ("\n");
exit (1);
}
/*******************************************************************
* parse_options
*
* Parse options from the argv array
*/
static void parse_options (char *argv[])
{
const struct option *opt;
char *const *ptr;
const char *arg = NULL;
ptr = argv + 1;
while (*ptr != NULL)
{
for (opt = option_table; opt->name; opt++)
{
if (opt->has_arg && !strncmp (*ptr, opt->name, strlen (opt->name)))
{
arg = *ptr + strlen (opt->name);
if (*arg == '\0')
{
ptr++;
arg = *ptr;
}
break;
}
if (!strcmp (*ptr, opt->name))
{
arg = NULL;
break;
}
}
if (!opt->name)
fatal ("Unrecognized option");
if (opt->has_arg && arg != NULL)
opt->func (arg);
else
opt->func ("");
ptr++;
}
if (globals.do_code && !globals.directory)
fatal ("-I must be used if generating code");
if (!globals.input_name)
fatal ("Option -d is mandatory");
if (VERBOSE && QUIET)
fatal ("Options -v and -q are mutually exclusive");
}
/*******************************************************************
* main
*/
#ifdef __GNUC__
int main (int argc __attribute__((unused)), char *argv[])
#else
int main (int argc, char *argv[])
#endif
{
parsed_symbol symbol;
int count = 0;
parse_options (argv);
dll_open (globals.input_name);
output_spec_preamble ();
output_header_preamble ();
output_c_preamble ();
memset (&symbol, 0, sizeof (parsed_symbol));
while ((symbol.symbol = dll_next_symbol ()))
{
count++;
if (NORMAL)
printf ("Export %3d - '%s' ...%c", count, symbol.symbol,
VERBOSE ? '\n' : ' ');
if (globals.do_code && count >= globals.start_ordinal
&& (!globals.end_ordinal || count <= globals.end_ordinal))
{
/* Attempt to get information about the symbol */
int result = symbol_demangle (&symbol);
if (result)
result = symbol_search (&symbol);
if (!result)
/* Clean up the prototype */
symbol_clean_string (symbol.function_name);
if (NORMAL)
puts (result ? "[Not Found]" : "[OK]");
}
else if (NORMAL)
puts ("[Ignoring]");
output_spec_symbol (&symbol);
output_header_symbol (&symbol);
output_c_symbol (&symbol);
symbol_clear (&symbol);
}
output_makefile ();
output_install_script ();
if (VERBOSE)
puts ("Finished, Cleaning up...");
return 0;
}
/*
* Misc functions
*
* Copyright 2000 Jon Griffiths
*/
#include "specmaker.h"
/*******************************************************************
* str_create
*
* Create a single string from many substrings
*/
char *str_create(size_t num_str, ...)
{
va_list args;
size_t len = 1, i = 0;
char *tmp, *t;
va_start (args, num_str);
for (i = 0; i < num_str; i++)
if ((t = va_arg(args, char *)))
len += strlen (t);
va_end (args);
if (!(tmp = (char *) malloc (len)))
fatal ("Out of memory");
tmp[0] = '\0';
va_start (args, num_str);
for (i = 0; i < num_str; i++)
if ((t = va_arg(args, char *)))
strcat (tmp, t);
va_end (args);
return tmp;
}
/*******************************************************************
* str_create_num
*
* Create a single string from many substrings, terminating in a number
*/
char *str_create_num(size_t num_str, int num, ...)
{
va_list args;
size_t len = 8, i = 0;
char *tmp, *t;
va_start (args, num);
for (i = 0; i < num_str; i++)
if ((t = va_arg(args, char *)))
len += strlen (t);
va_end (args);
if (!(tmp = (char *) malloc (len)))
fatal ("Out of memory");
tmp[0] = '\0';
va_start (args, num);
for (i = 0; i < num_str; i++)
if ((t = va_arg(args, char *)))
strcat (tmp, t);
va_end (args);
sprintf (tmp + len - 8, "%d", num);
return tmp;
}
/*******************************************************************
* str_substring
*
* Create a new substring from a string
*/
char *str_substring(const char *start, const char *end)
{
char *newstr;
assert (start && end && end > start);
if (!(newstr = (char *) malloc (end - start + 1)))
fatal ("Out of memory");
memcpy (newstr, start, end - start);
newstr [end - start] = '\0';
return newstr;
}
/*******************************************************************
* str_replace
*
* Swap two strings in another string, in place
* Modified PD code from 'snippets'
*/
char *str_replace (char *str, const char *oldstr, const char *newstr)
{
int oldlen, newlen;
char *p, *q;
if (!(p = strstr(str, oldstr)))
return p;
oldlen = strlen (oldstr);
newlen = strlen (newstr);
memmove (q = p + newlen, p + oldlen, strlen (p + oldlen) + 1);
memcpy (p, newstr, newlen);
return q;
}
/*******************************************************************
* str_match
*
* Locate one string in another, ignoring spaces
*/
const char *str_match (const char *str, const char *match, int *found)
{
assert(str && match && found);
for (; *str == ' '; str++);
if (!strncmp (str, match, strlen (match)))
{
*found = 1;
str += strlen (match);
for (; *str == ' '; str++);
}
else
*found = 0;
return str;
}
/*******************************************************************
* str_find_set
*
* Locate the first occurence of a set of characters in a string
*/
const char *str_find_set (const char *str, const char *findset)
{
assert(str && findset);
while (*str)
{
const char *p = findset;
while (*p)
if (*p++ == *str)
return str;
str++;
}
return NULL;
}
/*******************************************************************
* str_toupper
*
* Uppercase a string
*/
char *str_toupper (char *str)
{
char *save = str;
while (*str)
{
*str = toupper (*str);
str++;
}
return save;
}
/*******************************************************************
* open_file
*
* Open a file returning only on success
*/
FILE *open_file (const char *name, const char *ext, const char *mode)
{
char fname[128];
FILE *fp;
if (((unsigned)snprintf (fname, sizeof (fname), "%s%s%s",
*mode == 'w' ? "./" : "", name, ext) > sizeof (fname)))
fatal ("File name too long");
if (VERBOSE)
printf ("Open file %s\n", fname);
fp = fopen (fname, mode);
if (!fp)
fatal ("Cant open file");
return fp;
}
/*******************************************************************
* fatal
*
* Fatal error handling
*/
void fatal (const char *message)
{
if (errno)
perror (message);
else
puts (message);
do_usage ();
}
/*
* Prototype search and parsing functions
*
* Copyright 2000 Jon Griffiths
*/
#include "specmaker.h"
static char *grep_buff = NULL;
static char *fgrep_buff = NULL;
static int symbol_from_prototype (parsed_symbol *sym, const char *prototype);
static const char *get_type (parsed_symbol *sym, const char *proto, int arg);
/*******************************************************************
* symbol_search
*
* Call Patrik Stridvall's 'function_grep.pl' script to retrieve a
* function prototype from include file(s)
*/
int symbol_search (parsed_symbol *sym)
{
static const size_t MAX_RESULT_LEN = 1024;
FILE *grep;
int attempt = 0;
assert (globals.do_code);
assert (globals.directory);
assert (sym && sym->symbol);
if (!symbol_is_valid_c (sym))
return - 1;
if (!grep_buff)
grep_buff = (char *) malloc (MAX_RESULT_LEN);
if (!fgrep_buff)
fgrep_buff = (char *) malloc (MAX_RESULT_LEN);
if (!grep_buff || !fgrep_buff)
fatal ("Out of Memory");
/* Use 'grep' to tell us which possible files the function is in,
* then use 'function_grep.pl' to get the prototype. If this fails the
* first time then give grep a more general query (that doesn't
* require an opening argument brace on the line with the function name).
*/
while (attempt < 2)
{
FILE *f_grep;
char *cmd = str_create (4, "grep -d recurse -l \"", sym->symbol,
!attempt ? "[:blank:]*(\" " : "\" ", globals.directory);
if (VERBOSE)
puts (cmd);
fflush (NULL); /* See 'man popen' */
if (!(grep = popen (cmd, "r")))
fatal ("Cannot execute grep -l");
free (cmd);
while (fgets (grep_buff, MAX_RESULT_LEN, grep))
{
int i;
for (i = 0; grep_buff[i] && grep_buff[i] != '\n' ; i++)
;
grep_buff[i] = '\0';
if (VERBOSE)
puts (grep_buff);
cmd = str_create (5, "function_grep.pl ", sym->symbol,
" \"", grep_buff, "\"");
if (VERBOSE)
puts (cmd);
fflush (NULL); /* See 'man popen' */
if (!(f_grep = popen (cmd, "r")))
fatal ("Cannot execute function_grep.pl");
free (cmd);
while (fgets (grep_buff, MAX_RESULT_LEN, f_grep))
{
char *iter = grep_buff;
/* Keep only the first line */
symbol_clean_string(grep_buff);
for (i = 0; grep_buff[i] && grep_buff[i] != '\n' ; i++)
;
grep_buff[i] = '\0';
if (VERBOSE)
puts (grep_buff);
while ((iter = strstr (iter, sym->symbol)))
{
if (iter > grep_buff && iter[-1] == ' ' &&
(iter[strlen (sym->symbol)] == ' ' ||
iter[strlen (sym->symbol)] == '('))
{
if (VERBOSE)
puts ("Prototype looks OK, processing");
if (!symbol_from_prototype (sym, grep_buff))
{
pclose (f_grep);
pclose (grep);
return 0; /* OK */
}
if (VERBOSE)
puts ("Failed, trying next");
}
else
iter += strlen (sym->symbol);
}
}
pclose (f_grep);
}
pclose (grep);
attempt++;
}
return -1; /* Not found */
}
/*******************************************************************
* symbol_from_prototype
*
* Convert a C prototype into a symbol
*/
static int symbol_from_prototype (parsed_symbol *sym, const char *proto)
{
char *iter;
int found;
proto = get_type (sym, proto, -1); /* Get return type */
if (!proto)
return -1;
iter = (char *)str_match (proto, sym->symbol, &found);
if (!found)
{
/* Calling Convention */
iter = strchr (iter, ' ');
if (!iter)
return -1;
sym->calling_convention = str_substring (proto, iter);
iter = (char *)str_match (iter, sym->symbol, &found);
if (!found)
return -1;
}
else
sym->calling_convention = strdup (CALLING_CONVENTION);
sym->function_name = strdup (sym->symbol);
proto = iter;
/* Now should be the arguments */
if (*proto++ != '(')
return -1;
for (; *proto == ' '; proto++);
if (!strncmp (proto, "void", 4))
return 0;
do
{
/* Process next argument */
str_match (proto, "...", &sym->varargs);
if (sym->varargs)
return 0;
if (!(proto = get_type (sym, proto, sym->argc)))
return -1;
sym->argc++;
if (*proto == ',')
proto++;
else if (*proto != ')')
return -1;
} while (*proto != ')');
return 0;
}
/*******************************************************************
* get_type
*
* Read a type from a prototype
*/
static const char *get_type (parsed_symbol *sym, const char *proto, int arg)
{
int is_const, is_volatile, is_struct, is_signed, is_unsigned, ptrs = 0;
char *iter, *type_str, *base_type, *catch_unsigned, dest_type;
assert (sym && sym->symbol);
assert (proto && *proto);
assert (arg < 0 || (unsigned)arg == sym->argc);
type_str = (char *)proto;
proto = str_match (proto, "const", &is_const);
proto = str_match (proto, "volatile", &is_volatile);
proto = str_match (proto, "struct", &is_struct);
if (!is_struct)
proto = str_match (proto, "union", &is_struct);
catch_unsigned = (char *)proto;
proto = str_match (proto, "unsigned", &is_unsigned);
proto = str_match (proto, "signed", &is_signed);
/* Can have 'unsigned const' or 'const unsigned' etc */
if (!is_const)
proto = str_match (proto, "const", &is_const);
if (!is_volatile)
proto = str_match (proto, "volatile", &is_volatile);
base_type = (char *)proto;
iter = (char *)str_find_set (proto, " ,*)");
if (!iter)
return NULL;
if (arg < 0 && (is_signed || is_unsigned))
{
/* Prevent calling convention from being swallowed by 'un/signed' alone */
if (strncmp (base_type, "int", 3) && strncmp (base_type, "long", 4) &&
strncmp (base_type, "short", 5) && strncmp (base_type, "char", 4))
{
iter = (char *)proto;
base_type = catch_unsigned;
}
}
else
catch_unsigned = NULL;
/* FIXME: skip const/volatile here too */
for (proto = iter; *proto; proto++)
if (*proto == '*')
ptrs++;
else if (*proto != ' ')
break;
if (!*proto)
return NULL;
type_str = str_substring (type_str, proto);
if (iter == base_type || catch_unsigned)
{
/* 'unsigned' with no type */
char *tmp = str_create (2, type_str, " int");
free (type_str);
type_str = tmp;
}
symbol_clean_string (type_str);
dest_type = symbol_get_type (type_str);
if (arg < 0)
{
sym->return_text = type_str;
sym->return_type = dest_type;
}
else
{
sym->arg_type [arg] = dest_type;
sym->arg_flag [arg] = is_const ? CT_CONST : is_volatile ? CT_VOLATILE : 0;
if (*proto == ',' || *proto == ')')
sym->arg_name [arg] = str_create_num (1, arg, "arg");
else
{
iter = (char *)str_find_set (proto, " ,)");
if (!iter)
{
free (type_str);
return NULL;
}
sym->arg_name [arg] = str_substring (proto, iter);
proto = iter;
}
sym->arg_text [arg] = type_str;
}
return proto;
}
#ifdef __GNUC__
/*******************************************************************
* search_cleanup
*
* Free memory used while searching (a niceity)
*/
void search_cleanup (void) __attribute__ ((destructor));
void search_cleanup (void)
{
if (grep_buff)
free (grep_buff);
if (fgrep_buff)
free (fgrep_buff);
}
#endif
/*
* Specmaker - A Wine DLL tool
*
* Copyright 2000 Jon Griffiths
*
* References:
* DLL symbol extraction based on file format from alib (anthonyw.cjb.net).
*
* Option processing shamelessly cadged from winebuild (www.winehq.com).
*
* All the cool functionality (prototyping, call tracing, forwarding)
* relies on Patrik Stridvall's 'function_grep.pl' script to work.
*
* http://msdn.microsoft.com/library/periodic/period96/msj/S330.htm
* This article provides both a description and freely downloadble
* implementation, in source code form, of how to extract symbols
* from Win32 PE executables/DLL's.
*
* http://www.kegel.com/mangle.html
* Gives information on the name mangling scheme used by MS compilers,
* used as the starting point for the code here. Contains a few
* mistakes and some incorrect assumptions, but the lists of types
* are pure gold.
*/
#ifndef __WINE_SPECMAKER_H
#define __WINE_SPECMAKER_H
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <assert.h>
#include <stdarg.h>
/* Argument type constants */
#define MAX_FUNCTION_ARGS 32
#define ARG_VOID 0x0
#define ARG_STRING 0x1
#define ARG_WIDE_STRING 0x2
#define ARG_POINTER 0x3
#define ARG_LONG 0x4
#define ARG_DOUBLE 0x5
#define ARG_STRUCT 0x6 /* By value */
#define ARG_FLOAT 0x7
#define ARG_VARARGS 0x8
/* Compound type flags */
#define CT_BY_REFERENCE 0x1
#define CT_VOLATILE 0x2
#define CT_CONST 0x4
/* Structure holding a parsed symbol */
typedef struct __parsed_symbol
{
char *symbol;
char *return_text;
char return_type;
char *calling_convention;
char *function_name;
unsigned int varargs;
unsigned int argc;
char arg_type [MAX_FUNCTION_ARGS];
char arg_flag [MAX_FUNCTION_ARGS];
char *arg_text [MAX_FUNCTION_ARGS];
char *arg_name [MAX_FUNCTION_ARGS];
} parsed_symbol;
/* All globals */
typedef struct __globals
{
/* Options */
int do_code; /* -c, -t, -f */
int do_trace; /* -t, -f */
int do_cdecl; /* -C */
int do_quiet; /* -q */
int do_verbose; /* -v */
int do_documentation; /* -D */
/* Option arguments */
int start_ordinal; /* -s */
int end_ordinal; /* -e */
const char *directory; /* -I */
const char *input_name; /* -d */
const char *forward_dll; /* -f */
const char *dll_name; /* -o */
char *uc_dll_name; /* -o */
} _globals;
extern _globals globals;
/* Names to use for output DLL */
#define OUTPUT_DLL_NAME \
(globals.dll_name ? globals.dll_name : globals.input_name)
#define OUTPUT_UC_DLL_NAME globals.uc_dll_name
/* Verbosity levels */
#define QUIET (globals.do_quiet)
#define NORMAL (!QUIET)
#define VERBOSE (globals.do_verbose)
/* Default calling convention */
#define CALLING_CONVENTION (globals.do_cdecl ? "__cdecl" : "__stdcall")
/* DLL functions */
void dll_open (const char *dll_name);
char *dll_next_symbol (void);
/* Symbol functions */
int symbol_demangle (parsed_symbol *symbol);
int symbol_search (parsed_symbol *symbol);
void symbol_clear(parsed_symbol *sym);
int symbol_is_valid_c(const parsed_symbol *sym);
int symbol_is_cdecl(const parsed_symbol *sym);
const char *symbol_get_spec_type (const parsed_symbol *sym, size_t arg);
void symbol_clean_string (const char *string);
int symbol_get_type (const char *string);
/* Output functions */
void output_spec_preamble (void);
void output_spec_symbol (const parsed_symbol *sym);
void output_header_preamble (void);
void output_header_symbol (const parsed_symbol *sym);
void output_c_preamble (void);
void output_c_symbol (const parsed_symbol *sym);
void output_makefile (void);
void output_install_script (void);
/* Misc functions */
char *str_create (size_t num_str, ...);
char *str_create_num (size_t num_str, int num, ...);
char *str_substring(const char *start, const char *end);
char *str_replace (char *str, const char *oldstr, const char *newstr);
const char *str_match (const char *str, const char *match, int *found);
const char *str_find_set (const char *str, const char *findset);
char *str_toupper (char *str);
FILE *open_file (const char *name, const char *ext, const char *mode);
#ifdef __GNUC__
void do_usage (void) __attribute__ ((noreturn));
#else
void do_usage (void);
#endif
void fatal (const char *message);
#endif /* __WINE_SPECMAKER_H */
/*
* Symbol functions
*
* Copyright 2000 Jon Griffiths
*/
#include "specmaker.h"
/* Items that are swapped in arguments after the symbol structure
* has been populated
*/
static const char *swap_after[] =
{
"\r", " ", /* Remove whitespace, normalise pointers and brackets */
"\t", " ",
" ", " ",
" * ", " *",
"* *", "**",
"* ", "*",
" ,", ",",
"( ", "(",
" )", ")",
"wchar_t", "WCHAR", /* Help with Unicode compliles */
"wctype_t", "WCHAR",
"wint_t", "WCHAR",
"unsigned __int64", "__uint64", /* Wine doesn't cope with unsigned i64's */
NULL, NULL
};
/* Items containing these substrings are assumed to be wide character
* strings, unless they contain more that one '*'. A preceeding 'LP'
* counts as a '*', so 'LPWCSTR *' is a pointer, not a string
*/
static const char *wide_strings[] =
{
"WSTR", "WCSTR", NULL
};
/* Items containing these substrings are assumed to be wide characters,
* unless they contain one '*'. A preceeding 'LP' counts as a '*',
* so 'WCHAR *' is string, while 'LPWCHAR *' is a pointer
*/
static const char *wide_chars[] =
{
"WCHAR", NULL
};
/* Items containing these substrings are assumed to be ASCII character
* strings, as above
*/
static const char *ascii_strings[] =
{
"STR", "CSTR", NULL
};
/* Items containing these substrings are assumed to be ASCII characters,
* as above
*/
static const char *ascii_chars[] =
{
"CHAR", "char", NULL
};
/* Any type other than the following will produce a FIXME warning with -v
* when mapped to a long, to allow fixups
*/
static const char *known_longs[] =
{
"char", "CHAR", "float", "int", "INT", "short", "SHORT", "long", "LONG",
"WCHAR", "BOOL", "bool", "INT16", NULL
};
/*******************************************************************
* symbol_clear
*
* Free the memory used by a symbol and initialise it
*/
void symbol_clear(parsed_symbol *sym)
{
int i;
assert (sym);
assert (sym->symbol);
free (sym->symbol);
if (sym->return_text)
free (sym->return_text);
if (sym->calling_convention)
free (sym->calling_convention);
if (sym->function_name)
free (sym->function_name);
for (i = sym->argc - 1; i >= 0; i--)
{
if (sym->arg_text [i])
free (sym->arg_text [i]);
if (sym->arg_name [i])
free (sym->arg_name [i]);
}
memset (sym, 0, sizeof (parsed_symbol));
}
/*******************************************************************
* symbol_is_valid_c
*
* Check if a symbol is a valid C identifier
*/
int symbol_is_valid_c(const parsed_symbol *sym)
{
char *name;
assert (sym);
assert (sym->symbol);
name = sym->symbol;
while (*name)
{
if (!isalnum (*name) && *name != '_')
return 0;
name++;
}
return 1;
}
/*******************************************************************
* symbol_is_cdecl
*
* Check if a symbol is cdecl
*/
int symbol_is_cdecl(const parsed_symbol *sym)
{
assert (sym);
assert (sym->symbol);
if (sym->calling_convention && (strstr (sym->calling_convention, "cdecl")
|| strstr (sym->calling_convention, "CDECL")))
return 1;
else if (!sym->calling_convention)
return globals.do_cdecl;
return 0;
}
/*******************************************************************
* symbol_get_spec_type
*
* Get the .spec file text for a symbols argument
*/
const char *symbol_get_spec_type (const parsed_symbol *sym, size_t arg)
{
assert (arg < sym->argc);
switch (sym->arg_type [arg])
{
case ARG_STRING: return "str";
case ARG_WIDE_STRING: return "wstr";
case ARG_POINTER: return "ptr";
case ARG_DOUBLE: return "double";
case ARG_STRUCT:
case ARG_FLOAT:
case ARG_LONG: return "long";
}
assert (0);
return NULL;
}
/*******************************************************************
* symbol_get_type
*
* Get the ARG_ constant for a type string
*/
int symbol_get_type (const char *string)
{
const char *iter = string;
const char **tab;
int ptrs = 0;
while (*iter)
{
if (*iter == '*' || (*iter == 'L' && iter[1] == 'P')
|| (*iter == '[' && iter[1] == ']'))
ptrs++;
if (ptrs > 1)
return ARG_POINTER;
iter++;
}
/* 0 or 1 pointer */
tab = wide_strings;
while (*tab++)
if (strstr (string, tab[-1]))
{
if (!ptrs) return ARG_WIDE_STRING;
else return ARG_POINTER;
}
tab = wide_chars;
while (*tab++)
if (strstr (string, tab[-1]))
{
if (!ptrs) return ARG_LONG;
else return ARG_WIDE_STRING;
}
tab = ascii_strings;
while (*tab++)
if (strstr (string, tab[-1]))
{
if (!ptrs) return ARG_STRING;
else return ARG_POINTER;
}
tab = ascii_chars;
while (*tab++)
if (strstr (string, tab[-1]))
{
if (!ptrs) return ARG_LONG;
else {
if (!strstr (string, "unsigned")) /* unsigned char * => ptr */
return ARG_STRING;
}
}
if (ptrs)
return ARG_POINTER; /* Pointer to some other type */
/* No pointers */
if (strstr (string, "double"))
return ARG_DOUBLE;
if (strstr (string, "void"))
return ARG_VOID;
if (strstr (string, "struct") || strstr (string, "union"))
return ARG_STRUCT; /* Struct by value, ugh */
if (VERBOSE)
{
int known = 0;
tab = known_longs;
while (*tab++)
if (strstr (string, tab[-1]))
{
known = 1;
break;
}
/* Unknown types passed by value can be 'grep'ed out for fixup later */
if (!known)
printf ("/* FIXME: By value type: Assumed 'int' */ typedef int %s;\n",
string);
}
return ARG_LONG;
}
/*******************************************************************
* symbol_clean_string
*
* Make a type string more Wine-friendly. Logically const :-)
*/
void symbol_clean_string (const char *string)
{
const char **tab = swap_after;
char *str = (char *)string;
#define SWAP(i, p, x, y) do { i = p; while ((i = str_replace (i, x, y))); } while(0)
while (tab [0])
{
char *p;
SWAP (p, str, tab [0], tab [1]);
tab += 2;
}
if (str [strlen (str) - 1] == ' ')
str [strlen (str) - 1] = '\0'; /* no trailing space */
if (*str == ' ')
memmove (str, str + 1, strlen (str)); /* No leading spaces */
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment