#! /usr/bin/perl -w
#
# Build the server/trace.c and server/request.h files
# from the contents of include/server.h.
#
# Copyright (C) 1998 Alexandre Julliard
#

%formats =
(
    "int"          => "%d",
    "long"         => "%ld",
    "char"         => "%c",
    "unsigned int" => "%08x",
    "void*"        => "%p",
    "time_t"       => "%ld",
    "path_t"       => "&dump_unicode_string",
    "char[1]"      => "\\\"%s\\\"",
    "WCHAR[1]"     => "&dump_unicode_string"
);

my @requests = ();
my %replies = ();

open(SERVER,"include/server.h") or die "Can't open include/server.h";

### Parse server.h to find request/reply structure definitions

my @trace_lines = ();

while (<SERVER>)
{
    if (/^struct +(\w+)_request/) { &DO_REQUEST($1); }
}

### Output the dumping function tables

push @trace_lines, "static const dump_func req_dumpers[REQ_NB_REQUESTS] = {\n";
foreach $req (@requests)
{
    push @trace_lines, "    (dump_func)dump_${req}_request,\n";
}
push @trace_lines, "};\n\n";

push @trace_lines, "static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {\n";
foreach $req (@requests)
{
    push @trace_lines, "    (dump_func)", $replies{$req} ? "dump_${req}_reply,\n" : "0,\n";
}
push @trace_lines, "};\n\n";

push @trace_lines, "static const char * const req_names[REQ_NB_REQUESTS] = {\n";
foreach $req (@requests)
{
    push @trace_lines, "    \"$req\",\n";
}
push @trace_lines, "};\n";

REPLACE_IN_FILE( "server/trace.c", @trace_lines );

### Replace the request list in server.h by the new values

my @server_lines = ();

push @server_lines, "enum request\n{\n";
foreach $req (@requests) { push @server_lines, "    REQ_\U$req,\n"; }
push @server_lines, "    REQ_NB_REQUESTS\n};\n";

REPLACE_IN_FILE( "include/server.h", @server_lines );

### Output the request handlers list

my @request_lines = ();

foreach $req (@requests) { push @request_lines, "DECL_HANDLER($req);\n"; }
push @request_lines, "\n#ifdef WANT_REQUEST_HANDLERS\n\n";
push @request_lines, "static const struct handler {\n";
push @request_lines, "    void       (*handler)( void *req, int fd );\n";
push @request_lines, "    unsigned int min_size;\n";
push @request_lines, "} req_handlers[REQ_NB_REQUESTS] = {\n";
foreach $req (@requests)
{
    push @request_lines, "    { (void(*)())req_$req, sizeof(struct ${req}_request) },\n";
}
push @request_lines, "};\n#endif  /* WANT_REQUEST_HANDLERS */\n";

REPLACE_IN_FILE( "server/request.h", @request_lines );

### Handle a request structure definition

sub DO_REQUEST
{
    my $name = shift;
    my @in_struct = ();
    my @out_struct = ();
    while (<SERVER>)
    {
	my ($dir, $type, $var);
	last if /^};$/;
        next if /^{$/;
	s!/\*.*\*/!!g;
	next if /^\s*$/;
	/^\s*(IN|OUT)\s*(\w+\**(\s+\w+\**)*)\s+(\w+)(\[[1]\])?;/ or die "Unrecognized syntax $_";
	$dir = $1;
	$type = $2 . ($5 || "");
	$var = $4;
	die "Unrecognized type $type" unless (defined($formats{$type}) || $5);
	if ($dir =~ /IN/) { push @in_struct, $type, $var; }
	if ($dir =~ /OUT/) { push @out_struct, $type, $var; }
    }
    push @requests, $name;
    &DO_DUMP_FUNC( $name, "request", @in_struct);
    if ($#out_struct >= 0)
    {
	$replies{$name} = 1;
	&DO_DUMP_FUNC( $name, "reply", @out_struct);
    }
}

### Generate a dumping function

sub DO_DUMP_FUNC
{
    my $name = shift;
    my $req = shift;
    push @trace_lines, "static void dump_${name}_$req( struct ${name}_request *req )\n{\n";
    while ($#_ >= 0)
    {
	my $type = shift;
	my $var = shift;
	if (defined($formats{$type}))
	{
            if ($formats{$type} =~ /^&(.*)/)
            {
                my $func = $1;
                push @trace_lines, "    fprintf( stderr, \" $var=\" );\n";
                push @trace_lines, "    $func( req->$var );\n";
                push @trace_lines, "    fprintf( stderr, \",\" );\n" if ($#_ > 0);
            }
            else
            {
                push @trace_lines, "    fprintf( stderr, \" $var=$formats{$type}";
                push @trace_lines, "," if ($#_ > 0);
                push @trace_lines, "\", ";
                push @trace_lines, "req->$var );\n";
            }
	}
	else  # must be some varargs format
	{
	    push @trace_lines, "    fprintf( stderr, \" $var=\" );\n";
	    push @trace_lines, "    dump_varargs_${name}_${req}( req );\n";
        }
    }
    push @trace_lines, "}\n\n";
}

### Replace the contents of a file between ### make_requests ### marks

sub REPLACE_IN_FILE
{
    my $name = shift;
    my @data = @_;
    my @lines = ();
    open(FILE,$name) or die "Can't open $name";
    while (<FILE>)
    {
	push @lines, $_;
	last if /\#\#\# make_requests begin \#\#\#/;
    }
    push @lines, "\n", @data;
    while (<FILE>)
    {
	if (/\#\#\# make_requests end \#\#\#/) { push @lines, "\n", $_; last; }
    }
    push @lines, <FILE>;
    open(FILE,">$name") or die "Can't modify $name";
    print FILE @lines;
    close(FILE);
}
