#!/usr/bin/perl
# Generate loadparm tables for loadparm.c
# by Andrew Bartlett
# based on mkproto.pl Written by Jelmer Vernooij
# based on the original mkproto.sh by Andrew Tridgell

use strict;

# don't use warnings module as it is not portable enough
# use warnings;

use Getopt::Long;
use File::Basename;
use File::Path;

#####################################################################
# read a file into a string

my $file = undef;
my $public_define = undef;
my $_public = "";
my $_private = "";
my $public_data = \$_public;
my $builddir = ".";
my $srcdir = ".";
my $generate_scope = undef;

sub public($)
{
	my ($d) = @_;
	$$public_data .= $d;
}

sub usage()
{
	print "Usage: mkparamdefs.pl [options] [c files]\n";
	print "OPTIONS:\n";
	print "  --srcdir=path          Read files relative to this directory\n";
	print "  --builddir=path        Write file relative to this directory\n";
	print "  --generate-scope=[GLOBAL|LOCAL]        Filter which definitions to generate\n";
	print "  --help                 Print this help message\n\n";
	exit 0;
}

GetOptions(
	'file=s' => sub { my ($f,$v) = @_; $file = $v; },
	'srcdir=s' => sub { my ($f,$v) = @_; $srcdir = $v; },
	'builddir=s' => sub { my ($f,$v) = @_; $builddir = $v; },
	'generate-scope=s' => sub { my ($f,$v) = @_; $generate_scope = $v; },
	'help' => \&usage
) or exit(1);

sub normalize_define($$)
{
	my ($define, $file) = @_;

	if (not defined($define) and defined($file)) {
		$define = "__" . uc($file) . "__";
		$define =~ tr{./}{__};
		$define =~ tr{\-}{_};
	} elsif (not defined($define)) {
		$define = '_S3_PARAM_H_';
	}

	return $define;
}

$public_define = normalize_define($public_define, $file);

sub file_load($)
{
    my($filename) = @_;
    local(*INPUTFILE);
    open(INPUTFILE, $filename) or return undef;
    my($saved_delim) = $/;
    undef $/;
    my($data) = <INPUTFILE>;
    close(INPUTFILE);
    $/ = $saved_delim;
    return $data;
}

sub print_header($$$)
{
	my ($file, $header_name,$generate_scope) = @_;
	$file->("#ifndef $header_name\n");
	$file->("#define $header_name\n\n");
$file->("/* This file was automatically generated by mkparamdefs.pl. DO NOT EDIT */\n\n");
	$file->("/**\n");
	if ($generate_scope eq "GLOBAL") {
	    $file->(" * This structure describes global (ie., server-wide) parameters.\n");
	    $file->(" */\n");
	    $file->("struct loadparm_global \n");
	    $file->("{\n");
	    $file->("\tTALLOC_CTX *ctx; /* Context for talloced members */\n");
	} elsif ($generate_scope eq "LOCAL") {
	    $file->(" * This structure describes a single service.\n");
	    $file->(" */\n");
	    $file->("struct loadparm_service \n");
	    $file->("{\n");
	}
}

sub print_footer($$$)
{
	my ($file, $header_name, $generate_scope) = @_;
	$file->("LOADPARM_EXTRA_" . $generate_scope . "S\n");
	$file->("};\n");
	$file->("\n#endif /* $header_name */\n\n");
}

sub handle_loadparm($$$)
{
	my ($file,$line,$generate_scope) = @_;

	my $scope;
	my $type;
	my $name;
	my $var;

	if ($line =~ /^FN_(GLOBAL|LOCAL)_(CONST_STRING|STRING|BOOL|bool|CHAR|INTEGER|LIST)\((\w+),(.*)\)/o) {
		$scope = $1;
		$type = $2;
		$name = $3;
		$var = $4;
	} elsif ($line =~ /^FN_(GLOBAL|LOCAL)_PARM_(CONST_STRING|STRING|BOOL|bool|CHAR|INTEGER|LIST)\((\w+),(.*)\)/o) {
		$scope = $1;
		$type = $2;
		$name = $3;
		$var = $4;
	} else {
	        return;
	}
	my %tmap = (
	    "BOOL" => "bool ",
	    "CONST_STRING" => "char *",
	    "STRING" => "char *",
	    "INTEGER" => "int ",
	    "CHAR" => "char ",
	    "LIST" => "const char **",
	    );

	if ($scope eq $generate_scope) {
	    $file->("\t$tmap{$type} $var;\n");
	}
}

sub process_file($$)
{
	my ($file, $filename) = @_;

	$filename =~ s/\.o$/\.c/g;

	if ($filename =~ /^\//) {
		open(FH, "<$filename") or die("Failed to open $filename");
	} elsif (!open(FH, "< $builddir/$filename")) {
	    open(FH, "< $srcdir/$filename") || die "Failed to open $filename";
	}

	my $comment = undef;
	my $incomment = 0;
	while (my $line = <FH>) {
		if ($line =~ /^\/\*\*/) {
			$comment = "";
			$incomment = 1;
		}

		if ($incomment) {
			$comment .= $line;
			if ($line =~ /\*\//) {
				$incomment = 0;
			}
		}

		# these are ordered for maximum speed
		next if ($line =~ /^\s/);

		next unless ($line =~ /\(/);

		next if ($line =~ /^\/|[;]/);

		if ($line =~ /^static (FN_.*)/) {
			handle_loadparm($file, $1, $generate_scope);
		} elsif ($line =~ /^FN_/) {
			handle_loadparm($file, $line, $generate_scope);
		}
		next;
	}

	close(FH);
}


print_header(\&public, $public_define, $generate_scope);

process_file(\&public, $_) foreach (@ARGV);
print_footer(\&public, $public_define, $generate_scope);

if (not defined($file)) {
	print STDOUT $$public_data;
}

mkpath(dirname($file), 0, 0755);
open(PUBLIC, ">$file") or die("Can't open `$file': $!");
print PUBLIC "$$public_data";
close(PUBLIC);
