1
0
mirror of https://github.com/samba-team/samba.git synced 2025-12-17 04:23:50 +03:00
Files
samba-mirror/source/pidl/lib/Parse/Pidl/Samba3/Parser.pm

604 lines
13 KiB
Perl

###################################################
# Samba3 NDR parser generator for IDL structures
# Copyright jelmer@samba.org 2005
# released under the GNU GPL
package Parse::Pidl::Samba3::Parser;
use strict;
use Parse::Pidl::Typelist qw(hasType getType mapType);
use Parse::Pidl::Util qw(has_property ParseExpr);
use Parse::Pidl::NDR qw(GetPrevLevel GetNextLevel ContainsDeferred);
use Parse::Pidl::Samba3::Types qw(DeclShort DeclLong InitType DissectType StringType);
use vars qw($VERSION);
$VERSION = '0.01';
use constant PRIMITIVES => 1;
use constant DEFERRED => 2;
my $res = "";
my $tabs = "";
sub indent() { $tabs.="\t"; }
sub deindent() { $tabs = substr($tabs, 1); }
sub pidl($) { $res .= $tabs.(shift)."\n"; }
sub fatal($$) { my ($e,$s) = @_; die("$e->{ORIGINAL}->{FILE}:$e->{ORIGINAL}->{LINE}: $s\n"); }
#TODO:
# - Add some security checks (array sizes, memory alloc == NULL, etc)
# - Don't add seperate _p and _d functions if there is no deferred data
# - [string] with non-varying arrays and "surrounding" strings
# - subcontext()
# - DATA_BLOB
sub Align($$)
{
my ($a,$b) = @_;
# Only align if previous element was smaller than current one
if ($$a < $b) {
pidl "if (!prs_align_custom(ps, $b))";
pidl "\treturn False;";
pidl "";
}
$$a = $b;
}
sub DeclareArrayVariables
{
my $es = shift;
my $what = shift;
my $output = 0;
foreach my $e (@$es) {
foreach my $l (@{$e->{LEVELS}}) {
if ($what) {
next if ($l->{IS_DEFERRED} and $what == PRIMITIVES);
next if (not $l->{IS_DEFERRED} and $what == DEFERRED);
}
if ($l->{TYPE} eq "ARRAY" and not $l->{IS_ZERO_TERMINATED}) {
pidl "uint32 i_$e->{NAME}_$l->{LEVEL_INDEX};";
$output = 1;
}
}
}
pidl "" if $output;
}
sub ParseElementLevelData($$$$$$$)
{
my ($e,$l,$nl,$env,$varname,$what,$align) = @_;
my $c = DissectType($e,$l,$varname,$what,$align);
return if not $c;
if (defined($e->{ALIGN})) {
Align($align, $e->{ALIGN});
} else {
# Default to 4
Align($align, 4);
}
pidl "if (!$c)";
pidl "\treturn False;";
}
sub ParseElementLevelArray($$$$$$$)
{
my ($e,$l,$nl,$env,$varname,$what,$align) = @_;
if ($l->{IS_ZERO_TERMINATED}) {
return if ($what == DEFERRED);
my ($t,$f) = StringType($e,$l);
Align($align, 4);
pidl "if (!smb_io_$t(\"$e->{NAME}\", &$varname, 1, ps, depth))";
pidl "\treturn False;";
$$align = 0;
return;
}
my $len = ParseExpr($l->{LENGTH_IS}, $env);
my $size = ParseExpr($l->{SIZE_IS}, $env);
if ($what == PRIMITIVES) {
# Fetch headers
if ($l->{IS_CONFORMANT} and not $l->{IS_SURROUNDING}) {
Align($align, 4);
pidl "if (!prs_uint32(\"size_$e->{NAME}\", ps, depth, &" . ParseExpr("size_$e->{NAME}", $env) . "))";
pidl "\treturn False;";
pidl "";
}
if ($l->{IS_VARYING}) {
Align($align, 4);
pidl "if (!prs_uint32(\"offset_$e->{NAME}\", ps, depth, &" . ParseExpr("offset_$e->{NAME}", $env) . "))";
pidl "\treturn False;";
pidl "";
pidl "if (!prs_uint32(\"length_$e->{NAME}\", ps, depth, &" . ParseExpr("length_$e->{NAME}", $env) . "))";
pidl "\treturn False;";
pidl "";
}
}
# Everything but fixed arrays have to be allocated
if (!$l->{IS_FIXED} and $what == PRIMITIVES) {
pidl "if (UNMARSHALLING(ps)) {";
indent;
pidl "$varname = (void *)PRS_ALLOC_MEM_VOID(ps,sizeof(*$varname)*$size);";
deindent;
pidl "}";
}
return if ($what == DEFERRED and not ContainsDeferred($e,$l));
my $i = "i_$e->{NAME}_$l->{LEVEL_INDEX}";
pidl "for ($i=0; $i<$len;$i++) {";
indent;
ParseElementLevel($e,$nl,$env,$varname."[$i]",$what,$align);
deindent;
pidl "}";
}
sub ParseElementLevelSwitch($$$$$$$)
{
my ($e,$l,$nl,$env,$varname,$what,$align) = @_;
ParseElementLevel($e,$nl,$env,$varname,$what,$align);
}
sub ParseElementLevelPtr($$$$$$$)
{
my ($e,$l,$nl,$env,$varname,$what,$align) = @_;
if ($what == PRIMITIVES) {
if (($l->{POINTER_TYPE} eq "ref") and ($l->{LEVEL} eq "EMBEDDED")) {
# Ref pointers always have to be non-NULL
pidl "if (MARSHALLING(ps) && !" . ParseExpr("ptr$l->{POINTER_INDEX}_$e->{NAME}", $env) . ")";
pidl "\treturn False;";
pidl "";
}
unless ($l->{POINTER_TYPE} eq "ref" and $l->{LEVEL} eq "TOP") {
Align($align, 4);
pidl "if (!prs_uint32(\"ptr$l->{POINTER_INDEX}_$e->{NAME}\", ps, depth, &" . ParseExpr("ptr$l->{POINTER_INDEX}_$e->{NAME}", $env) . "))";
pidl "\treturn False;";
pidl "";
}
}
if ($l->{POINTER_TYPE} eq "relative") {
fatal($e, "relative pointers not supported for Samba 3");
#FIXME
}
if ($what == DEFERRED) {
if ($l->{POINTER_TYPE} ne "ref") {
pidl "if (" . ParseExpr("ptr$l->{POINTER_INDEX}_$e->{NAME}", $env) . ") {";
indent;
}
ParseElementLevel($e,$nl,$env,$varname,PRIMITIVES,$align);
ParseElementLevel($e,$nl,$env,$varname,DEFERRED,$align);
if ($l->{POINTER_TYPE} ne "ref") {
deindent;
pidl "}";
}
$$align = 0;
}
}
sub ParseElementLevelSubcontext($$$$$$$)
{
my ($e,$l,$nl,$env,$varname,$what,$align) = @_;
fatal($e, "subcontext() not supported for Samba 3");
#FIXME
}
sub ParseElementLevel($$$$$$)
{
my ($e,$l,$env,$varname,$what,$align) = @_;
{
DATA => \&ParseElementLevelData,
SUBCONTEXT => \&ParseElementLevelSubcontext,
POINTER => \&ParseElementLevelPtr,
SWITCH => \&ParseElementLevelSwitch,
ARRAY => \&ParseElementLevelArray
}->{$l->{TYPE}}->($e,$l,GetNextLevel($e,$l),$env,$varname,$what,$align);
}
sub ParseElement($$$$)
{
my ($e,$env,$what,$align) = @_;
ParseElementLevel($e, $e->{LEVELS}[0], $env, ParseExpr($e->{NAME}, $env), $what, $align);
}
sub InitLevel($$$$)
{
sub InitLevel($$$$);
my ($e,$l,$varname,$env) = @_;
if ($l->{TYPE} eq "POINTER") {
if ($l->{POINTER_TYPE} eq "ref") {
pidl "if (!$varname)";
pidl "\treturn False;";
pidl "";
} else {
pidl "if ($varname) {";
indent;
}
unless ($l->{POINTER_TYPE} eq "ref" and $l->{LEVEL} eq "TOP") {
pidl ParseExpr("ptr$l->{POINTER_INDEX}_$e->{NAME}", $env) . " = 1;";
}
InitLevel($e, GetNextLevel($e,$l), "*$varname", $env);
if ($l->{POINTER_TYPE} ne "ref") {
deindent;
pidl "} else {";
pidl "\t" . ParseExpr("ptr$l->{POINTER_INDEX}_$e->{NAME}", $env) . " = 0;";
pidl "}";
}
} elsif ($l->{TYPE} eq "ARRAY" and $l->{IS_ZERO_TERMINATED}) {
my ($t,$f) = StringType($e,$l);
pidl "init_$t(&" . ParseExpr($e->{NAME}, $env) . ", ".substr($varname, 1) . ", $f);";
} elsif ($l->{TYPE} eq "ARRAY") {
pidl ParseExpr($e->{NAME}, $env) . " = $varname;";
} elsif ($l->{TYPE} eq "DATA") {
pidl InitType($e, $l, ParseExpr($e->{NAME}, $env), $varname);
} elsif ($l->{TYPE} eq "SWITCH") {
InitLevel($e, GetNextLevel($e,$l), $varname, $env);
pidl ParseExpr($e->{NAME}, $env) . ".switch_value = " . ParseExpr($l->{SWITCH_IS}, $env) . ";";
}
}
sub GenerateEnvElement($$)
{
my ($e,$env) = @_;
foreach my $l (@{$e->{LEVELS}}) {
if ($l->{TYPE} eq "DATA") {
$env->{$e->{NAME}} = "v->$e->{NAME}";
} elsif ($l->{TYPE} eq "POINTER") {
$env->{"ptr$l->{POINTER_INDEX}_$e->{NAME}"} = "v->ptr$l->{POINTER_INDEX}_$e->{NAME}";
} elsif ($l->{TYPE} eq "SWITCH") {
} elsif ($l->{TYPE} eq "ARRAY" and not $l->{IS_ZERO_TERMINATED}) {
$env->{"length_$e->{NAME}"} = "v->length_$e->{NAME}";
$env->{"size_$e->{NAME}"} = "v->size_$e->{NAME}";
$env->{"offset_$e->{NAME}"} = "v->offset_$e->{NAME}";
}
}
}
sub ParseStruct($$$)
{
my ($if,$s,$n) = @_;
my $fn = "$if->{NAME}_io_$n";
my $sn = uc("$if->{NAME}_$n");
my $ifn = "init_$if->{NAME}_$n";
my $args = "";
foreach (@{$s->{ELEMENTS}}) {
$args .= ", " . DeclLong($_);
}
my $env = { "this" => "v" };
GenerateEnvElement($_, $env) foreach (@{$s->{ELEMENTS}});
pidl "BOOL $ifn($sn *v$args)";
pidl "{";
indent;
pidl "DEBUG(5,(\"$ifn\\n\"));";
pidl "";
# Call init for all arguments
foreach (@{$s->{ELEMENTS}}) {
InitLevel($_, $_->{LEVELS}[0], $_->{NAME}, $env);
pidl "";
}
pidl "return True;";
deindent;
pidl "}";
pidl "";
my $pfn = "$fn\_p";
my $dfn = "$fn\_d";
pidl "BOOL $pfn(const char *desc, $sn *v, prs_struct *ps, int depth)";
pidl "{";
indent;
DeclareArrayVariables($s->{ELEMENTS}, PRIMITIVES);
pidl "if (v == NULL)";
pidl "\treturn False;";
pidl "";
pidl "prs_debug(ps, depth, desc, \"$pfn\");";
pidl "depth++;";
my $align = 8;
if ($s->{SURROUNDING_ELEMENT}) {
pidl "if (!prs_uint32(\"size_$s->{SURROUNDING_ELEMENT}->{NAME}\", ps, depth, &" . ParseExpr("size_$s->{SURROUNDING_ELEMENT}->{NAME}", $env) . "))";
pidl "\treturn False;";
pidl "";
$align = 4;
}
foreach (@{$s->{ELEMENTS}}) {
ParseElement($_, $env, PRIMITIVES, \$align);
pidl "";
}
pidl "return True;";
deindent;
pidl "}";
pidl "";
pidl "BOOL $dfn(const char *desc, $sn *v, prs_struct *ps, int depth)";
pidl "{";
indent;
DeclareArrayVariables($s->{ELEMENTS}, DEFERRED);
pidl "if (v == NULL)";
pidl "\treturn False;";
pidl "";
pidl "prs_debug(ps, depth, desc, \"$dfn\");";
pidl "depth++;";
$align = 0;
foreach (@{$s->{ELEMENTS}}) {
ParseElement($_, $env, DEFERRED, \$align);
pidl "";
}
pidl "return True;";
deindent;
pidl "}";
pidl "";
}
sub UnionGenerateEnvElement($)
{
my $e = shift;
my $env = {};
foreach my $l (@{$e->{LEVELS}}) {
if ($l->{TYPE} eq "DATA") {
$env->{$e->{NAME}} = "v->u.$e->{NAME}";
} elsif ($l->{TYPE} eq "POINTER") {
$env->{"ptr$l->{POINTER_INDEX}_$e->{NAME}"} = "v->ptr$l->{POINTER_INDEX}";
} elsif ($l->{TYPE} eq "SWITCH") {
} elsif ($l->{TYPE} eq "ARRAY" and not $l->{IS_ZERO_TERMINATED}) {
$env->{"length_$e->{NAME}"} = "v->length";
$env->{"size_$e->{NAME}"} = "v->size";
$env->{"offset_$e->{NAME}"} = "v->offset";
}
}
return $env;
}
sub ParseUnion($$$)
{
my ($if,$u,$n) = @_;
my $fn = "$if->{NAME}_io_$n";
my $sn = uc("$if->{NAME}_$n\_ctr");
my $pfn = "$fn\_p";
my $dfn = "$fn\_d";
pidl "BOOL $pfn(const char *desc, $sn* v, prs_struct *ps, int depth)";
pidl "{";
indent;
DeclareArrayVariables($u->{ELEMENTS});
if (defined ($u->{SWITCH_TYPE})) {
pidl "if (!prs_$u->{SWITCH_TYPE}(\"switch_value\", ps, depth, &v->switch_value))";
pidl "\treturn False;";
pidl "";
}
# Maybe check here that level and v->switch_value are equal?
pidl "switch (v->switch_value) {";
indent;
foreach (@{$u->{ELEMENTS}}) {
pidl "$_->{CASE}:";
indent;
if ($_->{TYPE} ne "EMPTY") {
pidl "depth++;";
my $env = UnionGenerateEnvElement($_);
my $align = 8;
ParseElement($_, $env, PRIMITIVES, \$align);
pidl "depth--;";
}
pidl "break;";
deindent;
pidl "";
}
unless ($u->{HAS_DEFAULT}) {
pidl "default:";
pidl "\treturn False;";
pidl "";
}
deindent;
pidl "}";
pidl "";
pidl "return True;";
deindent;
pidl "}";
pidl "";
pidl "BOOL $dfn(const char *desc, $sn* v, prs_struct *ps, int depth)";
pidl "{";
indent;
DeclareArrayVariables($u->{ELEMENTS});
if (defined($u->{SWITCH_TYPE})) {
pidl "switch (v->switch_value) {";
} else {
pidl "switch (level) {";
}
indent;
foreach (@{$u->{ELEMENTS}}) {
pidl "$_->{CASE}:";
indent;
if ($_->{TYPE} ne "EMPTY") {
pidl "depth++;";
my $env = UnionGenerateEnvElement($_);
my $align = 0;
ParseElement($_, $env, DEFERRED, \$align);
pidl "depth--;";
}
pidl "break;";
deindent;
pidl "";
}
deindent;
pidl "}";
pidl "";
pidl "return True;";
deindent;
pidl "}";
}
sub CreateFnDirection($$$$$)
{
my ($fn,$ifn,$s,$all,$es) = @_;
my $args = "";
foreach (@$all) { $args .= ", " . DeclLong($_); }
my $env = { };
GenerateEnvElement($_, $env) foreach (@$es);
pidl "BOOL $ifn($s *v$args)";
pidl "{";
indent;
pidl "DEBUG(5,(\"$ifn\\n\"));";
pidl "";
# Call init for all arguments
foreach (@$es) {
InitLevel($_, $_->{LEVELS}[0], $_->{NAME}, $env);
pidl "";
}
pidl "return True;";
deindent;
pidl "}";
pidl "";
pidl "BOOL $fn(const char *desc, $s *v, prs_struct *ps, int depth)";
pidl "{";
indent;
DeclareArrayVariables($es);
pidl "if (v == NULL)";
pidl "\treturn False;";
pidl "";
pidl "prs_debug(ps, depth, desc, \"$fn\");";
pidl "depth++;";
my $align = 8;
foreach (@$es) {
ParseElement($_, $env, PRIMITIVES, \$align);
ParseElement($_, $env, DEFERRED, \$align);
pidl "";
}
pidl "return True;";
deindent;
pidl "}";
pidl "";
}
sub ParseFunction($$)
{
my ($if,$fn) = @_;
my @in = ();
my @out = ();
my @all = @{$fn->{ELEMENTS}};
foreach (@{$fn->{ELEMENTS}}) {
push (@in, $_) if (grep(/in/, @{$_->{DIRECTION}}));
push (@out, $_) if (grep(/out/, @{$_->{DIRECTION}}));
}
if (defined($fn->{RETURN_TYPE})) {
my $status = {
NAME => "status",
TYPE => $fn->{RETURN_TYPE},
LEVELS => [
{
TYPE => "DATA",
DATA_TYPE => $fn->{RETURN_TYPE}
}
]
};
push (@out, $status);
push (@all, $status);
}
CreateFnDirection("$if->{NAME}_io_q_$fn->{NAME}",
"init_$if->{NAME}_q_$fn->{NAME}",
uc("$if->{NAME}_q_$fn->{NAME}"),
\@in, \@in);
CreateFnDirection("$if->{NAME}_io_r_$fn->{NAME}",
"init_$if->{NAME}_r_$fn->{NAME}",
uc("$if->{NAME}_r_$fn->{NAME}"),
\@all, \@out);
}
sub ParseInterface($)
{
my $if = shift;
# Structures first
pidl "/* $if->{NAME} structures */";
foreach (@{$if->{TYPES}}) {
ParseStruct($if, $_->{DATA}, $_->{NAME}) if ($_->{DATA}->{TYPE} eq "STRUCT");
ParseUnion($if, $_->{DATA}, $_->{NAME}) if ($_->{DATA}->{TYPE} eq "UNION");
}
pidl "/* $if->{NAME} functions */";
ParseFunction($if, $_) foreach (@{$if->{FUNCTIONS}});
}
sub Parse($$)
{
my($ndr,$filename) = @_;
$tabs = "";
$res = "";
pidl "/*";
pidl " * Unix SMB/CIFS implementation.";
pidl " * parser auto-generated by pidl. DO NOT MODIFY!";
pidl " */";
pidl "";
pidl "#include \"includes.h\"";
pidl "";
pidl "#undef DBGC_CLASS";
pidl "#define DBGC_CLASS DBGC_RPC_PARSE";
pidl "";
foreach (@$ndr) {
ParseInterface($_) if ($_->{TYPE} eq "INTERFACE");
}
return $res;
}
1;