diff options
author | Günther Deschner <gd@samba.org> | 2009-02-17 03:32:16 +0100 |
---|---|---|
committer | Karolin Seeger <kseeger@samba.org> | 2009-03-27 13:06:46 +0100 |
commit | 82dcd55c09a19e59ea3cfd24d2ae63bf2815e163 (patch) | |
tree | 9e655f7ec58271ba46232fe6cf78aa59184cd0b8 /source | |
parent | 9d29633f1dc6ddf857c4518e815b24db8b9ade6d (diff) | |
download | samba-82dcd55c09a19e59ea3cfd24d2ae63bf2815e163.tar.gz samba-82dcd55c09a19e59ea3cfd24d2ae63bf2815e163.tar.xz samba-82dcd55c09a19e59ea3cfd24d2ae63bf2815e163.zip |
pidl: add pidl in order to be able to regenerate librpc functions.
This is pidl as of 0c6ce977ffb8f53f546596b76c1e0858cc6a88ba.
Guenther
(cherry picked from commit bd53a095de3d221cab4a90a35cd018edd800f79a)
Diffstat (limited to 'source')
61 files changed, 20213 insertions, 0 deletions
diff --git a/source/pidl/MANIFEST b/source/pidl/MANIFEST new file mode 100644 index 00000000000..051c5d2b19f --- /dev/null +++ b/source/pidl/MANIFEST @@ -0,0 +1,43 @@ +MANIFEST +tests/parse_idl.pl +tests/Util.pm +tests/ndr_refptr.pl +tests/ndr_string.pl +tests/ndr_simple.pl +tests/ndr_align.pl +tests/ndr_alloc.pl +tests/ndr_array.pl +tests/ndr.pl +tests/samba-ndr.pl +tests/util.pl +tests/test_util.pl +tests/ndr_represent.pl +tests/ndr_compat.pl +tests/ndr_fullptr.pl +tests/ndr_tagtype.pl +tests/header.pl +lib/Parse/Pidl/Samba3/ClientNDR.pm +lib/Parse/Pidl/Samba3/ServerNDR.pm +lib/Parse/Pidl/Samba4/NDR/Server.pm +lib/Parse/Pidl/Samba4/NDR/Parser.pm +lib/Parse/Pidl/Samba4/NDR/Client.pm +lib/Parse/Pidl/Samba4/Header.pm +lib/Parse/Pidl/Samba4/SWIG.pm +lib/Parse/Pidl/Samba4/TDR.pm +lib/Parse/Pidl/Samba4/Template.pm +lib/Parse/Pidl/Samba4/EJS.pm +lib/Parse/Pidl/Samba4.pm +lib/Parse/Pidl/Wireshark/Conformance.pm +lib/Parse/Pidl/Wireshark/NDR.pm +lib/Parse/Pidl/Typelist.pm +lib/Parse/Pidl/Dump.pm +lib/Parse/Pidl/Compat.pm +lib/Parse/Pidl/Util.pm +lib/Parse/Pidl/NDR.pm +lib/Parse/Pidl.pm +Makefile.PL +idl.yp +TODO +README +pidl +META.yml diff --git a/source/pidl/META.yml b/source/pidl/META.yml new file mode 100644 index 00000000000..e523c3044f8 --- /dev/null +++ b/source/pidl/META.yml @@ -0,0 +1,18 @@ +name: Parse-Pidl +abstract: Generate parsers / DCE/RPC-clients from IDL +author: + - Andrew Tridgell <tridge@samba.org> + - Jelmer Vernooij <jelmer@samba.org> + - Stefan Metzmacher <metze@samba.org> + - Tim Potter <tpot@samba.org> +license: gplv3 +installdirs: site +homepage: http://www.samba.org/ +bugtracker: http://bugzilla.samba.org/ +requires: + Parse::Yapp: 0 +recommends: + Data::Dumper: 0 +meta-spec: + version: 1.3 + url: http://module-build.sourceforge.net/META-spec-v1.3.html diff --git a/source/pidl/Makefile.PL b/source/pidl/Makefile.PL new file mode 100755 index 00000000000..2a405fcc2b1 --- /dev/null +++ b/source/pidl/Makefile.PL @@ -0,0 +1,17 @@ +use ExtUtils::MakeMaker; +WriteMakefile( + 'NAME' => 'Parse::Pidl', + 'VERSION_FROM' => 'lib/Parse/Pidl.pm', + 'EXE_FILES' => [ 'pidl' ], + 'test' => { 'TESTS' => 'tests/*.pl' } +); + +sub MY::postamble { +<<'EOT'; +lib/Parse/Pidl/IDL.pm: idl.yp + yapp -m 'Parse::Pidl::IDL' -o lib/Parse/Pidl/IDL.pm idl.yp + +lib/Parse/Pidl/Expr.pm: expr.yp + yapp -m 'Parse::Pidl::Expr' -o lib/Parse/Pidl/Expr.pm expr.yp +EOT +} diff --git a/source/pidl/README b/source/pidl/README new file mode 100644 index 00000000000..9c5d7e6aef4 --- /dev/null +++ b/source/pidl/README @@ -0,0 +1,64 @@ +Introduction: +============= +This directory contains the source code of the pidl (Perl IDL) +compiler for Samba 4. + +The main sources for pidl are available by Subversion on +svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/pidl + +Pidl works by building a parse tree from a .pidl file (a simple +dump of it's internal parse tree) or a .idl file +(a file format mostly like the IDL file format midl uses). +The IDL file parser is in idl.yp (a yacc file converted to +perl code by yapp) + +After a parse tree is present, pidl will call one of it's backends +(which one depends on the options given on the command-line). Here is +a list of current backends: + +Standalone installation: +======================== +Run Makefile.PL to generate the Makefile. + +Then run "make install" (as root) to install. + +Internals overview: +=================== + +-- Generic -- +Parse::Pidl::Dump - Converts the parse tree back to an IDL file +Parse::Pidl::Samba4::Header - Generates header file with data structures defined in IDL file +Parse::Pidl::NDR - Generates intermediate datastructures for use by NDR parses/generators +Parse::Pidl::ODL - Generates IDL structures from ODL structures for use in the NDR parser generator +Parse::Pidl::Test - Utility functions for use in pidl's testsuite + +-- Samba NDR -- +Parse::Pidl::Samba4::NDR::Client - Generates client call functions in C using the NDR parser +Parse::Pidl::Samba4::SWIG - Generates SWIG interface files (.i) +Parse::Pidl::Samba4::NDR::Parser - Generates pull/push functions for parsing NDR +Parse::Pidl::Samba4::NDR::Server - Generates server side implementation in C +Parse::Pidl::Samba4::TDR - Parser generator for the "Trivial Data Representation" +Parse::Pidl::Samba4::Template - Generates stubs in C for server implementation +Parse::Pidl::Samba4::EJS - Generates bindings for Embedded JavaScript (EJS) +Parse::Pidl::Samba4::Python - Generates bindings for Python + +-- Samba COM / DCOM -- +Parse::Pidl::Samba4::COM::Proxy - Generates proxy object for DCOM (client-side) +Parse::Pidl::Samba4::COM::Stub - Generates stub call handler for DCOM (server-side) +Parse::Pidl::Samba4::COM::Header - Generates headers for COM + +-- Wireshark -- +Parse::Pidl::Wireshark::NDR - Generates a parser for the Wireshark network sniffer +Parse::Pidl::Wireshark::Conformance - Reads conformance files containing additional data for generating Wireshark parsers + +-- Utility modules -- +Parse::Pidl::Util - Misc utility functions used by *.pm and pidl.pl +Parse::Pidl::Typelist - Utility functions for keeping track of known types and their representation in C + +Tips for hacking on pidl: + - Look at the pidl's parse tree by using the --keep option and looking + at the generated .pidl file. + - The various backends have a lot in common, if you don't understand how one + implements something, look at the others + - See pidl(1) and the documentation on midl + - See 'info bison' and yapp(1) for information on the file format of idl.yp diff --git a/source/pidl/TODO b/source/pidl/TODO new file mode 100644 index 00000000000..17b4d9d8430 --- /dev/null +++ b/source/pidl/TODO @@ -0,0 +1,42 @@ +- true multiple dimension array / strings in arrays support + +- compatibility mode for generating MIDL-readable data: + - strip out pidl-specific properties + +- make bitmap an optional attribute on enum +- support nested elements +- support typedefs properly (e.g. allow "typedef void **bla;") +- make typedefs generate real typedefs +- improve represent_as(): allow it to be used for arrays and other complex + types + +- --explain-ndr option that dumps out parse tree ? + +- seperate tables for NDR and DCE/RPC + - maybe no tables for NDR at all? we only need them for ndrdump + and that can use dlsym() + +- allow data structures outside of interfaces + +- mem_ctx in the interface rather than as struct ndr member. + +- real typelibs + +- fix [in,out] handling and allocation for samba3: + - add inout + - make NULL to mean "allocate me" + - remove NDR_AUTO_REF_ALLOC flag + +- automatic test generator based on IDL pointer types + +- support converting structs to tuples in Python rather than objects +- convert structs with a single mattering member to that member directly, e.g.: + struct bar { + int size; + [size_is(size)] uint32 *array; + }; + + should be converted to an array of uint32's + +- python: fill in size members automatically in some places if the struct isn't being returned + (so we don't have to cope with the array growing) diff --git a/source/pidl/config.m4 b/source/pidl/config.m4 new file mode 100644 index 00000000000..8b8bc5acf02 --- /dev/null +++ b/source/pidl/config.m4 @@ -0,0 +1,9 @@ +# Check whether ExtUtils::ExtMaker is available + +if perl -e "use ExtUtils::MakeMaker" 2>/dev/null; then + HAVE_PERL_EXTUTILS_MAKEMAKER=1 +else + HAVE_PERL_EXTUTILS_MAKEMAKER=0 +fi + +AC_SUBST(HAVE_PERL_EXTUTILS_MAKEMAKER) diff --git a/source/pidl/config.mk b/source/pidl/config.mk new file mode 100644 index 00000000000..b1b68b9ca97 --- /dev/null +++ b/source/pidl/config.mk @@ -0,0 +1,31 @@ +PIDL = $(PERL) $(pidldir)/pidl + +$(pidldir)/Makefile: $(pidldir)/Makefile.PL + cd $(pidldir) && $(PERL) Makefile.PL PREFIX=$(prefix) + +pidl-testcov: $(pidldir)/Makefile + cd $(pidldir) && cover -test + +installpidl:: $(pidldir)/Makefile + $(MAKE) -C $(pidldir) install_vendor VENDORPREFIX=$(prefix) \ + INSTALLVENDORLIB=$(datarootdir)/perl5 \ + INSTALLVENDORBIN=$(bindir) \ + INSTALLVENDORSCRIPT=$(bindir) \ + INSTALLVENDORMAN1DIR=$(mandir)/man1 \ + INSTALLVENDORMAN3DIR=$(mandir)/man3 + +ifeq ($(HAVE_PERL_EXTUTILS_MAKEMAKER),1) +install:: installpidl +endif + +$(pidldir)/lib/Parse/Pidl/IDL.pm: $(pidldir)/idl.yp + -$(YAPP) -m 'Parse::Pidl::IDL' -o $(pidldir)/lib/Parse/Pidl/IDL.pm $(pidldir)/idl.yp ||\ + touch $(pidldir)/lib/Parse/Pidl/IDL.pm + +$(pidldir)/lib/Parse/Pidl/Expr.pm: $(pidldir)/idl.yp + -$(YAPP) -m 'Parse::Pidl::Expr' -o $(pidldir)/lib/Parse/Pidl/Expr.pm $(pidldir)/expr.yp ||\ + touch $(pidldir)/lib/Parse/Pidl/Expr.pm + +testcov-html:: pidl-testcov + + diff --git a/source/pidl/expr.yp b/source/pidl/expr.yp new file mode 100644 index 00000000000..ffdbdaf8a27 --- /dev/null +++ b/source/pidl/expr.yp @@ -0,0 +1,150 @@ +# expr.yp +# Copyright (C) 2006 Jelmer Vernooij <jelmer@samba.org> +# Published under the GNU GPL +# +%left '->' +%right '!' '~' +%left '*' '/' '%' +%left '+' '-' +%left '<<' '>>' +%left '>' '<' +%left '==' '!=' +%left '&' +%left '|' +%left '&&' +%left '||' +%left '?' ':' +%left NEG DEREF ADDROF INV +%left '.' + +%% +exp: NUM + | TEXT { "\"$_[1]\"" } + | func + | var + | '~' exp %prec INV { "~$_[2]" } + | exp '+' exp { "$_[1] + $_[3]" } + | exp '-' exp { "$_[1] - $_[3]" } + | exp '*' exp { "$_[1] * $_[3]" } + | exp '%' exp { "$_[1] % $_[3]" } + | exp '<' exp { "$_[1] < $_[3]" } + | exp '>' exp { "$_[1] > $_[3]" } + | exp '|' exp { "$_[1] | $_[3]" } + | exp '==' exp { "$_[1] == $_[3]" } + | exp '<=' exp { "$_[1] <= $_[3]" } + | exp '=>' exp { "$_[1] => $_[3]" } + | exp '<<' exp { "$_[1] << $_[3]" } + | exp '>>' exp { "$_[1] >> $_[3]" } + | exp '!=' exp { "$_[1] != $_[3]" } + | exp '||' exp { "$_[1] || $_[3]" } + | exp '&&' exp { "$_[1] && $_[3]" } + | exp '&' exp { "$_[1] & $_[3]" } + | exp '?' exp ':' exp { "$_[1]?$_[3]:$_[5]" } + | '~' exp { "~$_[1]" } + | '!' exp { "not $_[1]" } + | exp '/' exp { "$_[1] / $_[3]" } + | '-' exp %prec NEG { "-$_[2]" } + | '&' exp %prec ADDROF { "&$_[2]" } + | exp '^' exp { "$_[1]^$_[3]" } + | '(' exp ')' { "($_[2])" } +; + +possible_pointer: + VAR { $_[0]->_Lookup($_[1]) } + | '*' possible_pointer %prec DEREF { $_[0]->_Dereference($_[2]); "*$_[2]" } + ; + +var: possible_pointer { $_[0]->_Use($_[1]) } + | var '.' VAR { $_[0]->_Use("$_[1].$_[3]") } + | '(' var ')' { "($_[2])" } + | var '->' VAR { $_[0]->_Use("*$_[1]"); $_[1]."->".$_[3] } +; + + +func: VAR '(' opt_args ')' { "$_[1]($_[3])" }; +opt_args: { "" } | args; +exp_or_possible_pointer: exp | possible_pointer; +args: exp_or_possible_pointer + | exp_or_possible_pointer ',' args { "$_[1], $_[3]" } +; + +%% + +package Parse::Pidl::Expr; + +sub _Lexer { + my($parser)=shift; + + $parser->YYData->{INPUT}=~s/^[ \t]//; + + for ($parser->YYData->{INPUT}) { + if (s/^(0x[0-9A-Fa-f]+)//) { + $parser->YYData->{LAST_TOKEN} = $1; + return('NUM',$1); + } + if (s/^([0-9]+(?:\.[0-9]+)?)//) { + $parser->YYData->{LAST_TOKEN} = $1; + return('NUM',$1); + } + if (s/^([A-Za-z_][A-Za-z0-9_]*)//) { + $parser->YYData->{LAST_TOKEN} = $1; + return('VAR',$1); + } + if (s/^\"(.*?)\"//) { + $parser->YYData->{LAST_TOKEN} = $1; + return('TEXT',$1); + } + if (s/^(==|!=|<=|>=|->|\|\||<<|>>|&&)//s) { + $parser->YYData->{LAST_TOKEN} = $1; + return($1,$1); + } + if (s/^(.)//s) { + $parser->YYData->{LAST_TOKEN} = $1; + return($1,$1); + } + } +} + +sub _Use($$) +{ + my ($self, $x) = @_; + if (defined($self->YYData->{USE})) { + return $self->YYData->{USE}->($x); + } + return $x; +} + +sub _Lookup($$) +{ + my ($self, $x) = @_; + return $self->YYData->{LOOKUP}->($x); +} + +sub _Dereference($$) +{ + my ($self, $x) = @_; + if (defined($self->YYData->{DEREFERENCE})) { + $self->YYData->{DEREFERENCE}->($x); + } +} + +sub _Error($) +{ + my ($self) = @_; + if (defined($self->YYData->{LAST_TOKEN})) { + $self->YYData->{ERROR}->("Parse error in `".$self->YYData->{FULL_INPUT}."' near `". $self->YYData->{LAST_TOKEN} . "'"); + } else { + $self->YYData->{ERROR}->("Parse error in `".$self->YYData->{FULL_INPUT}."'"); + } +} + +sub Run { + my($self, $data, $error, $lookup, $deref, $use) = @_; + $self->YYData->{FULL_INPUT} = $data; + $self->YYData->{INPUT} = $data; + $self->YYData->{LOOKUP} = $lookup; + $self->YYData->{DEREFERENCE} = $deref; + $self->YYData->{ERROR} = $error; + $self->YYData->{USE} = $use; + return $self->YYParse( yylex => \&_Lexer, yyerror => \&_Error); +} diff --git a/source/pidl/idl.yp b/source/pidl/idl.yp new file mode 100644 index 00000000000..781a279783f --- /dev/null +++ b/source/pidl/idl.yp @@ -0,0 +1,485 @@ +######################## +# IDL Parse::Yapp parser +# Copyright (C) Andrew Tridgell <tridge@samba.org> +# released under the GNU GPL version 3 or later + + + +# the precedence actually doesn't matter at all for this grammar, but +# by providing a precedence we reduce the number of conflicts +# enormously +%left '-' '+' '&' '|' '*' '>' '.' '/' '(' ')' '[' ',' ';' + + +################ +# grammar +%% +idl: + #empty { {} } + | idl interface { push(@{$_[1]}, $_[2]); $_[1] } + | idl coclass { push(@{$_[1]}, $_[2]); $_[1] } + | idl import { push(@{$_[1]}, $_[2]); $_[1] } + | idl include { push(@{$_[1]}, $_[2]); $_[1] } + | idl importlib { push(@{$_[1]}, $_[2]); $_[1] } + | idl cpp_quote { push(@{$_[1]}, $_[2]); $_[1] } +; + +import: 'import' commalist ';' {{ + "TYPE" => "IMPORT", + "PATHS" => $_[2], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE} + }} +; +include: 'include' commalist ';' {{ + "TYPE" => "INCLUDE", + "PATHS" => $_[2], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE} + }} +; +importlib: 'importlib' commalist ';' {{ + "TYPE" => "IMPORTLIB", + "PATHS" => $_[2], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE} + }} +; + +commalist: + text { [ $_[1] ] } + | commalist ',' text { push(@{$_[1]}, $_[3]); $_[1] } +; + +coclass: property_list 'coclass' identifier '{' interface_names '}' optional_semicolon + {{ + "TYPE" => "COCLASS", + "PROPERTIES" => $_[1], + "NAME" => $_[3], + "DATA" => $_[5], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} +; + +interface_names: + #empty { {} } + | interface_names 'interface' identifier ';' { push(@{$_[1]}, $_[2]); $_[1] } +; + +interface: property_list 'interface' identifier '{' definitions '}' optional_semicolon + {{ + "TYPE" => "INTERFACE", + "PROPERTIES" => $_[1], + "NAME" => $_[3], + "DATA" => $_[5], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} +; + +cpp_quote: 'cpp_quote' '(' text ')' + {{ + "TYPE" => "CPP_QUOTE", + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + "DATA" => $_[3] + }} +; + +definitions: + definition { [ $_[1] ] } + | definitions definition { push(@{$_[1]}, $_[2]); $_[1] } +; + + +definition: function | const | typedef | typedecl +; + +const: 'const' identifier pointers identifier '=' anytext ';' + {{ + "TYPE" => "CONST", + "DTYPE" => $_[2], + "POINTERS" => $_[3], + "NAME" => $_[4], + "VALUE" => $_[6], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} + | 'const' identifier pointers identifier array_len '=' anytext ';' + {{ + "TYPE" => "CONST", + "DTYPE" => $_[2], + "POINTERS" => $_[3], + "NAME" => $_[4], + "ARRAY_LEN" => $_[5], + "VALUE" => $_[7], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} +; + + +function: property_list type identifier '(' element_list2 ')' ';' + {{ + "TYPE" => "FUNCTION", + "NAME" => $_[3], + "RETURN_TYPE" => $_[2], + "PROPERTIES" => $_[1], + "ELEMENTS" => $_[5], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} +; + +typedef: property_list 'typedef' type identifier array_len ';' + {{ + "TYPE" => "TYPEDEF", + "PROPERTIES" => $_[1], + "NAME" => $_[4], + "DATA" => $_[3], + "ARRAY_LEN" => $_[5], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} +; + +usertype: struct | union | enum | bitmap; + +typedecl: usertype ';' { $_[1] }; + +sign: 'signed' | 'unsigned'; + +existingtype: + sign identifier { ($_[1]?$_[1]:"signed") ." $_[2]" } + | identifier +; + +type: usertype | existingtype | void { "void" } ; + +enum_body: '{' enum_elements '}' { $_[2] }; +opt_enum_body: | enum_body; +enum: property_list 'enum' optional_identifier opt_enum_body + {{ + "TYPE" => "ENUM", + "PROPERTIES" => $_[1], + "NAME" => $_[3], + "ELEMENTS" => $_[4] + }} +; + +enum_elements: + enum_element { [ $_[1] ] } + | enum_elements ',' enum_element { push(@{$_[1]}, $_[3]); $_[1] } +; + +enum_element: identifier + | identifier '=' anytext { "$_[1]$_[2]$_[3]" } +; + +bitmap_body: '{' opt_bitmap_elements '}' { $_[2] }; +opt_bitmap_body: | bitmap_body; +bitmap: property_list 'bitmap' optional_identifier opt_bitmap_body + {{ + "TYPE" => "BITMAP", + "PROPERTIES" => $_[1], + "NAME" => $_[3], + "ELEMENTS" => $_[4] + }} +; + +bitmap_elements: + bitmap_element { [ $_[1] ] } + | bitmap_elements ',' bitmap_element { push(@{$_[1]}, $_[3]); $_[1] } +; + +opt_bitmap_elements: | bitmap_elements; + +bitmap_element: identifier '=' anytext { "$_[1] ( $_[3] )" } +; + +struct_body: '{' element_list1 '}' { $_[2] }; +opt_struct_body: | struct_body; + +struct: property_list 'struct' optional_identifier opt_struct_body + {{ + "TYPE" => "STRUCT", + "PROPERTIES" => $_[1], + "NAME" => $_[3], + "ELEMENTS" => $_[4] + }} +; + +empty_element: property_list ';' + {{ + "NAME" => "", + "TYPE" => "EMPTY", + "PROPERTIES" => $_[1], + "POINTERS" => 0, + "ARRAY_LEN" => [], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} +; + +base_or_empty: base_element ';' | empty_element; + +optional_base_element: + property_list base_or_empty { $_[2]->{PROPERTIES} = FlattenHash([$_[1],$_[2]->{PROPERTIES}]); $_[2] } +; + +union_elements: + #empty + | union_elements optional_base_element { push(@{$_[1]}, $_[2]); $_[1] } +; + +union_body: '{' union_elements '}' { $_[2] }; +opt_union_body: | union_body; + +union: property_list 'union' optional_identifier opt_union_body + {{ + "TYPE" => "UNION", + "PROPERTIES" => $_[1], + "NAME" => $_[3], + "ELEMENTS" => $_[4] + }} +; + +base_element: property_list type pointers identifier array_len + {{ + "NAME" => $_[4], + "TYPE" => $_[2], + "PROPERTIES" => $_[1], + "POINTERS" => $_[3], + "ARRAY_LEN" => $_[5], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} +; + + +pointers: + #empty + { 0 } + | pointers '*' { $_[1]+1 } +; + +element_list1: + { [] } + | element_list1 base_element ';' { push(@{$_[1]}, $_[2]); $_[1] } +; + +element_list2: + #empty + | 'void' + | base_element { [ $_[1] ] } + | element_list2 ',' base_element { push(@{$_[1]}, $_[3]); $_[1] } +; + +array_len: + #empty { [] } + | '[' ']' array_len { push(@{$_[3]}, "*"); $_[3] } + | '[' anytext ']' array_len { push(@{$_[4]}, "$_[2]"); $_[4] } +; + + +property_list: + #empty + | property_list '[' properties ']' { FlattenHash([$_[1],$_[3]]); } +; + +properties: property { $_[1] } + | properties ',' property { FlattenHash([$_[1], $_[3]]); } +; + +property: identifier {{ "$_[1]" => "1" }} + | identifier '(' commalisttext ')' {{ "$_[1]" => "$_[3]" }} +; + +commalisttext: + anytext + | commalisttext ',' anytext { "$_[1],$_[3]" } +; + +anytext: #empty + { "" } + | identifier | constant | text + | anytext '-' anytext { "$_[1]$_[2]$_[3]" } + | anytext '.' anytext { "$_[1]$_[2]$_[3]" } + | anytext '*' anytext { "$_[1]$_[2]$_[3]" } + | anytext '>' anytext { "$_[1]$_[2]$_[3]" } + | anytext '<' anytext { "$_[1]$_[2]$_[3]" } + | anytext '|' anytext { "$_[1]$_[2]$_[3]" } + | anytext '&' anytext { "$_[1]$_[2]$_[3]" } + | anytext '/' anytext { "$_[1]$_[2]$_[3]" } + | anytext '?' anytext { "$_[1]$_[2]$_[3]" } + | anytext ':' anytext { "$_[1]$_[2]$_[3]" } + | anytext '=' anytext { "$_[1]$_[2]$_[3]" } + | anytext '+' anytext { "$_[1]$_[2]$_[3]" } + | anytext '~' anytext { "$_[1]$_[2]$_[3]" } + | anytext '(' commalisttext ')' anytext { "$_[1]$_[2]$_[3]$_[4]$_[5]" } + | anytext '{' commalisttext '}' anytext { "$_[1]$_[2]$_[3]$_[4]$_[5]" } +; + +identifier: IDENTIFIER +; + +optional_identifier: + IDENTIFIER + | #empty { undef } +; + +constant: CONSTANT +; + +text: TEXT { "\"$_[1]\"" } +; + +optional_semicolon: + #empty + | ';' +; + + +##################################### +# start code +%% + +use Parse::Pidl qw(error); + +##################################################################### +# flatten an array of hashes into a single hash +sub FlattenHash($) +{ + my $a = shift; + my %b; + for my $d (@{$a}) { + for my $k (keys %{$d}) { + $b{$k} = $d->{$k}; + } + } + return \%b; +} + + + +##################################################################### +# traverse a perl data structure removing any empty arrays or +# hashes and any hash elements that map to undef +sub CleanData($) +{ + sub CleanData($); + my($v) = shift; + return undef if (not defined($v)); + if (ref($v) eq "ARRAY") { + foreach my $i (0 .. $#{$v}) { + CleanData($v->[$i]); + } + # this removes any undefined elements from the array + @{$v} = grep { defined $_ } @{$v}; + } elsif (ref($v) eq "HASH") { + foreach my $x (keys %{$v}) { + CleanData($v->{$x}); + if (!defined $v->{$x}) { delete($v->{$x}); next; } + } + } + return $v; +} + +sub _Error { + if (exists $_[0]->YYData->{ERRMSG}) { + error($_[0]->YYData, $_[0]->YYData->{ERRMSG}); + delete $_[0]->YYData->{ERRMSG}; + return; + } + my $last_token = $_[0]->YYData->{LAST_TOKEN}; + + error($_[0]->YYData, "Syntax error near '$last_token'"); +} + +sub _Lexer($) +{ + my($parser)=shift; + + $parser->YYData->{INPUT} or return('',undef); + +again: + $parser->YYData->{INPUT} =~ s/^[ \t]*//; + + for ($parser->YYData->{INPUT}) { + if (/^\#/) { + if (s/^\# (\d+) \"(.*?)\"( \d+|)//) { + $parser->YYData->{LINE} = $1-1; + $parser->YYData->{FILE} = $2; + goto again; + } + if (s/^\#line (\d+) \"(.*?)\"( \d+|)//) { + $parser->YYData->{LINE} = $1-1; + $parser->YYData->{FILE} = $2; + goto again; + } + if (s/^(\#.*)$//m) { + goto again; + } + } + if (s/^(\n)//) { + $parser->YYData->{LINE}++; + goto again; + } + if (s/^\"(.*?)\"//) { + $parser->YYData->{LAST_TOKEN} = $1; + return('TEXT',$1); + } + if (s/^(\d+)(\W|$)/$2/) { + $parser->YYData->{LAST_TOKEN} = $1; + return('CONSTANT',$1); + } + if (s/^([\w_]+)//) { + $parser->YYData->{LAST_TOKEN} = $1; + if ($1 =~ + /^(coclass|interface|const|typedef|union|cpp_quote + |struct|enum|bitmap|void|unsigned|signed|import|include + |importlib)$/x) { + return $1; + } + return('IDENTIFIER',$1); + } + if (s/^(.)//s) { + $parser->YYData->{LAST_TOKEN} = $1; + return($1,$1); + } + } +} + +sub parse_string +{ + my ($data,$filename) = @_; + + my $self = new Parse::Pidl::IDL; + + $self->YYData->{FILE} = $filename; + $self->YYData->{INPUT} = $data; + $self->YYData->{LINE} = 0; + $self->YYData->{LAST_TOKEN} = "NONE"; + + my $idl = $self->YYParse( yylex => \&_Lexer, yyerror => \&_Error ); + + return CleanData($idl); +} + +sub parse_file($$) +{ + my ($filename,$incdirs) = @_; + + my $saved_delim = $/; + undef $/; + my $cpp = $ENV{CPP}; + if (! defined $cpp) { + $cpp = "cpp"; + } + my $includes = join('',map { " -I$_" } @$incdirs); + my $data = `$cpp -D__PIDL__$includes -xc $filename`; + $/ = $saved_delim; + + return parse_string($data, $filename); +} diff --git a/source/pidl/lib/Parse/Pidl.pm b/source/pidl/lib/Parse/Pidl.pm new file mode 100644 index 00000000000..2d82ad5e74c --- /dev/null +++ b/source/pidl/lib/Parse/Pidl.pm @@ -0,0 +1,38 @@ +################################################### +# package to parse IDL files and generate code for +# rpc functions in Samba +# Copyright tridge@samba.org 2000-2003 +# Copyright jelmer@samba.org 2005 +# released under the GNU GPL + +package Parse::Pidl; + +require Exporter; +@ISA = qw(Exporter); +@EXPORT_OK = qw(warning error fatal $VERSION); + +use strict; + +use vars qw ( $VERSION ); + +$VERSION = '0.02'; + +sub warning +{ + my ($l,$m) = @_; + print STDERR "$l->{FILE}:$l->{LINE}: warning: $m\n"; +} + +sub error +{ + my ($l,$m) = @_; + print STDERR "$l->{FILE}:$l->{LINE}: error: $m\n"; +} + +sub fatal($$) +{ + my ($e,$s) = @_; + die("$e->{FILE}:$e->{LINE}: $s\n"); +} + +1; diff --git a/source/pidl/lib/Parse/Pidl/CUtil.pm b/source/pidl/lib/Parse/Pidl/CUtil.pm new file mode 100644 index 00000000000..942a4101ef6 --- /dev/null +++ b/source/pidl/lib/Parse/Pidl/CUtil.pm @@ -0,0 +1,52 @@ +################################################### +# C utility functions for pidl +# Copyright jelmer@samba.org 2005-2007 +# released under the GNU GPL +package Parse::Pidl::CUtil; + +require Exporter; +@ISA = qw(Exporter); +@EXPORT = qw(get_pointer_to get_value_of get_array_element); +use vars qw($VERSION); +$VERSION = '0.01'; + +use strict; + +sub get_pointer_to($) +{ + my $var_name = shift; + + if ($var_name =~ /^\*(.*)$/) { + return $1; + } elsif ($var_name =~ /^\&(.*)$/) { + return "&($var_name)"; + } else { + return "&$var_name"; + } +} + +sub get_value_of($) +{ + my $var_name = shift; + + if ($var_name =~ /^\&(.*)$/) { + return $1; + } else { + return "*$var_name"; + } +} + +sub get_array_element($$) +{ + my ($var_name, $idx) = @_; + + if ($var_name =~ /^\*.*$/) { + $var_name = "($var_name)"; + } elsif ($var_name =~ /^\&.*$/) { + $var_name = "($var_name)"; + } + + return "$var_name"."[$idx]"; +} + +1; diff --git a/source/pidl/lib/Parse/Pidl/Compat.pm b/source/pidl/lib/Parse/Pidl/Compat.pm new file mode 100644 index 00000000000..b682c7826df --- /dev/null +++ b/source/pidl/lib/Parse/Pidl/Compat.pm @@ -0,0 +1,163 @@ +################################################### +# IDL Compatibility checker +# Copyright jelmer@samba.org 2005 +# released under the GNU GPL + +package Parse::Pidl::Compat; + +use Parse::Pidl qw(warning); +use Parse::Pidl::Util qw(has_property); +use strict; + +use vars qw($VERSION); +$VERSION = '0.01'; + +my %supported_properties = ( + # interface + "helpstring" => ["INTERFACE", "FUNCTION"], + "version" => ["INTERFACE"], + "uuid" => ["INTERFACE"], + "endpoint" => ["INTERFACE"], + "pointer_default" => ["INTERFACE"], + + # dcom + "object" => ["INTERFACE"], + "local" => ["INTERFACE", "FUNCTION"], + "iid_is" => ["ELEMENT"], + "call_as" => ["FUNCTION"], + "idempotent" => ["FUNCTION"], + + # function + "in" => ["ELEMENT"], + "out" => ["ELEMENT"], + + # pointer + "ref" => ["ELEMENT"], + "ptr" => ["ELEMENT"], + "unique" => ["ELEMENT"], + "ignore" => ["ELEMENT"], + + "value" => ["ELEMENT"], + + # generic + "public" => ["FUNCTION", "TYPEDEF"], + "nopush" => ["FUNCTION", "TYPEDEF"], + "nopull" => ["FUNCTION", "TYPEDEF"], + "noprint" => ["FUNCTION", "TYPEDEF"], + "noejs" => ["FUNCTION", "TYPEDEF"], + + # union + "switch_is" => ["ELEMENT"], + "switch_type" => ["ELEMENT", "TYPEDEF"], + "case" => ["ELEMENT"], + "default" => ["ELEMENT"], + + # subcontext + "subcontext" => ["ELEMENT"], + "subcontext_size" => ["ELEMENT"], + + # enum + "enum16bit" => ["TYPEDEF"], + "v1_enum" => ["TYPEDEF"], + + # bitmap + "bitmap8bit" => ["TYPEDEF"], + "bitmap16bit" => ["TYPEDEF"], + "bitmap32bit" => ["TYPEDEF"], + "bitmap64bit" => ["TYPEDEF"], + + # array + "range" => ["ELEMENT"], + "size_is" => ["ELEMENT"], + "string" => ["ELEMENT"], + "noheader" => ["ELEMENT"], + "charset" => ["ELEMENT"], + "length_is" => ["ELEMENT"], +); + +sub CheckTypedef($) +{ + my ($td) = @_; + + if (has_property($td, "nodiscriminant")) { + warning($td, "nodiscriminant property not supported"); + } + + if ($td->{TYPE} eq "BITMAP") { + warning($td, "converting bitmap to scalar"); + #FIXME + } + + if (has_property($td, "gensize")) { + warning($td, "ignoring gensize() property. "); + } + + if (has_property($td, "enum8bit") and has_property($td, "enum16bit")) { + warning($td, "8 and 16 bit enums not supported, converting to scalar"); + #FIXME + } + + StripProperties($td); +} + +sub CheckElement($) +{ + my $e = shift; + + if (has_property($e, "noheader")) { + warning($e, "noheader property not supported"); + return; + } + + if (has_property($e, "subcontext")) { + warning($e, "converting subcontext to byte array"); + #FIXME + } + + if (has_property($e, "compression")) { + warning($e, "compression() property not supported"); + } + + if (has_property($e, "sptr")) { + warning($e, "sptr() pointer property not supported"); + } + + if (has_property($e, "relative")) { + warning($e, "relative() pointer property not supported"); + } + + if (has_property($e, "flag")) { + warning($e, "ignoring flag() property"); + } + + if (has_property($e, "value")) { + warning($e, "ignoring value() property"); + } +} + +sub CheckFunction($) +{ + my $fn = shift; + + if (has_property($fn, "noopnum")) { + warning($fn, "noopnum not converted. Opcodes will be out of sync."); + } +} + +sub CheckInterface($) +{ + my $if = shift; + +} + +sub Check($) +{ + my $pidl = shift; + my $nidl = []; + + foreach (@{$pidl}) { + push (@$nidl, CheckInterface($_)) if ($_->{TYPE} eq "INTERFACE"); + } +} + +1; diff --git a/source/pidl/lib/Parse/Pidl/Dump.pm b/source/pidl/lib/Parse/Pidl/Dump.pm new file mode 100644 index 00000000000..dbad2e094b6 --- /dev/null +++ b/source/pidl/lib/Parse/Pidl/Dump.pm @@ -0,0 +1,294 @@ +################################################### +# dump function for IDL structures +# Copyright tridge@samba.org 2000 +# Copyright jelmer@samba.org 2005 +# released under the GNU GPL + +=pod + +=head1 NAME + +Parse::Pidl::Dump - Dump support + +=head1 DESCRIPTION + +This module provides functions that can generate IDL code from +internal pidl data structures. + +=cut + +package Parse::Pidl::Dump; + +use Exporter; + +use vars qw($VERSION); +$VERSION = '0.01'; +@ISA = qw(Exporter); +@EXPORT_OK = qw(DumpType DumpTypedef DumpStruct DumpEnum DumpBitmap DumpUnion DumpFunction); + +use strict; +use Parse::Pidl::Util qw(has_property); + +my($res); + +##################################################################### +# dump a properties list +sub DumpProperties($) +{ + my($props) = shift; + my $res = ""; + + foreach my $d ($props) { + foreach my $k (keys %{$d}) { + if ($k eq "in") { + $res .= "[in] "; + next; + } + if ($k eq "out") { + $res .= "[out] "; + next; + } + if ($k eq "ref") { + $res .= "[ref] "; + next; + } + $res .= "[$k($d->{$k})] "; + } + } + return $res; +} + +##################################################################### +# dump a structure element +sub DumpElement($) +{ + my($element) = shift; + my $res = ""; + + (defined $element->{PROPERTIES}) && + ($res .= DumpProperties($element->{PROPERTIES})); + $res .= DumpType($element->{TYPE}); + $res .= " "; + for my $i (1..$element->{POINTERS}) { + $res .= "*"; + } + $res .= "$element->{NAME}"; + foreach (@{$element->{ARRAY_LEN}}) { + $res .= "[$_]"; + } + + return $res; +} + +##################################################################### +# dump a struct +sub DumpStruct($) +{ + my($struct) = shift; + my($res); + + $res .= "struct "; + if ($struct->{NAME}) { + $res.="$struct->{NAME} "; + } + + $res.="{\n"; + if (defined $struct->{ELEMENTS}) { + foreach (@{$struct->{ELEMENTS}}) { + $res .= "\t" . DumpElement($_) . ";\n"; + } + } + $res .= "}"; + + return $res; +} + + +##################################################################### +# dump a struct +sub DumpEnum($) +{ + my($enum) = shift; + my($res); + + $res .= "enum {\n"; + + foreach (@{$enum->{ELEMENTS}}) { + if (/^([A-Za-z0-9_]+)[ \t]*\((.*)\)$/) { + $res .= "\t$1 = $2,\n"; + } else { + $res .= "\t$_,\n"; + } + } + + $res.= "}"; + + return $res; +} + +##################################################################### +# dump a struct +sub DumpBitmap($) +{ + my($bitmap) = shift; + my($res); + + $res .= "bitmap {\n"; + + foreach (@{$bitmap->{ELEMENTS}}) { + if (/^([A-Za-z0-9_]+)[ \t]*\((.*)\)$/) { + $res .= "\t$1 = $2,\n"; + } else { + die ("Bitmap $bitmap->{NAME} has field $_ without proper value"); + } + } + + $res.= "}"; + + return $res; +} + + +##################################################################### +# dump a union element +sub DumpUnionElement($) +{ + my($element) = shift; + my($res); + + if (has_property($element, "default")) { + $res .= "[default] ;\n"; + } else { + $res .= "[case($element->{PROPERTIES}->{case})] "; + $res .= DumpElement($element), if defined($element); + $res .= ";\n"; + } + + return $res; +} + +##################################################################### +# dump a union +sub DumpUnion($) +{ + my($union) = shift; + my($res); + + (defined $union->{PROPERTIES}) && + ($res .= DumpProperties($union->{PROPERTIES})); + $res .= "union {\n"; + foreach my $e (@{$union->{ELEMENTS}}) { + $res .= DumpUnionElement($e); + } + $res .= "}"; + + return $res; +} + +##################################################################### +# dump a type +sub DumpType($) +{ + my($data) = shift; + + if (ref($data) eq "HASH") { + return DumpStruct($data) if ($data->{TYPE} eq "STRUCT"); + return DumpUnion($data) if ($data->{TYPE} eq "UNION"); + return DumpEnum($data) if ($data->{TYPE} eq "ENUM"); + return DumpBitmap($data) if ($data->{TYPE} eq "BITMAP"); + } else { + return $data; + } +} + +##################################################################### +# dump a typedef +sub DumpTypedef($) +{ + my($typedef) = shift; + my($res); + + $res .= "typedef "; + $res .= DumpType($typedef->{DATA}); + $res .= " $typedef->{NAME};\n\n"; + + return $res; +} + +##################################################################### +# dump a typedef +sub DumpFunction($) +{ + my($function) = shift; + my($first) = 1; + my($res); + + $res .= DumpType($function->{RETURN_TYPE}); + $res .= " $function->{NAME}(\n"; + for my $d (@{$function->{ELEMENTS}}) { + unless ($first) { $res .= ",\n"; } $first = 0; + $res .= DumpElement($d); + } + $res .= "\n);\n\n"; + + return $res; +} + +##################################################################### +# dump a module header +sub DumpInterfaceProperties($) +{ + my($header) = shift; + my($data) = $header->{DATA}; + my($first) = 1; + my($res); + + $res .= "[\n"; + foreach my $k (keys %{$data}) { + $first || ($res .= ",\n"); $first = 0; + $res .= "$k($data->{$k})"; + } + $res .= "\n]\n"; + + return $res; +} + +##################################################################### +# dump the interface definitions +sub DumpInterface($) +{ + my($interface) = shift; + my($data) = $interface->{DATA}; + my($res); + + $res .= DumpInterfaceProperties($interface->{PROPERTIES}); + + $res .= "interface $interface->{NAME}\n{\n"; + foreach my $d (@{$data}) { + ($d->{TYPE} eq "TYPEDEF") && + ($res .= DumpTypedef($d)); + ($d->{TYPE} eq "FUNCTION") && + ($res .= DumpFunction($d)); + } + $res .= "}\n"; + + return $res; +} + + +##################################################################### +# dump a parsed IDL structure back into an IDL file +sub Dump($) +{ + my($idl) = shift; + my($res); + + $res = "/* Dumped by pidl */\n\n"; + foreach my $x (@{$idl}) { + ($x->{TYPE} eq "INTERFACE") && + ($res .= DumpInterface($x)); + } + return $res; +} + +1; diff --git a/source/pidl/lib/Parse/Pidl/Expr.pm b/source/pidl/lib/Parse/Pidl/Expr.pm new file mode 100644 index 00000000000..66c3c488e0c --- /dev/null +++ b/source/pidl/lib/Parse/Pidl/Expr.pm @@ -0,0 +1,1442 @@ +#################################################################### +# +# This file was generated using Parse::Yapp version 1.05. +# +# Don't edit this file, use source file instead. +# +# ANY CHANGE MADE HERE WILL BE LOST ! +# +#################################################################### +package Parse::Pidl::Expr; +use vars qw ( @ISA ); +use strict; + +@ISA= qw ( Parse::Yapp::Driver ); +use Parse::Yapp::Driver; + + + +sub new { + my($class)=shift; + ref($class) + and $class=ref($class); + + my($self)=$class->SUPER::new( yyversion => '1.05', + yystates => +[ + {#State 0 + ACTIONS => { + "-" => 1, + "~" => 3, + "&" => 4, + 'NUM' => 5, + 'TEXT' => 6, + "(" => 7, + "!" => 8, + "*" => 9, + 'VAR' => 10 + }, + GOTOS => { + 'exp' => 2, + 'var' => 11, + 'func' => 12, + 'possible_pointer' => 13 + } + }, + {#State 1 + ACTIONS => { + "-" => 1, + "~" => 3, + "&" => 4, + 'TEXT' => 6, + 'NUM' => 5, + "(" => 7, + "!" => 8, + "*" => 9, + 'VAR' => 10 + }, + GOTOS => { + 'exp' => 14, + 'var' => 11, + 'func' => 12, + 'possible_pointer' => 13 + } + }, + {#State 2 + ACTIONS => { + '' => 16, + "-" => 15, + "<" => 17, + "+" => 18, + "%" => 19, + "==" => 20, + "^" => 21, + "*" => 22, + ">>" => 23, + "!=" => 24, + "?" => 25, + "||" => 26, + "&&" => 27, + "&" => 28, + "/" => 29, + "|" => 30, + "<<" => 32, + "=>" => 31, + "<=" => 33, + ">" => 34 + } + }, + {#State 3 + ACTIONS => { + "-" => 1, + "~" => 3, + "&" => 4, + 'TEXT' => 6, + 'NUM' => 5, + "!" => 8, + "(" => 7, + "*" => 9, + 'VAR' => 10 + }, + GOTOS => { + 'exp' => 35, + 'var' => 11, + 'func' => 12, + 'possible_pointer' => 13 + } + }, + {#State 4 + ACTIONS => { + "-" => 1, + "~" => 3, + "&" => 4, + 'TEXT' => 6, + 'NUM' => 5, + "!" => 8, + "(" => 7, + "*" => 9, + 'VAR' => 10 + }, + GOTOS => { + 'exp' => 36, + 'var' => 11, + 'func' => 12, + 'possible_pointer' => 13 + } + }, + {#State 5 + DEFAULT => -1 + }, + {#State 6 + DEFAULT => -2 + }, + {#State 7 + ACTIONS => { + "-" => 1, + "~" => 3, + "&" => 4, + 'TEXT' => 6, + 'NUM' => 5, + "!" => 8, + "(" => 7, + "*" => 9, + 'VAR' => 10 + }, + GOTOS => { + 'exp' => 38, + 'var' => 37, + 'func' => 12, + 'possible_pointer' => 13 + } + }, + {#State 8 + ACTIONS => { + "-" => 1, + "~" => 3, + "&" => 4, + 'TEXT' => 6, + 'NUM' => 5, + "!" => 8, + "(" => 7, + "*" => 9, + 'VAR' => 10 + }, + GOTOS => { + 'exp' => 39, + 'var' => 11, + 'func' => 12, + 'possible_pointer' => 13 + } + }, + {#State 9 + ACTIONS => { + "*" => 9, + 'VAR' => 41 + }, + GOTOS => { + 'possible_pointer' => 40 + } + }, + {#State 10 + ACTIONS => { + "(" => 42 + }, + DEFAULT => -30 + }, + {#State 11 + ACTIONS => { + "->" => 43, + "." => 44 + }, + DEFAULT => -4 + }, + {#State 12 + DEFAULT => -3 + }, + {#State 13 + DEFAULT => -32 + }, + {#State 14 + ACTIONS => { + "^" => 21, + "=>" => 31, + "<=" => 33 + }, + DEFAULT => -26 + }, + {#State 15 + ACTIONS => { + "-" => 1, + "~" => 3, + "&" => 4, + 'TEXT' => 6, + 'NUM' => 5, + "!" => 8, + "(" => 7, + "*" => 9, + 'VAR' => 10 + }, + GOTOS => { + 'exp' => 45, + 'var' => 11, + 'func' => 12, + 'possible_pointer' => 13 + } + }, + {#State 16 + DEFAULT => 0 + }, + {#State 17 + ACTIONS => { + "-" => 1, + "~" => 3, + "&" => 4, + 'TEXT' => 6, + 'NUM' => 5, + "!" => 8, + "(" => 7, + "*" => 9, + 'VAR' => 10 + }, + GOTOS => { + 'exp' => 46, + 'var' => 11, + 'func' => 12, + 'possible_pointer' => 13 + } + }, + {#State 18 + ACTIONS => { + "-" => 1, + "~" => 3, + "&" => 4, + 'TEXT' => 6, + 'NUM' => 5, + "!" => 8, + "(" => 7, + "*" => 9, + 'VAR' => 10 + }, + GOTOS => { + 'exp' => 47, + 'var' => 11, + 'func' => 12, + 'possible_pointer' => 13 + } + }, + {#State 19 + ACTIONS => { + "-" => 1, + "~" => 3, + "&" => 4, + 'TEXT' => 6, + 'NUM' => 5, + "!" => 8, + "(" => 7, + "*" => 9, + 'VAR' => 10 + }, + GOTOS => { + 'exp' => 48, + 'var' => 11, + 'func' => 12, + 'possible_pointer' => 13 + } + }, + {#State 20 + ACTIONS => { + "-" => 1, + "~" => 3, + "&" => 4, + 'TEXT' => 6, + 'NUM' => 5, + "!" => 8, + "(" => 7, + "*" => 9, + 'VAR' => 10 + }, + GOTOS => { + 'exp' => 49, + 'var' => 11, + 'func' => 12, + 'possible_pointer' => 13 + } + }, + {#State 21 + ACTIONS => { + "-" => 1, + "~" => 3, + "&" => 4, + 'TEXT' => 6, + 'NUM' => 5, + "!" => 8, + "(" => 7, + "*" => 9, + 'VAR' => 10 + }, + GOTOS => { + 'exp' => 50, + 'var' => 11, + 'func' => 12, + 'possible_pointer' => 13 + } + }, + {#State 22 + ACTIONS => { + "-" => 1, + "~" => 3, + "&" => 4, + 'TEXT' => 6, + 'NUM' => 5, + "!" => 8, + "(" => 7, + "*" => 9, + 'VAR' => 10 + }, + GOTOS => { + 'exp' => 51, + 'var' => 11, + 'func' => 12, + 'possible_pointer' => 13 + } + }, + {#State 23 + ACTIONS => { + "-" => 1, + "~" => 3, + "&" => 4, + 'TEXT' => 6, + 'NUM' => 5, + "!" => 8, + "(" => 7, + "*" => 9, + 'VAR' => 10 + }, + GOTOS => { + 'exp' => 52, + 'var' => 11, + 'func' => 12, + 'possible_pointer' => 13 + } + }, + {#State 24 + ACTIONS => { + "-" => 1, + "~" => 3, + "&" => 4, + 'TEXT' => 6, + 'NUM' => 5, + "!" => 8, + "(" => 7, + "*" => 9, + 'VAR' => 10 + }, + GOTOS => { + 'exp' => 53, + 'var' => 11, + 'func' => 12, + 'possible_pointer' => 13 + } + }, + {#State 25 + ACTIONS => { + "-" => 1, + "~" => 3, + "&" => 4, + 'TEXT' => 6, + 'NUM' => 5, + "!" => 8, + "(" => 7, + "*" => 9, + 'VAR' => 10 + }, + GOTOS => { + 'exp' => 54, + 'var' => 11, + 'func' => 12, + 'possible_pointer' => 13 + } + }, + {#State 26 + ACTIONS => { + "-" => 1, + "~" => 3, + "&" => 4, + 'TEXT' => 6, + 'NUM' => 5, + "!" => 8, + "(" => 7, + "*" => 9, + 'VAR' => 10 + }, + GOTOS => { + 'exp' => 55, + 'var' => 11, + 'func' => 12, + 'possible_pointer' => 13 + } + }, + {#State 27 + ACTIONS => { + "-" => 1, + "~" => 3, + "&" => 4, + 'TEXT' => 6, + 'NUM' => 5, + "!" => 8, + "(" => 7, + "*" => 9, + 'VAR' => 10 + }, + GOTOS => { + 'exp' => 56, + 'var' => 11, + 'func' => 12, + 'possible_pointer' => 13 + } + }, + {#State 28 + ACTIONS => { + "-" => 1, + "~" => 3, + "&" => 4, + 'TEXT' => 6, + 'NUM' => 5, + "!" => 8, + "(" => 7, + "*" => 9, + 'VAR' => 10 + }, + GOTOS => { + 'exp' => 57, + 'var' => 11, + 'func' => 12, + 'possible_pointer' => 13 + } + }, + {#State 29 + ACTIONS => { + "-" => 1, + "~" => 3, + "&" => 4, + 'TEXT' => 6, + 'NUM' => 5, + "!" => 8, + "(" => 7, + "*" => 9, + 'VAR' => 10 + }, + GOTOS => { + 'exp' => 58, + 'var' => 11, + 'func' => 12, + 'possible_pointer' => 13 + } + }, + {#State 30 + ACTIONS => { + "-" => 1, + "~" => 3, + "&" => 4, + 'TEXT' => 6, + 'NUM' => 5, + "!" => 8, + "(" => 7, + "*" => 9, + 'VAR' => 10 + }, + GOTOS => { + 'exp' => 59, + 'var' => 11, + 'func' => 12, + 'possible_pointer' => 13 + } + }, + {#State 31 + ACTIONS => { + "-" => 1, + "~" => 3, + "&" => 4, + 'TEXT' => 6, + 'NUM' => 5, + "!" => 8, + "(" => 7, + "*" => 9, + 'VAR' => 10 + }, + GOTOS => { + 'exp' => 60, + 'var' => 11, + 'func' => 12, + 'possible_pointer' => 13 + } + }, + {#State 32 + ACTIONS => { + "-" => 1, + "~" => 3, + "&" => 4, + 'TEXT' => 6, + 'NUM' => 5, + "!" => 8, + "(" => 7, + "*" => 9, + 'VAR' => 10 + }, + GOTOS => { + 'exp' => 61, + 'var' => 11, + 'func' => 12, + 'possible_pointer' => 13 + } + }, + {#State 33 + ACTIONS => { + "-" => 1, + "~" => 3, + "&" => 4, + 'TEXT' => 6, + 'NUM' => 5, + "!" => 8, + "(" => 7, + "*" => 9, + 'VAR' => 10 + }, + GOTOS => { + 'exp' => 62, + 'var' => 11, + 'func' => 12, + 'possible_pointer' => 13 + } + }, + {#State 34 + ACTIONS => { + "-" => 1, + "~" => 3, + "&" => 4, + 'TEXT' => 6, + 'NUM' => 5, + "!" => 8, + "(" => 7, + "*" => 9, + 'VAR' => 10 + }, + GOTOS => { + 'exp' => 63, + 'var' => 11, + 'func' => 12, + 'possible_pointer' => 13 + } + }, + {#State 35 + ACTIONS => { + "^" => 21, + "=>" => 31, + "<=" => 33 + }, + DEFAULT => -5 + }, + {#State 36 + ACTIONS => { + "^" => 21, + "=>" => 31, + "<=" => 33 + }, + DEFAULT => -27 + }, + {#State 37 + ACTIONS => { + ")" => 64, + "->" => 43, + "." => 44 + }, + DEFAULT => -4 + }, + {#State 38 + ACTIONS => { + "-" => 15, + "<" => 17, + "+" => 18, + "%" => 19, + "==" => 20, + "^" => 21, + "*" => 22, + ")" => 65, + ">>" => 23, + "!=" => 24, + "?" => 25, + "&&" => 27, + "||" => 26, + "&" => 28, + "/" => 29, + "|" => 30, + "=>" => 31, + "<<" => 32, + "<=" => 33, + ">" => 34 + } + }, + {#State 39 + ACTIONS => { + "-" => 15, + "<" => 17, + "+" => 18, + "%" => 19, + "==" => 20, + "^" => 21, + "*" => 22, + ">>" => 23, + "!=" => 24, + "?" => 25, + "&&" => 27, + "||" => 26, + "&" => 28, + "/" => 29, + "|" => 30, + "=>" => 31, + "<<" => 32, + "<=" => 33, + ">" => 34 + }, + DEFAULT => -24 + }, + {#State 40 + DEFAULT => -31 + }, + {#State 41 + DEFAULT => -30 + }, + {#State 42 + ACTIONS => { + "-" => 1, + "~" => 3, + "&" => 4, + 'TEXT' => 6, + 'NUM' => 5, + "!" => 8, + "(" => 7, + "*" => 9, + 'VAR' => 10 + }, + DEFAULT => -37, + GOTOS => { + 'exp' => 69, + 'var' => 11, + 'args' => 66, + 'func' => 12, + 'opt_args' => 70, + 'exp_or_possible_pointer' => 67, + 'possible_pointer' => 68 + } + }, + {#State 43 + ACTIONS => { + 'VAR' => 71 + } + }, + {#State 44 + ACTIONS => { + 'VAR' => 72 + } + }, + {#State 45 + ACTIONS => { + "<" => 17, + "==" => 20, + "^" => 21, + ">>" => 23, + "!=" => 24, + "?" => 25, + "&&" => 27, + "||" => 26, + "&" => 28, + "|" => 30, + "=>" => 31, + "<<" => 32, + "<=" => 33, + ">" => 34 + }, + DEFAULT => -7 + }, + {#State 46 + ACTIONS => { + "==" => 20, + "^" => 21, + "!=" => 24, + "?" => 25, + "&&" => 27, + "||" => 26, + "&" => 28, + "|" => 30, + "=>" => 31, + "<=" => 33 + }, + DEFAULT => -10 + }, + {#State 47 + ACTIONS => { + "<" => 17, + "==" => 20, + "^" => 21, + ">>" => 23, + "!=" => 24, + "?" => 25, + "&&" => 27, + "||" => 26, + "&" => 28, + "|" => 30, + "=>" => 31, + "<<" => 32, + "<=" => 33, + ">" => 34 + }, + DEFAULT => -6 + }, + {#State 48 + ACTIONS => { + "-" => 15, + "<" => 17, + "+" => 18, + "==" => 20, + "^" => 21, + ">>" => 23, + "!=" => 24, + "?" => 25, + "&&" => 27, + "||" => 26, + "&" => 28, + "|" => 30, + "=>" => 31, + "<<" => 32, + "<=" => 33, + ">" => 34 + }, + DEFAULT => -9 + }, + {#State 49 + ACTIONS => { + "^" => 21, + "?" => 25, + "&&" => 27, + "||" => 26, + "&" => 28, + "|" => 30, + "=>" => 31, + "<=" => 33 + }, + DEFAULT => -13 + }, + {#State 50 + ACTIONS => { + "-" => 15, + "<" => 17, + "+" => 18, + "%" => 19, + "==" => 20, + "^" => 21, + "*" => 22, + ">>" => 23, + "!=" => 24, + "?" => 25, + "&&" => 27, + "||" => 26, + "&" => 28, + "/" => 29, + "|" => 30, + "=>" => 31, + "<<" => 32, + "<=" => 33, + ">" => 34 + }, + DEFAULT => -28 + }, + {#State 51 + ACTIONS => { + "-" => 15, + "<" => 17, + "+" => 18, + "==" => 20, + "^" => 21, + ">>" => 23, + "!=" => 24, + "?" => 25, + "&&" => 27, + "||" => 26, + "&" => 28, + "|" => 30, + "=>" => 31, + "<<" => 32, + "<=" => 33, + ">" => 34 + }, + DEFAULT => -8 + }, + {#State 52 + ACTIONS => { + "<" => 17, + "==" => 20, + "^" => 21, + "!=" => 24, + "?" => 25, + "&&" => 27, + "||" => 26, + "&" => 28, + "|" => 30, + "=>" => 31, + "<=" => 33, + ">" => 34 + }, + DEFAULT => -17 + }, + {#State 53 + ACTIONS => { + "^" => 21, + "?" => 25, + "&&" => 27, + "||" => 26, + "&" => 28, + "|" => 30, + "=>" => 31, + "<=" => 33 + }, + DEFAULT => -18 + }, + {#State 54 + ACTIONS => { + ":" => 73, + "-" => 15, + "<" => 17, + "+" => 18, + "%" => 19, + "==" => 20, + "^" => 21, + "*" => 22, + ">>" => 23, + "!=" => 24, + "?" => 25, + "&&" => 27, + "||" => 26, + "&" => 28, + "/" => 29, + "|" => 30, + "=>" => 31, + "<<" => 32, + "<=" => 33, + ">" => 34 + } + }, + {#State 55 + ACTIONS => { + "^" => 21, + "?" => 25, + "=>" => 31, + "<=" => 33 + }, + DEFAULT => -19 + }, + {#State 56 + ACTIONS => { + "^" => 21, + "?" => 25, + "||" => 26, + "=>" => 31, + "<=" => 33 + }, + DEFAULT => -20 + }, + {#State 57 + ACTIONS => { + "^" => 21, + "?" => 25, + "&&" => 27, + "||" => 26, + "|" => 30, + "=>" => 31, + "<=" => 33 + }, + DEFAULT => -21 + }, + {#State 58 + ACTIONS => { + "-" => 15, + "<" => 17, + "+" => 18, + "==" => 20, + "^" => 21, + ">>" => 23, + "!=" => 24, + "?" => 25, + "&&" => 27, + "||" => 26, + "&" => 28, + "|" => 30, + "=>" => 31, + "<<" => 32, + "<=" => 33, + ">" => 34 + }, + DEFAULT => -25 + }, + {#State 59 + ACTIONS => { + "^" => 21, + "?" => 25, + "&&" => 27, + "||" => 26, + "=>" => 31, + "<=" => 33 + }, + DEFAULT => -12 + }, + {#State 60 + ACTIONS => { + "-" => 15, + "<" => 17, + "+" => 18, + "%" => 19, + "==" => 20, + "^" => 21, + "*" => 22, + ">>" => 23, + "!=" => 24, + "?" => 25, + "&&" => 27, + "||" => 26, + "&" => 28, + "/" => 29, + "|" => 30, + "=>" => 31, + "<<" => 32, + "<=" => 33, + ">" => 34 + }, + DEFAULT => -15 + }, + {#State 61 + ACTIONS => { + "<" => 17, + "==" => 20, + "^" => 21, + "!=" => 24, + "?" => 25, + "&&" => 27, + "||" => 26, + "&" => 28, + "|" => 30, + "=>" => 31, + "<=" => 33, + ">" => 34 + }, + DEFAULT => -16 + }, + {#State 62 + ACTIONS => { + "-" => 15, + "<" => 17, + "+" => 18, + "%" => 19, + "==" => 20, + "^" => 21, + "*" => 22, + ">>" => 23, + "!=" => 24, + "?" => 25, + "&&" => 27, + "||" => 26, + "&" => 28, + "/" => 29, + "|" => 30, + "=>" => 31, + "<<" => 32, + "<=" => 33, + ">" => 34 + }, + DEFAULT => -14 + }, + {#State 63 + ACTIONS => { + "==" => 20, + "^" => 21, + "!=" => 24, + "?" => 25, + "&&" => 27, + "||" => 26, + "&" => 28, + "|" => 30, + "=>" => 31, + "<=" => 33 + }, + DEFAULT => -11 + }, + {#State 64 + DEFAULT => -34 + }, + {#State 65 + DEFAULT => -29 + }, + {#State 66 + DEFAULT => -38 + }, + {#State 67 + ACTIONS => { + "," => 74 + }, + DEFAULT => -41 + }, + {#State 68 + DEFAULT => -32 + }, + {#State 69 + ACTIONS => { + "-" => 15, + "<" => 17, + "+" => 18, + "%" => 19, + "==" => 20, + "^" => 21, + "*" => 22, + ">>" => 23, + "!=" => 24, + "?" => 25, + "&&" => 27, + "||" => 26, + "&" => 28, + "/" => 29, + "|" => 30, + "=>" => 31, + "<<" => 32, + "<=" => 33, + ">" => 34 + }, + DEFAULT => -39 + }, + {#State 70 + ACTIONS => { + ")" => 75 + } + }, + {#State 71 + DEFAULT => -35 + }, + {#State 72 + DEFAULT => -33 + }, + {#State 73 + ACTIONS => { + "-" => 1, + "~" => 3, + "&" => 4, + 'TEXT' => 6, + 'NUM' => 5, + "!" => 8, + "(" => 7, + "*" => 9, + 'VAR' => 10 + }, + GOTOS => { + 'exp' => 76, + 'var' => 11, + 'func' => 12, + 'possible_pointer' => 13 + } + }, + {#State 74 + ACTIONS => { + "-" => 1, + "~" => 3, + "&" => 4, + 'TEXT' => 6, + 'NUM' => 5, + "!" => 8, + "(" => 7, + "*" => 9, + 'VAR' => 10 + }, + GOTOS => { + 'exp' => 69, + 'var' => 11, + 'args' => 77, + 'func' => 12, + 'exp_or_possible_pointer' => 67, + 'possible_pointer' => 68 + } + }, + {#State 75 + DEFAULT => -36 + }, + {#State 76 + ACTIONS => { + "^" => 21, + "=>" => 31, + "<=" => 33 + }, + DEFAULT => -22 + }, + {#State 77 + DEFAULT => -42 + } +], + yyrules => +[ + [#Rule 0 + '$start', 2, undef + ], + [#Rule 1 + 'exp', 1, undef + ], + [#Rule 2 + 'exp', 1, +sub +#line 22 "pidl/expr.yp" +{ "\"$_[1]\"" } + ], + [#Rule 3 + 'exp', 1, undef + ], + [#Rule 4 + 'exp', 1, undef + ], + [#Rule 5 + 'exp', 2, +sub +#line 25 "pidl/expr.yp" +{ "~$_[2]" } + ], + [#Rule 6 + 'exp', 3, +sub +#line 26 "pidl/expr.yp" +{ "$_[1] + $_[3]" } + ], + [#Rule 7 + 'exp', 3, +sub +#line 27 "pidl/expr.yp" +{ "$_[1] - $_[3]" } + ], + [#Rule 8 + 'exp', 3, +sub +#line 28 "pidl/expr.yp" +{ "$_[1] * $_[3]" } + ], + [#Rule 9 + 'exp', 3, +sub +#line 29 "pidl/expr.yp" +{ "$_[1] % $_[3]" } + ], + [#Rule 10 + 'exp', 3, +sub +#line 30 "pidl/expr.yp" +{ "$_[1] < $_[3]" } + ], + [#Rule 11 + 'exp', 3, +sub +#line 31 "pidl/expr.yp" +{ "$_[1] > $_[3]" } + ], + [#Rule 12 + 'exp', 3, +sub +#line 32 "pidl/expr.yp" +{ "$_[1] | $_[3]" } + ], + [#Rule 13 + 'exp', 3, +sub +#line 33 "pidl/expr.yp" +{ "$_[1] == $_[3]" } + ], + [#Rule 14 + 'exp', 3, +sub +#line 34 "pidl/expr.yp" +{ "$_[1] <= $_[3]" } + ], + [#Rule 15 + 'exp', 3, +sub +#line 35 "pidl/expr.yp" +{ "$_[1] => $_[3]" } + ], + [#Rule 16 + 'exp', 3, +sub +#line 36 "pidl/expr.yp" +{ "$_[1] << $_[3]" } + ], + [#Rule 17 + 'exp', 3, +sub +#line 37 "pidl/expr.yp" +{ "$_[1] >> $_[3]" } + ], + [#Rule 18 + 'exp', 3, +sub +#line 38 "pidl/expr.yp" +{ "$_[1] != $_[3]" } + ], + [#Rule 19 + 'exp', 3, +sub +#line 39 "pidl/expr.yp" +{ "$_[1] || $_[3]" } + ], + [#Rule 20 + 'exp', 3, +sub +#line 40 "pidl/expr.yp" +{ "$_[1] && $_[3]" } + ], + [#Rule 21 + 'exp', 3, +sub +#line 41 "pidl/expr.yp" +{ "$_[1] & $_[3]" } + ], + [#Rule 22 + 'exp', 5, +sub +#line 42 "pidl/expr.yp" +{ "$_[1]?$_[3]:$_[5]" } + ], + [#Rule 23 + 'exp', 2, +sub +#line 43 "pidl/expr.yp" +{ "~$_[1]" } + ], + [#Rule 24 + 'exp', 2, +sub +#line 44 "pidl/expr.yp" +{ "not $_[1]" } + ], + [#Rule 25 + 'exp', 3, +sub +#line 45 "pidl/expr.yp" +{ "$_[1] / $_[3]" } + ], + [#Rule 26 + 'exp', 2, +sub +#line 46 "pidl/expr.yp" +{ "-$_[2]" } + ], + [#Rule 27 + 'exp', 2, +sub +#line 47 "pidl/expr.yp" +{ "&$_[2]" } + ], + [#Rule 28 + 'exp', 3, +sub +#line 48 "pidl/expr.yp" +{ "$_[1]^$_[3]" } + ], + [#Rule 29 + 'exp', 3, +sub +#line 49 "pidl/expr.yp" +{ "($_[2])" } + ], + [#Rule 30 + 'possible_pointer', 1, +sub +#line 53 "pidl/expr.yp" +{ $_[0]->_Lookup($_[1]) } + ], + [#Rule 31 + 'possible_pointer', 2, +sub +#line 54 "pidl/expr.yp" +{ $_[0]->_Dereference($_[2]); "*$_[2]" } + ], + [#Rule 32 + 'var', 1, +sub +#line 57 "pidl/expr.yp" +{ $_[0]->_Use($_[1]) } + ], + [#Rule 33 + 'var', 3, +sub +#line 58 "pidl/expr.yp" +{ $_[0]->_Use("$_[1].$_[3]") } + ], + [#Rule 34 + 'var', 3, +sub +#line 59 "pidl/expr.yp" +{ "($_[2])" } + ], + [#Rule 35 + 'var', 3, +sub +#line 60 "pidl/expr.yp" +{ $_[0]->_Use("*$_[1]"); $_[1]."->".$_[3] } + ], + [#Rule 36 + 'func', 4, +sub +#line 64 "pidl/expr.yp" +{ "$_[1]($_[3])" } + ], + [#Rule 37 + 'opt_args', 0, +sub +#line 65 "pidl/expr.yp" +{ "" } + ], + [#Rule 38 + 'opt_args', 1, undef + ], + [#Rule 39 + 'exp_or_possible_pointer', 1, undef + ], + [#Rule 40 + 'exp_or_possible_pointer', 1, undef + ], + [#Rule 41 + 'args', 1, undef + ], + [#Rule 42 + 'args', 3, +sub +#line 68 "pidl/expr.yp" +{ "$_[1], $_[3]" } + ] +], + @_); + bless($self,$class); +} + +#line 71 "pidl/expr.yp" + + +package Parse::Pidl::Expr; + +sub _Lexer { + my($parser)=shift; + + $parser->YYData->{INPUT}=~s/^[ \t]//; + + for ($parser->YYData->{INPUT}) { + if (s/^(0x[0-9A-Fa-f]+)//) { + $parser->YYData->{LAST_TOKEN} = $1; + return('NUM',$1); + } + if (s/^([0-9]+(?:\.[0-9]+)?)//) { + $parser->YYData->{LAST_TOKEN} = $1; + return('NUM',$1); + } + if (s/^([A-Za-z_][A-Za-z0-9_]*)//) { + $parser->YYData->{LAST_TOKEN} = $1; + return('VAR',$1); + } + if (s/^\"(.*?)\"//) { + $parser->YYData->{LAST_TOKEN} = $1; + return('TEXT',$1); + } + if (s/^(==|!=|<=|>=|->|\|\||<<|>>|&&)//s) { + $parser->YYData->{LAST_TOKEN} = $1; + return($1,$1); + } + if (s/^(.)//s) { + $parser->YYData->{LAST_TOKEN} = $1; + return($1,$1); + } + } +} + +sub _Use($$) +{ + my ($self, $x) = @_; + if (defined($self->YYData->{USE})) { + return $self->YYData->{USE}->($x); + } + return $x; +} + +sub _Lookup($$) +{ + my ($self, $x) = @_; + return $self->YYData->{LOOKUP}->($x); +} + +sub _Dereference($$) +{ + my ($self, $x) = @_; + if (defined($self->YYData->{DEREFERENCE})) { + $self->YYData->{DEREFERENCE}->($x); + } +} + +sub _Error($) +{ + my ($self) = @_; + if (defined($self->YYData->{LAST_TOKEN})) { + $self->YYData->{ERROR}->("Parse error in `".$self->YYData->{FULL_INPUT}."' near `". $self->YYData->{LAST_TOKEN} . "'"); + } else { + $self->YYData->{ERROR}->("Parse error in `".$self->YYData->{FULL_INPUT}."'"); + } +} + +sub Run { + my($self, $data, $error, $lookup, $deref, $use) = @_; + $self->YYData->{FULL_INPUT} = $data; + $self->YYData->{INPUT} = $data; + $self->YYData->{LOOKUP} = $lookup; + $self->YYData->{DEREFERENCE} = $deref; + $self->YYData->{ERROR} = $error; + $self->YYData->{USE} = $use; + return $self->YYParse( yylex => \&_Lexer, yyerror => \&_Error); +} + +1; diff --git a/source/pidl/lib/Parse/Pidl/IDL.pm b/source/pidl/lib/Parse/Pidl/IDL.pm new file mode 100644 index 00000000000..ba9cae3a207 --- /dev/null +++ b/source/pidl/lib/Parse/Pidl/IDL.pm @@ -0,0 +1,2504 @@ +#################################################################### +# +# This file was generated using Parse::Yapp version 1.05. +# +# Don't edit this file, use source file instead. +# +# ANY CHANGE MADE HERE WILL BE LOST ! +# +#################################################################### +package Parse::Pidl::IDL; +use vars qw ( @ISA ); +use strict; + +@ISA= qw ( Parse::Yapp::Driver ); +use Parse::Yapp::Driver; + + + +sub new { + my($class)=shift; + ref($class) + and $class=ref($class); + + my($self)=$class->SUPER::new( yyversion => '1.05', + yystates => +[ + {#State 0 + DEFAULT => -1, + GOTOS => { + 'idl' => 1 + } + }, + {#State 1 + ACTIONS => { + '' => 2, + "cpp_quote" => 3, + "importlib" => 4, + "import" => 7, + "include" => 13 + }, + DEFAULT => -83, + GOTOS => { + 'cpp_quote' => 11, + 'importlib' => 10, + 'interface' => 9, + 'include' => 5, + 'coclass' => 12, + 'import' => 8, + 'property_list' => 6 + } + }, + {#State 2 + DEFAULT => 0 + }, + {#State 3 + ACTIONS => { + "(" => 14 + } + }, + {#State 4 + ACTIONS => { + 'TEXT' => 16 + }, + GOTOS => { + 'commalist' => 15, + 'text' => 17 + } + }, + {#State 5 + DEFAULT => -5 + }, + {#State 6 + ACTIONS => { + "coclass" => 18, + "[" => 20, + "interface" => 19 + } + }, + {#State 7 + ACTIONS => { + 'TEXT' => 16 + }, + GOTOS => { + 'commalist' => 21, + 'text' => 17 + } + }, + {#State 8 + DEFAULT => -4 + }, + {#State 9 + DEFAULT => -2 + }, + {#State 10 + DEFAULT => -6 + }, + {#State 11 + DEFAULT => -7 + }, + {#State 12 + DEFAULT => -3 + }, + {#State 13 + ACTIONS => { + 'TEXT' => 16 + }, + GOTOS => { + 'commalist' => 22, + 'text' => 17 + } + }, + {#State 14 + ACTIONS => { + 'TEXT' => 16 + }, + GOTOS => { + 'text' => 23 + } + }, + {#State 15 + ACTIONS => { + ";" => 24, + "," => 25 + } + }, + {#State 16 + DEFAULT => -114 + }, + {#State 17 + DEFAULT => -11 + }, + {#State 18 + ACTIONS => { + 'IDENTIFIER' => 26 + }, + GOTOS => { + 'identifier' => 27 + } + }, + {#State 19 + ACTIONS => { + 'IDENTIFIER' => 26 + }, + GOTOS => { + 'identifier' => 28 + } + }, + {#State 20 + ACTIONS => { + 'IDENTIFIER' => 26 + }, + GOTOS => { + 'identifier' => 30, + 'property' => 31, + 'properties' => 29 + } + }, + {#State 21 + ACTIONS => { + ";" => 32, + "," => 25 + } + }, + {#State 22 + ACTIONS => { + ";" => 33, + "," => 25 + } + }, + {#State 23 + ACTIONS => { + ")" => 34 + } + }, + {#State 24 + DEFAULT => -10 + }, + {#State 25 + ACTIONS => { + 'TEXT' => 16 + }, + GOTOS => { + 'text' => 35 + } + }, + {#State 26 + DEFAULT => -110 + }, + {#State 27 + ACTIONS => { + "{" => 36 + } + }, + {#State 28 + ACTIONS => { + "{" => 37 + } + }, + {#State 29 + ACTIONS => { + "," => 38, + "]" => 39 + } + }, + {#State 30 + ACTIONS => { + "(" => 40 + }, + DEFAULT => -87 + }, + {#State 31 + DEFAULT => -85 + }, + {#State 32 + DEFAULT => -8 + }, + {#State 33 + DEFAULT => -9 + }, + {#State 34 + DEFAULT => -17 + }, + {#State 35 + DEFAULT => -12 + }, + {#State 36 + DEFAULT => -14, + GOTOS => { + 'interface_names' => 41 + } + }, + {#State 37 + ACTIONS => { + "const" => 51 + }, + DEFAULT => -83, + GOTOS => { + 'typedecl' => 42, + 'function' => 43, + 'definitions' => 45, + 'bitmap' => 44, + 'definition' => 48, + 'property_list' => 47, + 'usertype' => 46, + 'const' => 50, + 'struct' => 49, + 'typedef' => 53, + 'enum' => 52, + 'union' => 54 + } + }, + {#State 38 + ACTIONS => { + 'IDENTIFIER' => 26 + }, + GOTOS => { + 'identifier' => 30, + 'property' => 55 + } + }, + {#State 39 + DEFAULT => -84 + }, + {#State 40 + ACTIONS => { + 'CONSTANT' => 58, + 'TEXT' => 16, + 'IDENTIFIER' => 26 + }, + DEFAULT => -91, + GOTOS => { + 'identifier' => 60, + 'text' => 61, + 'anytext' => 56, + 'constant' => 57, + 'commalisttext' => 59 + } + }, + {#State 41 + ACTIONS => { + "}" => 62, + "interface" => 63 + } + }, + {#State 42 + DEFAULT => -23 + }, + {#State 43 + DEFAULT => -20 + }, + {#State 44 + DEFAULT => -31 + }, + {#State 45 + ACTIONS => { + "}" => 64, + "const" => 51 + }, + DEFAULT => -83, + GOTOS => { + 'typedecl' => 42, + 'function' => 43, + 'bitmap' => 44, + 'definition' => 65, + 'property_list' => 47, + 'usertype' => 46, + 'struct' => 49, + 'const' => 50, + 'typedef' => 53, + 'enum' => 52, + 'union' => 54 + } + }, + {#State 46 + ACTIONS => { + ";" => 66 + } + }, + {#State 47 + ACTIONS => { + "typedef" => 67, + 'IDENTIFIER' => 26, + "signed" => 75, + "union" => 68, + "enum" => 77, + "bitmap" => 78, + 'void' => 69, + "unsigned" => 79, + "[" => 20, + "struct" => 74 + }, + GOTOS => { + 'existingtype' => 76, + 'bitmap' => 44, + 'usertype' => 71, + 'property_list' => 70, + 'identifier' => 72, + 'struct' => 49, + 'enum' => 52, + 'type' => 80, + 'union' => 54, + 'sign' => 73 + } + }, + {#State 48 + DEFAULT => -18 + }, + {#State 49 + DEFAULT => -28 + }, + {#State 50 + DEFAULT => -21 + }, + {#State 51 + ACTIONS => { + 'IDENTIFIER' => 26 + }, + GOTOS => { + 'identifier' => 81 + } + }, + {#State 52 + DEFAULT => -30 + }, + {#State 53 + DEFAULT => -22 + }, + {#State 54 + DEFAULT => -29 + }, + {#State 55 + DEFAULT => -86 + }, + {#State 56 + ACTIONS => { + "-" => 83, + ":" => 82, + "<" => 84, + "+" => 86, + "~" => 85, + "*" => 87, + "?" => 88, + "{" => 89, + "&" => 90, + "/" => 91, + "=" => 92, + "(" => 93, + "|" => 94, + "." => 95, + ">" => 96 + }, + DEFAULT => -89 + }, + {#State 57 + DEFAULT => -93 + }, + {#State 58 + DEFAULT => -113 + }, + {#State 59 + ACTIONS => { + "," => 97, + ")" => 98 + } + }, + {#State 60 + DEFAULT => -92 + }, + {#State 61 + DEFAULT => -94 + }, + {#State 62 + ACTIONS => { + ";" => 99 + }, + DEFAULT => -115, + GOTOS => { + 'optional_semicolon' => 100 + } + }, + {#State 63 + ACTIONS => { + 'IDENTIFIER' => 26 + }, + GOTOS => { + 'identifier' => 101 + } + }, + {#State 64 + ACTIONS => { + ";" => 99 + }, + DEFAULT => -115, + GOTOS => { + 'optional_semicolon' => 102 + } + }, + {#State 65 + DEFAULT => -19 + }, + {#State 66 + DEFAULT => -32 + }, + {#State 67 + ACTIONS => { + 'IDENTIFIER' => 26, + "signed" => 75, + 'void' => 69, + "unsigned" => 79 + }, + DEFAULT => -83, + GOTOS => { + 'existingtype' => 76, + 'bitmap' => 44, + 'usertype' => 71, + 'property_list' => 70, + 'identifier' => 72, + 'struct' => 49, + 'enum' => 52, + 'type' => 103, + 'union' => 54, + 'sign' => 73 + } + }, + {#State 68 + ACTIONS => { + 'IDENTIFIER' => 104 + }, + DEFAULT => -112, + GOTOS => { + 'optional_identifier' => 105 + } + }, + {#State 69 + DEFAULT => -39 + }, + {#State 70 + ACTIONS => { + "union" => 68, + "enum" => 77, + "bitmap" => 78, + "[" => 20, + "struct" => 74 + } + }, + {#State 71 + DEFAULT => -37 + }, + {#State 72 + DEFAULT => -36 + }, + {#State 73 + ACTIONS => { + 'IDENTIFIER' => 26 + }, + GOTOS => { + 'identifier' => 106 + } + }, + {#State 74 + ACTIONS => { + 'IDENTIFIER' => 104 + }, + DEFAULT => -112, + GOTOS => { + 'optional_identifier' => 107 + } + }, + {#State 75 + DEFAULT => -33 + }, + {#State 76 + DEFAULT => -38 + }, + {#State 77 + ACTIONS => { + 'IDENTIFIER' => 104 + }, + DEFAULT => -112, + GOTOS => { + 'optional_identifier' => 108 + } + }, + {#State 78 + ACTIONS => { + 'IDENTIFIER' => 104 + }, + DEFAULT => -112, + GOTOS => { + 'optional_identifier' => 109 + } + }, + {#State 79 + DEFAULT => -34 + }, + {#State 80 + ACTIONS => { + 'IDENTIFIER' => 26 + }, + GOTOS => { + 'identifier' => 110 + } + }, + {#State 81 + DEFAULT => -72, + GOTOS => { + 'pointers' => 111 + } + }, + {#State 82 + ACTIONS => { + 'CONSTANT' => 58, + 'TEXT' => 16, + 'IDENTIFIER' => 26 + }, + DEFAULT => -91, + GOTOS => { + 'identifier' => 60, + 'anytext' => 112, + 'text' => 61, + 'constant' => 57 + } + }, + {#State 83 + ACTIONS => { + 'CONSTANT' => 58, + 'TEXT' => 16, + 'IDENTIFIER' => 26 + }, + DEFAULT => -91, + GOTOS => { + 'identifier' => 60, + 'anytext' => 113, + 'text' => 61, + 'constant' => 57 + } + }, + {#State 84 + ACTIONS => { + 'CONSTANT' => 58, + 'TEXT' => 16, + 'IDENTIFIER' => 26 + }, + DEFAULT => -91, + GOTOS => { + 'identifier' => 60, + 'anytext' => 114, + 'text' => 61, + 'constant' => 57 + } + }, + {#State 85 + ACTIONS => { + 'CONSTANT' => 58, + 'TEXT' => 16, + 'IDENTIFIER' => 26 + }, + DEFAULT => -91, + GOTOS => { + 'identifier' => 60, + 'anytext' => 115, + 'text' => 61, + 'constant' => 57 + } + }, + {#State 86 + ACTIONS => { + 'CONSTANT' => 58, + 'TEXT' => 16, + 'IDENTIFIER' => 26 + }, + DEFAULT => -91, + GOTOS => { + 'identifier' => 60, + 'anytext' => 116, + 'text' => 61, + 'constant' => 57 + } + }, + {#State 87 + ACTIONS => { + 'CONSTANT' => 58, + 'TEXT' => 16, + 'IDENTIFIER' => 26 + }, + DEFAULT => -91, + GOTOS => { + 'identifier' => 60, + 'anytext' => 117, + 'text' => 61, + 'constant' => 57 + } + }, + {#State 88 + ACTIONS => { + 'CONSTANT' => 58, + 'TEXT' => 16, + 'IDENTIFIER' => 26 + }, + DEFAULT => -91, + GOTOS => { + 'identifier' => 60, + 'anytext' => 118, + 'text' => 61, + 'constant' => 57 + } + }, + {#State 89 + ACTIONS => { + 'CONSTANT' => 58, + 'TEXT' => 16, + 'IDENTIFIER' => 26 + }, + DEFAULT => -91, + GOTOS => { + 'identifier' => 60, + 'anytext' => 56, + 'text' => 61, + 'constant' => 57, + 'commalisttext' => 119 + } + }, + {#State 90 + ACTIONS => { + 'CONSTANT' => 58, + 'TEXT' => 16, + 'IDENTIFIER' => 26 + }, + DEFAULT => -91, + GOTOS => { + 'identifier' => 60, + 'anytext' => 120, + 'text' => 61, + 'constant' => 57 + } + }, + {#State 91 + ACTIONS => { + 'CONSTANT' => 58, + 'TEXT' => 16, + 'IDENTIFIER' => 26 + }, + DEFAULT => -91, + GOTOS => { + 'identifier' => 60, + 'anytext' => 121, + 'text' => 61, + 'constant' => 57 + } + }, + {#State 92 + ACTIONS => { + 'CONSTANT' => 58, + 'TEXT' => 16, + 'IDENTIFIER' => 26 + }, + DEFAULT => -91, + GOTOS => { + 'identifier' => 60, + 'anytext' => 122, + 'text' => 61, + 'constant' => 57 + } + }, + {#State 93 + ACTIONS => { + 'CONSTANT' => 58, + 'TEXT' => 16, + 'IDENTIFIER' => 26 + }, + DEFAULT => -91, + GOTOS => { + 'identifier' => 60, + 'anytext' => 56, + 'text' => 61, + 'constant' => 57, + 'commalisttext' => 123 + } + }, + {#State 94 + ACTIONS => { + 'CONSTANT' => 58, + 'TEXT' => 16, + 'IDENTIFIER' => 26 + }, + DEFAULT => -91, + GOTOS => { + 'identifier' => 60, + 'anytext' => 124, + 'text' => 61, + 'constant' => 57 + } + }, + {#State 95 + ACTIONS => { + 'CONSTANT' => 58, + 'TEXT' => 16, + 'IDENTIFIER' => 26 + }, + DEFAULT => -91, + GOTOS => { + 'identifier' => 60, + 'anytext' => 125, + 'text' => 61, + 'constant' => 57 + } + }, + {#State 96 + ACTIONS => { + 'CONSTANT' => 58, + 'TEXT' => 16, + 'IDENTIFIER' => 26 + }, + DEFAULT => -91, + GOTOS => { + 'identifier' => 60, + 'anytext' => 126, + 'text' => 61, + 'constant' => 57 + } + }, + {#State 97 + ACTIONS => { + 'CONSTANT' => 58, + 'TEXT' => 16, + 'IDENTIFIER' => 26 + }, + DEFAULT => -91, + GOTOS => { + 'identifier' => 60, + 'anytext' => 127, + 'text' => 61, + 'constant' => 57 + } + }, + {#State 98 + DEFAULT => -88 + }, + {#State 99 + DEFAULT => -116 + }, + {#State 100 + DEFAULT => -13 + }, + {#State 101 + ACTIONS => { + ";" => 128 + } + }, + {#State 102 + DEFAULT => -16 + }, + {#State 103 + ACTIONS => { + 'IDENTIFIER' => 26 + }, + GOTOS => { + 'identifier' => 129 + } + }, + {#State 104 + DEFAULT => -111 + }, + {#State 105 + ACTIONS => { + "{" => 131 + }, + DEFAULT => -68, + GOTOS => { + 'union_body' => 132, + 'opt_union_body' => 130 + } + }, + {#State 106 + DEFAULT => -35 + }, + {#State 107 + ACTIONS => { + "{" => 134 + }, + DEFAULT => -58, + GOTOS => { + 'struct_body' => 133, + 'opt_struct_body' => 135 + } + }, + {#State 108 + ACTIONS => { + "{" => 136 + }, + DEFAULT => -41, + GOTOS => { + 'opt_enum_body' => 138, + 'enum_body' => 137 + } + }, + {#State 109 + ACTIONS => { + "{" => 140 + }, + DEFAULT => -49, + GOTOS => { + 'bitmap_body' => 141, + 'opt_bitmap_body' => 139 + } + }, + {#State 110 + ACTIONS => { + "(" => 142 + } + }, + {#State 111 + ACTIONS => { + 'IDENTIFIER' => 26, + "*" => 144 + }, + GOTOS => { + 'identifier' => 143 + } + }, + {#State 112 + ACTIONS => { + "-" => 83, + ":" => 82, + "<" => 84, + "+" => 86, + "~" => 85, + "*" => 87, + "?" => 88, + "{" => 89, + "&" => 90, + "/" => 91, + "=" => 92, + "(" => 93, + "|" => 94, + "." => 95, + ">" => 96 + }, + DEFAULT => -104 + }, + {#State 113 + ACTIONS => { + ":" => 82, + "<" => 84, + "~" => 85, + "?" => 88, + "{" => 89, + "=" => 92 + }, + DEFAULT => -95 + }, + {#State 114 + ACTIONS => { + "-" => 83, + ":" => 82, + "<" => 84, + "+" => 86, + "~" => 85, + "*" => 87, + "?" => 88, + "{" => 89, + "&" => 90, + "/" => 91, + "=" => 92, + "(" => 93, + "|" => 94, + "." => 95, + ">" => 96 + }, + DEFAULT => -99 + }, + {#State 115 + ACTIONS => { + "-" => 83, + ":" => 82, + "<" => 84, + "+" => 86, + "~" => 85, + "*" => 87, + "?" => 88, + "{" => 89, + "&" => 90, + "/" => 91, + "=" => 92, + "(" => 93, + "|" => 94, + "." => 95, + ">" => 96 + }, + DEFAULT => -107 + }, + {#State 116 + ACTIONS => { + ":" => 82, + "<" => 84, + "~" => 85, + "?" => 88, + "{" => 89, + "=" => 92 + }, + DEFAULT => -106 + }, + {#State 117 + ACTIONS => { + ":" => 82, + "<" => 84, + "~" => 85, + "?" => 88, + "{" => 89, + "=" => 92 + }, + DEFAULT => -97 + }, + {#State 118 + ACTIONS => { + "-" => 83, + ":" => 82, + "<" => 84, + "+" => 86, + "~" => 85, + "*" => 87, + "?" => 88, + "{" => 89, + "&" => 90, + "/" => 91, + "=" => 92, + "(" => 93, + "|" => 94, + "." => 95, + ">" => 96 + }, + DEFAULT => -103 + }, + {#State 119 + ACTIONS => { + "}" => 145, + "," => 97 + } + }, + {#State 120 + ACTIONS => { + ":" => 82, + "<" => 84, + "~" => 85, + "?" => 88, + "{" => 89, + "=" => 92 + }, + DEFAULT => -101 + }, + {#State 121 + ACTIONS => { + ":" => 82, + "<" => 84, + "~" => 85, + "?" => 88, + "{" => 89, + "=" => 92 + }, + DEFAULT => -102 + }, + {#State 122 + ACTIONS => { + "-" => 83, + ":" => 82, + "<" => 84, + "+" => 86, + "~" => 85, + "*" => 87, + "?" => 88, + "{" => 89, + "&" => 90, + "/" => 91, + "=" => 92, + "(" => 93, + "|" => 94, + "." => 95, + ">" => 96 + }, + DEFAULT => -105 + }, + {#State 123 + ACTIONS => { + "," => 97, + ")" => 146 + } + }, + {#State 124 + ACTIONS => { + ":" => 82, + "<" => 84, + "~" => 85, + "?" => 88, + "{" => 89, + "=" => 92 + }, + DEFAULT => -100 + }, + {#State 125 + ACTIONS => { + ":" => 82, + "<" => 84, + "~" => 85, + "?" => 88, + "{" => 89, + "=" => 92 + }, + DEFAULT => -96 + }, + {#State 126 + ACTIONS => { + ":" => 82, + "<" => 84, + "~" => 85, + "?" => 88, + "{" => 89, + "=" => 92 + }, + DEFAULT => -98 + }, + {#State 127 + ACTIONS => { + "-" => 83, + ":" => 82, + "<" => 84, + "+" => 86, + "~" => 85, + "*" => 87, + "?" => 88, + "{" => 89, + "&" => 90, + "/" => 91, + "=" => 92, + "(" => 93, + "|" => 94, + "." => 95, + ">" => 96 + }, + DEFAULT => -90 + }, + {#State 128 + DEFAULT => -15 + }, + {#State 129 + ACTIONS => { + "[" => 147 + }, + DEFAULT => -80, + GOTOS => { + 'array_len' => 148 + } + }, + {#State 130 + DEFAULT => -70 + }, + {#State 131 + DEFAULT => -65, + GOTOS => { + 'union_elements' => 149 + } + }, + {#State 132 + DEFAULT => -69 + }, + {#State 133 + DEFAULT => -59 + }, + {#State 134 + DEFAULT => -74, + GOTOS => { + 'element_list1' => 150 + } + }, + {#State 135 + DEFAULT => -60 + }, + {#State 136 + ACTIONS => { + 'IDENTIFIER' => 26 + }, + GOTOS => { + 'identifier' => 151, + 'enum_element' => 152, + 'enum_elements' => 153 + } + }, + {#State 137 + DEFAULT => -42 + }, + {#State 138 + DEFAULT => -43 + }, + {#State 139 + DEFAULT => -51 + }, + {#State 140 + ACTIONS => { + 'IDENTIFIER' => 26 + }, + DEFAULT => -54, + GOTOS => { + 'identifier' => 156, + 'bitmap_element' => 155, + 'bitmap_elements' => 154, + 'opt_bitmap_elements' => 157 + } + }, + {#State 141 + DEFAULT => -50 + }, + {#State 142 + ACTIONS => { + "," => -76, + "void" => 161, + ")" => -76 + }, + DEFAULT => -83, + GOTOS => { + 'base_element' => 158, + 'element_list2' => 160, + 'property_list' => 159 + } + }, + {#State 143 + ACTIONS => { + "[" => 147, + "=" => 163 + }, + GOTOS => { + 'array_len' => 162 + } + }, + {#State 144 + DEFAULT => -73 + }, + {#State 145 + ACTIONS => { + 'CONSTANT' => 58, + 'TEXT' => 16, + 'IDENTIFIER' => 26 + }, + DEFAULT => -91, + GOTOS => { + 'identifier' => 60, + 'anytext' => 164, + 'text' => 61, + 'constant' => 57 + } + }, + {#State 146 + ACTIONS => { + 'CONSTANT' => 58, + 'TEXT' => 16, + 'IDENTIFIER' => 26 + }, + DEFAULT => -91, + GOTOS => { + 'identifier' => 60, + 'anytext' => 165, + 'text' => 61, + 'constant' => 57 + } + }, + {#State 147 + ACTIONS => { + 'CONSTANT' => 58, + 'TEXT' => 16, + "]" => 166, + 'IDENTIFIER' => 26 + }, + DEFAULT => -91, + GOTOS => { + 'identifier' => 60, + 'anytext' => 167, + 'text' => 61, + 'constant' => 57 + } + }, + {#State 148 + ACTIONS => { + ";" => 168 + } + }, + {#State 149 + ACTIONS => { + "}" => 169 + }, + DEFAULT => -83, + GOTOS => { + 'optional_base_element' => 171, + 'property_list' => 170 + } + }, + {#State 150 + ACTIONS => { + "}" => 172 + }, + DEFAULT => -83, + GOTOS => { + 'base_element' => 173, + 'property_list' => 159 + } + }, + {#State 151 + ACTIONS => { + "=" => 174 + }, + DEFAULT => -46 + }, + {#State 152 + DEFAULT => -44 + }, + {#State 153 + ACTIONS => { + "}" => 175, + "," => 176 + } + }, + {#State 154 + ACTIONS => { + "," => 177 + }, + DEFAULT => -55 + }, + {#State 155 + DEFAULT => -52 + }, + {#State 156 + ACTIONS => { + "=" => 178 + } + }, + {#State 157 + ACTIONS => { + "}" => 179 + } + }, + {#State 158 + DEFAULT => -78 + }, + {#State 159 + ACTIONS => { + 'IDENTIFIER' => 26, + "signed" => 75, + 'void' => 69, + "unsigned" => 79, + "[" => 20 + }, + DEFAULT => -83, + GOTOS => { + 'existingtype' => 76, + 'bitmap' => 44, + 'usertype' => 71, + 'property_list' => 70, + 'identifier' => 72, + 'struct' => 49, + 'enum' => 52, + 'type' => 180, + 'union' => 54, + 'sign' => 73 + } + }, + {#State 160 + ACTIONS => { + "," => 181, + ")" => 182 + } + }, + {#State 161 + DEFAULT => -77 + }, + {#State 162 + ACTIONS => { + "=" => 183 + } + }, + {#State 163 + ACTIONS => { + 'CONSTANT' => 58, + 'TEXT' => 16, + 'IDENTIFIER' => 26 + }, + DEFAULT => -91, + GOTOS => { + 'identifier' => 60, + 'anytext' => 184, + 'text' => 61, + 'constant' => 57 + } + }, + {#State 164 + ACTIONS => { + "-" => 83, + ":" => 82, + "<" => 84, + "+" => 86, + "~" => 85, + "*" => 87, + "?" => 88, + "{" => 89, + "&" => 90, + "/" => 91, + "=" => 92, + "(" => 93, + "|" => 94, + "." => 95, + ">" => 96 + }, + DEFAULT => -109 + }, + {#State 165 + ACTIONS => { + ":" => 82, + "<" => 84, + "~" => 85, + "?" => 88, + "{" => 89, + "=" => 92 + }, + DEFAULT => -108 + }, + {#State 166 + ACTIONS => { + "[" => 147 + }, + DEFAULT => -80, + GOTOS => { + 'array_len' => 185 + } + }, + {#State 167 + ACTIONS => { + "-" => 83, + ":" => 82, + "?" => 88, + "<" => 84, + "+" => 86, + "~" => 85, + "&" => 90, + "{" => 89, + "/" => 91, + "=" => 92, + "|" => 94, + "(" => 93, + "*" => 87, + "." => 95, + "]" => 186, + ">" => 96 + } + }, + {#State 168 + DEFAULT => -27 + }, + {#State 169 + DEFAULT => -67 + }, + {#State 170 + ACTIONS => { + "[" => 20 + }, + DEFAULT => -83, + GOTOS => { + 'base_or_empty' => 187, + 'base_element' => 188, + 'empty_element' => 189, + 'property_list' => 190 + } + }, + {#State 171 + DEFAULT => -66 + }, + {#State 172 + DEFAULT => -57 + }, + {#State 173 + ACTIONS => { + ";" => 191 + } + }, + {#State 174 + ACTIONS => { + 'CONSTANT' => 58, + 'TEXT' => 16, + 'IDENTIFIER' => 26 + }, + DEFAULT => -91, + GOTOS => { + 'identifier' => 60, + 'anytext' => 192, + 'text' => 61, + 'constant' => 57 + } + }, + {#State 175 + DEFAULT => -40 + }, + {#State 176 + ACTIONS => { + 'IDENTIFIER' => 26 + }, + GOTOS => { + 'identifier' => 151, + 'enum_element' => 193 + } + }, + {#State 177 + ACTIONS => { + 'IDENTIFIER' => 26 + }, + GOTOS => { + 'identifier' => 156, + 'bitmap_element' => 194 + } + }, + {#State 178 + ACTIONS => { + 'CONSTANT' => 58, + 'TEXT' => 16, + 'IDENTIFIER' => 26 + }, + DEFAULT => -91, + GOTOS => { + 'identifier' => 60, + 'anytext' => 195, + 'text' => 61, + 'constant' => 57 + } + }, + {#State 179 + DEFAULT => -48 + }, + {#State 180 + DEFAULT => -72, + GOTOS => { + 'pointers' => 196 + } + }, + {#State 181 + DEFAULT => -83, + GOTOS => { + 'base_element' => 197, + 'property_list' => 159 + } + }, + {#State 182 + ACTIONS => { + ";" => 198 + } + }, + {#State 183 + ACTIONS => { + 'CONSTANT' => 58, + 'TEXT' => 16, + 'IDENTIFIER' => 26 + }, + DEFAULT => -91, + GOTOS => { + 'identifier' => 60, + 'anytext' => 199, + 'text' => 61, + 'constant' => 57 + } + }, + {#State 184 + ACTIONS => { + "-" => 83, + ":" => 82, + "?" => 88, + "<" => 84, + ";" => 200, + "+" => 86, + "~" => 85, + "&" => 90, + "{" => 89, + "/" => 91, + "=" => 92, + "|" => 94, + "(" => 93, + "*" => 87, + "." => 95, + ">" => 96 + } + }, + {#State 185 + DEFAULT => -81 + }, + {#State 186 + ACTIONS => { + "[" => 147 + }, + DEFAULT => -80, + GOTOS => { + 'array_len' => 201 + } + }, + {#State 187 + DEFAULT => -64 + }, + {#State 188 + ACTIONS => { + ";" => 202 + } + }, + {#State 189 + DEFAULT => -63 + }, + {#State 190 + ACTIONS => { + 'IDENTIFIER' => 26, + "signed" => 75, + ";" => 203, + 'void' => 69, + "unsigned" => 79, + "[" => 20 + }, + DEFAULT => -83, + GOTOS => { + 'existingtype' => 76, + 'bitmap' => 44, + 'usertype' => 71, + 'property_list' => 70, + 'identifier' => 72, + 'struct' => 49, + 'enum' => 52, + 'type' => 180, + 'union' => 54, + 'sign' => 73 + } + }, + {#State 191 + DEFAULT => -75 + }, + {#State 192 + ACTIONS => { + "-" => 83, + ":" => 82, + "<" => 84, + "+" => 86, + "~" => 85, + "*" => 87, + "?" => 88, + "{" => 89, + "&" => 90, + "/" => 91, + "=" => 92, + "(" => 93, + "|" => 94, + "." => 95, + ">" => 96 + }, + DEFAULT => -47 + }, + {#State 193 + DEFAULT => -45 + }, + {#State 194 + DEFAULT => -53 + }, + {#State 195 + ACTIONS => { + "-" => 83, + ":" => 82, + "<" => 84, + "+" => 86, + "~" => 85, + "*" => 87, + "?" => 88, + "{" => 89, + "&" => 90, + "/" => 91, + "=" => 92, + "(" => 93, + "|" => 94, + "." => 95, + ">" => 96 + }, + DEFAULT => -56 + }, + {#State 196 + ACTIONS => { + 'IDENTIFIER' => 26, + "*" => 144 + }, + GOTOS => { + 'identifier' => 204 + } + }, + {#State 197 + DEFAULT => -79 + }, + {#State 198 + DEFAULT => -26 + }, + {#State 199 + ACTIONS => { + "-" => 83, + ":" => 82, + "?" => 88, + "<" => 84, + ";" => 205, + "+" => 86, + "~" => 85, + "&" => 90, + "{" => 89, + "/" => 91, + "=" => 92, + "|" => 94, + "(" => 93, + "*" => 87, + "." => 95, + ">" => 96 + } + }, + {#State 200 + DEFAULT => -24 + }, + {#State 201 + DEFAULT => -82 + }, + {#State 202 + DEFAULT => -62 + }, + {#State 203 + DEFAULT => -61 + }, + {#State 204 + ACTIONS => { + "[" => 147 + }, + DEFAULT => -80, + GOTOS => { + 'array_len' => 206 + } + }, + {#State 205 + DEFAULT => -25 + }, + {#State 206 + DEFAULT => -71 + } +], + yyrules => +[ + [#Rule 0 + '$start', 2, undef + ], + [#Rule 1 + 'idl', 0, undef + ], + [#Rule 2 + 'idl', 2, +sub +#line 19 "pidl/idl.yp" +{ push(@{$_[1]}, $_[2]); $_[1] } + ], + [#Rule 3 + 'idl', 2, +sub +#line 20 "pidl/idl.yp" +{ push(@{$_[1]}, $_[2]); $_[1] } + ], + [#Rule 4 + 'idl', 2, +sub +#line 21 "pidl/idl.yp" +{ push(@{$_[1]}, $_[2]); $_[1] } + ], + [#Rule 5 + 'idl', 2, +sub +#line 22 "pidl/idl.yp" +{ push(@{$_[1]}, $_[2]); $_[1] } + ], + [#Rule 6 + 'idl', 2, +sub +#line 23 "pidl/idl.yp" +{ push(@{$_[1]}, $_[2]); $_[1] } + ], + [#Rule 7 + 'idl', 2, +sub +#line 24 "pidl/idl.yp" +{ push(@{$_[1]}, $_[2]); $_[1] } + ], + [#Rule 8 + 'import', 3, +sub +#line 27 "pidl/idl.yp" +{{ + "TYPE" => "IMPORT", + "PATHS" => $_[2], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE} + }} + ], + [#Rule 9 + 'include', 3, +sub +#line 34 "pidl/idl.yp" +{{ + "TYPE" => "INCLUDE", + "PATHS" => $_[2], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE} + }} + ], + [#Rule 10 + 'importlib', 3, +sub +#line 41 "pidl/idl.yp" +{{ + "TYPE" => "IMPORTLIB", + "PATHS" => $_[2], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE} + }} + ], + [#Rule 11 + 'commalist', 1, +sub +#line 50 "pidl/idl.yp" +{ [ $_[1] ] } + ], + [#Rule 12 + 'commalist', 3, +sub +#line 51 "pidl/idl.yp" +{ push(@{$_[1]}, $_[3]); $_[1] } + ], + [#Rule 13 + 'coclass', 7, +sub +#line 55 "pidl/idl.yp" +{{ + "TYPE" => "COCLASS", + "PROPERTIES" => $_[1], + "NAME" => $_[3], + "DATA" => $_[5], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} + ], + [#Rule 14 + 'interface_names', 0, undef + ], + [#Rule 15 + 'interface_names', 4, +sub +#line 67 "pidl/idl.yp" +{ push(@{$_[1]}, $_[2]); $_[1] } + ], + [#Rule 16 + 'interface', 7, +sub +#line 71 "pidl/idl.yp" +{{ + "TYPE" => "INTERFACE", + "PROPERTIES" => $_[1], + "NAME" => $_[3], + "DATA" => $_[5], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} + ], + [#Rule 17 + 'cpp_quote', 4, +sub +#line 82 "pidl/idl.yp" +{{ + "TYPE" => "CPP_QUOTE", + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + "DATA" => $_[3] + }} + ], + [#Rule 18 + 'definitions', 1, +sub +#line 91 "pidl/idl.yp" +{ [ $_[1] ] } + ], + [#Rule 19 + 'definitions', 2, +sub +#line 92 "pidl/idl.yp" +{ push(@{$_[1]}, $_[2]); $_[1] } + ], + [#Rule 20 + 'definition', 1, undef + ], + [#Rule 21 + 'definition', 1, undef + ], + [#Rule 22 + 'definition', 1, undef + ], + [#Rule 23 + 'definition', 1, undef + ], + [#Rule 24 + 'const', 7, +sub +#line 100 "pidl/idl.yp" +{{ + "TYPE" => "CONST", + "DTYPE" => $_[2], + "POINTERS" => $_[3], + "NAME" => $_[4], + "VALUE" => $_[6], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} + ], + [#Rule 25 + 'const', 8, +sub +#line 110 "pidl/idl.yp" +{{ + "TYPE" => "CONST", + "DTYPE" => $_[2], + "POINTERS" => $_[3], + "NAME" => $_[4], + "ARRAY_LEN" => $_[5], + "VALUE" => $_[7], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} + ], + [#Rule 26 + 'function', 7, +sub +#line 124 "pidl/idl.yp" +{{ + "TYPE" => "FUNCTION", + "NAME" => $_[3], + "RETURN_TYPE" => $_[2], + "PROPERTIES" => $_[1], + "ELEMENTS" => $_[5], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} + ], + [#Rule 27 + 'typedef', 6, +sub +#line 136 "pidl/idl.yp" +{{ + "TYPE" => "TYPEDEF", + "PROPERTIES" => $_[1], + "NAME" => $_[4], + "DATA" => $_[3], + "ARRAY_LEN" => $_[5], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} + ], + [#Rule 28 + 'usertype', 1, undef + ], + [#Rule 29 + 'usertype', 1, undef + ], + [#Rule 30 + 'usertype', 1, undef + ], + [#Rule 31 + 'usertype', 1, undef + ], + [#Rule 32 + 'typedecl', 2, +sub +#line 149 "pidl/idl.yp" +{ $_[1] } + ], + [#Rule 33 + 'sign', 1, undef + ], + [#Rule 34 + 'sign', 1, undef + ], + [#Rule 35 + 'existingtype', 2, +sub +#line 154 "pidl/idl.yp" +{ ($_[1]?$_[1]:"signed") ." $_[2]" } + ], + [#Rule 36 + 'existingtype', 1, undef + ], + [#Rule 37 + 'type', 1, undef + ], + [#Rule 38 + 'type', 1, undef + ], + [#Rule 39 + 'type', 1, +sub +#line 158 "pidl/idl.yp" +{ "void" } + ], + [#Rule 40 + 'enum_body', 3, +sub +#line 160 "pidl/idl.yp" +{ $_[2] } + ], + [#Rule 41 + 'opt_enum_body', 0, undef + ], + [#Rule 42 + 'opt_enum_body', 1, undef + ], + [#Rule 43 + 'enum', 4, +sub +#line 163 "pidl/idl.yp" +{{ + "TYPE" => "ENUM", + "PROPERTIES" => $_[1], + "NAME" => $_[3], + "ELEMENTS" => $_[4] + }} + ], + [#Rule 44 + 'enum_elements', 1, +sub +#line 172 "pidl/idl.yp" +{ [ $_[1] ] } + ], + [#Rule 45 + 'enum_elements', 3, +sub +#line 173 "pidl/idl.yp" +{ push(@{$_[1]}, $_[3]); $_[1] } + ], + [#Rule 46 + 'enum_element', 1, undef + ], + [#Rule 47 + 'enum_element', 3, +sub +#line 177 "pidl/idl.yp" +{ "$_[1]$_[2]$_[3]" } + ], + [#Rule 48 + 'bitmap_body', 3, +sub +#line 180 "pidl/idl.yp" +{ $_[2] } + ], + [#Rule 49 + 'opt_bitmap_body', 0, undef + ], + [#Rule 50 + 'opt_bitmap_body', 1, undef + ], + [#Rule 51 + 'bitmap', 4, +sub +#line 183 "pidl/idl.yp" +{{ + "TYPE" => "BITMAP", + "PROPERTIES" => $_[1], + "NAME" => $_[3], + "ELEMENTS" => $_[4] + }} + ], + [#Rule 52 + 'bitmap_elements', 1, +sub +#line 192 "pidl/idl.yp" +{ [ $_[1] ] } + ], + [#Rule 53 + 'bitmap_elements', 3, +sub +#line 193 "pidl/idl.yp" +{ push(@{$_[1]}, $_[3]); $_[1] } + ], + [#Rule 54 + 'opt_bitmap_elements', 0, undef + ], + [#Rule 55 + 'opt_bitmap_elements', 1, undef + ], + [#Rule 56 + 'bitmap_element', 3, +sub +#line 198 "pidl/idl.yp" +{ "$_[1] ( $_[3] )" } + ], + [#Rule 57 + 'struct_body', 3, +sub +#line 201 "pidl/idl.yp" +{ $_[2] } + ], + [#Rule 58 + 'opt_struct_body', 0, undef + ], + [#Rule 59 + 'opt_struct_body', 1, undef + ], + [#Rule 60 + 'struct', 4, +sub +#line 205 "pidl/idl.yp" +{{ + "TYPE" => "STRUCT", + "PROPERTIES" => $_[1], + "NAME" => $_[3], + "ELEMENTS" => $_[4] + }} + ], + [#Rule 61 + 'empty_element', 2, +sub +#line 214 "pidl/idl.yp" +{{ + "NAME" => "", + "TYPE" => "EMPTY", + "PROPERTIES" => $_[1], + "POINTERS" => 0, + "ARRAY_LEN" => [], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} + ], + [#Rule 62 + 'base_or_empty', 2, undef + ], + [#Rule 63 + 'base_or_empty', 1, undef + ], + [#Rule 64 + 'optional_base_element', 2, +sub +#line 228 "pidl/idl.yp" +{ $_[2]->{PROPERTIES} = FlattenHash([$_[1],$_[2]->{PROPERTIES}]); $_[2] } + ], + [#Rule 65 + 'union_elements', 0, undef + ], + [#Rule 66 + 'union_elements', 2, +sub +#line 233 "pidl/idl.yp" +{ push(@{$_[1]}, $_[2]); $_[1] } + ], + [#Rule 67 + 'union_body', 3, +sub +#line 236 "pidl/idl.yp" +{ $_[2] } + ], + [#Rule 68 + 'opt_union_body', 0, undef + ], + [#Rule 69 + 'opt_union_body', 1, undef + ], + [#Rule 70 + 'union', 4, +sub +#line 240 "pidl/idl.yp" +{{ + "TYPE" => "UNION", + "PROPERTIES" => $_[1], + "NAME" => $_[3], + "ELEMENTS" => $_[4] + }} + ], + [#Rule 71 + 'base_element', 5, +sub +#line 249 "pidl/idl.yp" +{{ + "NAME" => $_[4], + "TYPE" => $_[2], + "PROPERTIES" => $_[1], + "POINTERS" => $_[3], + "ARRAY_LEN" => $_[5], + "FILE" => $_[0]->YYData->{FILE}, + "LINE" => $_[0]->YYData->{LINE}, + }} + ], + [#Rule 72 + 'pointers', 0, +sub +#line 263 "pidl/idl.yp" +{ 0 } + ], + [#Rule 73 + 'pointers', 2, +sub +#line 264 "pidl/idl.yp" +{ $_[1]+1 } + ], + [#Rule 74 + 'element_list1', 0, +sub +#line 268 "pidl/idl.yp" +{ [] } + ], + [#Rule 75 + 'element_list1', 3, +sub +#line 269 "pidl/idl.yp" +{ push(@{$_[1]}, $_[2]); $_[1] } + ], + [#Rule 76 + 'element_list2', 0, undef + ], + [#Rule 77 + 'element_list2', 1, undef + ], + [#Rule 78 + 'element_list2', 1, +sub +#line 275 "pidl/idl.yp" +{ [ $_[1] ] } + ], + [#Rule 79 + 'element_list2', 3, +sub +#line 276 "pidl/idl.yp" +{ push(@{$_[1]}, $_[3]); $_[1] } + ], + [#Rule 80 + 'array_len', 0, undef + ], + [#Rule 81 + 'array_len', 3, +sub +#line 281 "pidl/idl.yp" +{ push(@{$_[3]}, "*"); $_[3] } + ], + [#Rule 82 + 'array_len', 4, +sub +#line 282 "pidl/idl.yp" +{ push(@{$_[4]}, "$_[2]"); $_[4] } + ], + [#Rule 83 + 'property_list', 0, undef + ], + [#Rule 84 + 'property_list', 4, +sub +#line 288 "pidl/idl.yp" +{ FlattenHash([$_[1],$_[3]]); } + ], + [#Rule 85 + 'properties', 1, +sub +#line 291 "pidl/idl.yp" +{ $_[1] } + ], + [#Rule 86 + 'properties', 3, +sub +#line 292 "pidl/idl.yp" +{ FlattenHash([$_[1], $_[3]]); } + ], + [#Rule 87 + 'property', 1, +sub +#line 295 "pidl/idl.yp" +{{ "$_[1]" => "1" }} + ], + [#Rule 88 + 'property', 4, +sub +#line 296 "pidl/idl.yp" +{{ "$_[1]" => "$_[3]" }} + ], + [#Rule 89 + 'commalisttext', 1, undef + ], + [#Rule 90 + 'commalisttext', 3, +sub +#line 301 "pidl/idl.yp" +{ "$_[1],$_[3]" } + ], + [#Rule 91 + 'anytext', 0, +sub +#line 305 "pidl/idl.yp" +{ "" } + ], + [#Rule 92 + 'anytext', 1, undef + ], + [#Rule 93 + 'anytext', 1, undef + ], + [#Rule 94 + 'anytext', 1, undef + ], + [#Rule 95 + 'anytext', 3, +sub +#line 307 "pidl/idl.yp" +{ "$_[1]$_[2]$_[3]" } + ], + [#Rule 96 + 'anytext', 3, +sub +#line 308 "pidl/idl.yp" +{ "$_[1]$_[2]$_[3]" } + ], + [#Rule 97 + 'anytext', 3, +sub +#line 309 "pidl/idl.yp" +{ "$_[1]$_[2]$_[3]" } + ], + [#Rule 98 + 'anytext', 3, +sub +#line 310 "pidl/idl.yp" +{ "$_[1]$_[2]$_[3]" } + ], + [#Rule 99 + 'anytext', 3, +sub +#line 311 "pidl/idl.yp" +{ "$_[1]$_[2]$_[3]" } + ], + [#Rule 100 + 'anytext', 3, +sub +#line 312 "pidl/idl.yp" +{ "$_[1]$_[2]$_[3]" } + ], + [#Rule 101 + 'anytext', 3, +sub +#line 313 "pidl/idl.yp" +{ "$_[1]$_[2]$_[3]" } + ], + [#Rule 102 + 'anytext', 3, +sub +#line 314 "pidl/idl.yp" +{ "$_[1]$_[2]$_[3]" } + ], + [#Rule 103 + 'anytext', 3, +sub +#line 315 "pidl/idl.yp" +{ "$_[1]$_[2]$_[3]" } + ], + [#Rule 104 + 'anytext', 3, +sub +#line 316 "pidl/idl.yp" +{ "$_[1]$_[2]$_[3]" } + ], + [#Rule 105 + 'anytext', 3, +sub +#line 317 "pidl/idl.yp" +{ "$_[1]$_[2]$_[3]" } + ], + [#Rule 106 + 'anytext', 3, +sub +#line 318 "pidl/idl.yp" +{ "$_[1]$_[2]$_[3]" } + ], + [#Rule 107 + 'anytext', 3, +sub +#line 319 "pidl/idl.yp" +{ "$_[1]$_[2]$_[3]" } + ], + [#Rule 108 + 'anytext', 5, +sub +#line 320 "pidl/idl.yp" +{ "$_[1]$_[2]$_[3]$_[4]$_[5]" } + ], + [#Rule 109 + 'anytext', 5, +sub +#line 321 "pidl/idl.yp" +{ "$_[1]$_[2]$_[3]$_[4]$_[5]" } + ], + [#Rule 110 + 'identifier', 1, undef + ], + [#Rule 111 + 'optional_identifier', 1, undef + ], + [#Rule 112 + 'optional_identifier', 0, undef + ], + [#Rule 113 + 'constant', 1, undef + ], + [#Rule 114 + 'text', 1, +sub +#line 335 "pidl/idl.yp" +{ "\"$_[1]\"" } + ], + [#Rule 115 + 'optional_semicolon', 0, undef + ], + [#Rule 116 + 'optional_semicolon', 1, undef + ] +], + @_); + bless($self,$class); +} + +#line 346 "pidl/idl.yp" + + +use Parse::Pidl qw(error); + +##################################################################### +# flatten an array of hashes into a single hash +sub FlattenHash($) +{ + my $a = shift; + my %b; + for my $d (@{$a}) { + for my $k (keys %{$d}) { + $b{$k} = $d->{$k}; + } + } + return \%b; +} + + + +##################################################################### +# traverse a perl data structure removing any empty arrays or +# hashes and any hash elements that map to undef +sub CleanData($) +{ + sub CleanData($); + my($v) = shift; + return undef if (not defined($v)); + if (ref($v) eq "ARRAY") { + foreach my $i (0 .. $#{$v}) { + CleanData($v->[$i]); + } + # this removes any undefined elements from the array + @{$v} = grep { defined $_ } @{$v}; + } elsif (ref($v) eq "HASH") { + foreach my $x (keys %{$v}) { + CleanData($v->{$x}); + if (!defined $v->{$x}) { delete($v->{$x}); next; } + } + } + return $v; +} + +sub _Error { + if (exists $_[0]->YYData->{ERRMSG}) { + error($_[0]->YYData, $_[0]->YYData->{ERRMSG}); + delete $_[0]->YYData->{ERRMSG}; + return; + } + my $last_token = $_[0]->YYData->{LAST_TOKEN}; + + error($_[0]->YYData, "Syntax error near '$last_token'"); +} + +sub _Lexer($) +{ + my($parser)=shift; + + $parser->YYData->{INPUT} or return('',undef); + +again: + $parser->YYData->{INPUT} =~ s/^[ \t]*//; + + for ($parser->YYData->{INPUT}) { + if (/^\#/) { + if (s/^\# (\d+) \"(.*?)\"( \d+|)//) { + $parser->YYData->{LINE} = $1-1; + $parser->YYData->{FILE} = $2; + goto again; + } + if (s/^\#line (\d+) \"(.*?)\"( \d+|)//) { + $parser->YYData->{LINE} = $1-1; + $parser->YYData->{FILE} = $2; + goto again; + } + if (s/^(\#.*)$//m) { + goto again; + } + } + if (s/^(\n)//) { + $parser->YYData->{LINE}++; + goto again; + } + if (s/^\"(.*?)\"//) { + $parser->YYData->{LAST_TOKEN} = $1; + return('TEXT',$1); + } + if (s/^(\d+)(\W|$)/$2/) { + $parser->YYData->{LAST_TOKEN} = $1; + return('CONSTANT',$1); + } + if (s/^([\w_]+)//) { + $parser->YYData->{LAST_TOKEN} = $1; + if ($1 =~ + /^(coclass|interface|const|typedef|union|cpp_quote + |struct|enum|bitmap|void|unsigned|signed|import|include + |importlib)$/x) { + return $1; + } + return('IDENTIFIER',$1); + } + if (s/^(.)//s) { + $parser->YYData->{LAST_TOKEN} = $1; + return($1,$1); + } + } +} + +sub parse_string +{ + my ($data,$filename) = @_; + + my $self = new Parse::Pidl::IDL; + + $self->YYData->{FILE} = $filename; + $self->YYData->{INPUT} = $data; + $self->YYData->{LINE} = 0; + $self->YYData->{LAST_TOKEN} = "NONE"; + + my $idl = $self->YYParse( yylex => \&_Lexer, yyerror => \&_Error ); + + return CleanData($idl); +} + +sub parse_file($$) +{ + my ($filename,$incdirs) = @_; + + my $saved_delim = $/; + undef $/; + my $cpp = $ENV{CPP}; + if (! defined $cpp) { + $cpp = "cpp"; + } + my $includes = join('',map { " -I$_" } @$incdirs); + my $data = `$cpp -D__PIDL__$includes -xc $filename`; + $/ = $saved_delim; + + return parse_string($data, $filename); +} + +1; diff --git a/source/pidl/lib/Parse/Pidl/NDR.pm b/source/pidl/lib/Parse/Pidl/NDR.pm new file mode 100644 index 00000000000..700b6abdf7f --- /dev/null +++ b/source/pidl/lib/Parse/Pidl/NDR.pm @@ -0,0 +1,1211 @@ +################################################### +# Samba4 NDR info tree generator +# Copyright tridge@samba.org 2000-2003 +# Copyright tpot@samba.org 2001 +# Copyright jelmer@samba.org 2004-2006 +# released under the GNU GPL + +=pod + +=head1 NAME + +Parse::Pidl::NDR - NDR parsing information generator + +=head1 DESCRIPTION + +Return a table describing the order in which the parts of an element +should be parsed +Possible level types: + - POINTER + - ARRAY + - SUBCONTEXT + - SWITCH + - DATA + +=head1 AUTHOR + +Jelmer Vernooij <jelmer@samba.org> + +=cut + +package Parse::Pidl::NDR; + +require Exporter; +use vars qw($VERSION); +$VERSION = '0.01'; +@ISA = qw(Exporter); +@EXPORT = qw(GetPrevLevel GetNextLevel ContainsDeferred ContainsString); +@EXPORT_OK = qw(GetElementLevelTable ParseElement ValidElement align_type mapToScalar ParseType can_contain_deferred is_charset_array); + +use strict; +use Parse::Pidl qw(warning fatal); +use Parse::Pidl::Typelist qw(hasType getType expandAlias); +use Parse::Pidl::Util qw(has_property property_matches); + +# Alignment of the built-in scalar types +my $scalar_alignment = { + 'void' => 0, + 'char' => 1, + 'int8' => 1, + 'uint8' => 1, + 'int16' => 2, + 'uint16' => 2, + 'int32' => 4, + 'uint32' => 4, + 'hyper' => 8, + 'pointer' => 8, + 'dlong' => 4, + 'udlong' => 4, + 'udlongr' => 4, + 'DATA_BLOB' => 4, + 'string' => 4, + 'string_array' => 4, #??? + 'time_t' => 4, + 'NTTIME' => 4, + 'NTTIME_1sec' => 4, + 'NTTIME_hyper' => 8, + 'WERROR' => 4, + 'NTSTATUS' => 4, + 'COMRESULT' => 4, + 'nbt_string' => 4, + 'wrepl_nbt_name' => 4, + 'ipv4address' => 4 +}; + +sub GetElementLevelTable($$) +{ + my ($e, $pointer_default) = @_; + + my $order = []; + my $is_deferred = 0; + my @bracket_array = (); + my @length_is = (); + my @size_is = (); + my $pointer_idx = 0; + + if (has_property($e, "size_is")) { + @size_is = split /,/, has_property($e, "size_is"); + } + + if (has_property($e, "length_is")) { + @length_is = split /,/, has_property($e, "length_is"); + } + + if (defined($e->{ARRAY_LEN})) { + @bracket_array = @{$e->{ARRAY_LEN}}; + } + + if (has_property($e, "out")) { + my $needptrs = 1; + + if (has_property($e, "string")) { $needptrs++; } + if ($#bracket_array >= 0) { $needptrs = 0; } + + warning($e, "[out] argument `$e->{NAME}' not a pointer") if ($needptrs > $e->{POINTERS}); + } + + # Parse the [][][][] style array stuff + for my $i (0 .. $#bracket_array) { + my $d = $bracket_array[$#bracket_array - $i]; + my $size = $d; + my $length = $d; + my $is_surrounding = 0; + my $is_varying = 0; + my $is_conformant = 0; + my $is_string = 0; + my $is_fixed = 0; + my $is_inline = 0; + + if ($d eq "*") { + $is_conformant = 1; + if ($size = shift @size_is) { + } elsif ((scalar(@size_is) == 0) and has_property($e, "string")) { + $is_string = 1; + delete($e->{PROPERTIES}->{string}); + } else { + fatal($e, "Must specify size_is() for conformant array!") + } + + if (($length = shift @length_is) or $is_string) { + $is_varying = 1; + } else { + $length = $size; + } + + if ($e == $e->{PARENT}->{ELEMENTS}[-1] + and $e->{PARENT}->{TYPE} ne "FUNCTION") { + $is_surrounding = 1; + } + } + + $is_fixed = 1 if (not $is_conformant and Parse::Pidl::Util::is_constant($size)); + $is_inline = 1 if (not $is_conformant and not Parse::Pidl::Util::is_constant($size)); + + push (@$order, { + TYPE => "ARRAY", + SIZE_IS => $size, + LENGTH_IS => $length, + IS_DEFERRED => $is_deferred, + IS_SURROUNDING => $is_surrounding, + IS_ZERO_TERMINATED => $is_string, + IS_VARYING => $is_varying, + IS_CONFORMANT => $is_conformant, + IS_FIXED => $is_fixed, + IS_INLINE => $is_inline + }); + } + + # Next, all the pointers + foreach my $i (1..$e->{POINTERS}) { + my $level = "EMBEDDED"; + # Top level "ref" pointers do not have a referrent identifier + $level = "TOP" if ($i == 1 and $e->{PARENT}->{TYPE} eq "FUNCTION"); + + my $pt; + # + # Only the first level gets the pointer type from the + # pointer property, the others get them from + # the pointer_default() interface property + # + # see http://msdn2.microsoft.com/en-us/library/aa378984(VS.85).aspx + # (Here they talk about the rightmost pointer, but testing shows + # they mean the leftmost pointer.) + # + # --metze + # + $pt = pointer_type($e); + if ($i > 1) { + $is_deferred = 1 if ($pt ne "ref" and $e->{PARENT}->{TYPE} eq "FUNCTION"); + $pt = $pointer_default; + } + + push (@$order, { + TYPE => "POINTER", + POINTER_TYPE => $pt, + POINTER_INDEX => $pointer_idx, + IS_DEFERRED => "$is_deferred", + LEVEL => $level + }); + + warning($e, "top-level \[out\] pointer `$e->{NAME}' is not a \[ref\] pointer") + if ($i == 1 and $pt ne "ref" and + $e->{PARENT}->{TYPE} eq "FUNCTION" and + not has_property($e, "in")); + + $pointer_idx++; + + # everything that follows will be deferred + $is_deferred = 1 if ($level ne "TOP"); + + my $array_size = shift @size_is; + my $array_length; + my $is_varying; + my $is_conformant; + my $is_string = 0; + if ($array_size) { + $is_conformant = 1; + if ($array_length = shift @length_is) { + $is_varying = 1; + } else { + $array_length = $array_size; + $is_varying =0; + } + } + + if (scalar(@size_is) == 0 and has_property($e, "string") and + $i == $e->{POINTERS}) { + $is_string = 1; + $is_varying = $is_conformant = has_property($e, "noheader")?0:1; + delete($e->{PROPERTIES}->{string}); + } + + if ($array_size or $is_string) { + push (@$order, { + TYPE => "ARRAY", + SIZE_IS => $array_size, + LENGTH_IS => $array_length, + IS_DEFERRED => $is_deferred, + IS_SURROUNDING => 0, + IS_ZERO_TERMINATED => $is_string, + IS_VARYING => $is_varying, + IS_CONFORMANT => $is_conformant, + IS_FIXED => 0, + IS_INLINE => 0 + }); + + $is_deferred = 0; + } + } + + if (defined(has_property($e, "subcontext"))) { + my $hdr_size = has_property($e, "subcontext"); + my $subsize = has_property($e, "subcontext_size"); + if (not defined($subsize)) { + $subsize = -1; + } + + push (@$order, { + TYPE => "SUBCONTEXT", + HEADER_SIZE => $hdr_size, + SUBCONTEXT_SIZE => $subsize, + IS_DEFERRED => $is_deferred, + COMPRESSION => has_property($e, "compression"), + }); + } + + if (my $switch = has_property($e, "switch_is")) { + push (@$order, { + TYPE => "SWITCH", + SWITCH_IS => $switch, + IS_DEFERRED => $is_deferred + }); + } + + if (scalar(@size_is) > 0) { + fatal($e, "size_is() on non-array element"); + } + + if (scalar(@length_is) > 0) { + fatal($e, "length_is() on non-array element"); + } + + if (has_property($e, "string")) { + fatal($e, "string() attribute on non-array element"); + } + + push (@$order, { + TYPE => "DATA", + DATA_TYPE => $e->{TYPE}, + IS_DEFERRED => $is_deferred, + CONTAINS_DEFERRED => can_contain_deferred($e->{TYPE}), + IS_SURROUNDING => 0 #FIXME + }); + + my $i = 0; + foreach (@$order) { $_->{LEVEL_INDEX} = $i; $i+=1; } + + return $order; +} + +##################################################################### +# see if a type contains any deferred data +sub can_contain_deferred($) +{ + sub can_contain_deferred($); + my ($type) = @_; + + return 1 unless (hasType($type)); # assume the worst + + $type = getType($type); + + return 0 if (Parse::Pidl::Typelist::is_scalar($type)); + + return can_contain_deferred($type->{DATA}) if ($type->{TYPE} eq "TYPEDEF"); + + return 0 unless defined($type->{ELEMENTS}); + + foreach (@{$type->{ELEMENTS}}) { + return 1 if ($_->{POINTERS}); + return 1 if (can_contain_deferred ($_->{TYPE})); + } + + return 0; +} + +sub pointer_type($) +{ + my $e = shift; + + return undef unless $e->{POINTERS}; + + return "ref" if (has_property($e, "ref")); + return "full" if (has_property($e, "ptr")); + return "sptr" if (has_property($e, "sptr")); + return "unique" if (has_property($e, "unique")); + return "relative" if (has_property($e, "relative")); + return "ignore" if (has_property($e, "ignore")); + + return undef; +} + +##################################################################### +# work out the correct alignment for a structure or union +sub find_largest_alignment($) +{ + my $s = shift; + + my $align = 1; + for my $e (@{$s->{ELEMENTS}}) { + my $a = 1; + + if ($e->{POINTERS}) { + $a = 4; + } elsif (has_property($e, "subcontext")) { + $a = 1; + } elsif (has_property($e, "transmit_as")) { + $a = align_type($e->{PROPERTIES}->{transmit_as}); + } else { + $a = align_type($e->{TYPE}); + } + + $align = $a if ($align < $a); + } + + return $align; +} + +##################################################################### +# align a type +sub align_type($) +{ + sub align_type($); + my ($e) = @_; + + if (ref($e) eq "HASH" and $e->{TYPE} eq "SCALAR") { + return $scalar_alignment->{$e->{NAME}}; + } + + return 0 if ($e eq "EMPTY"); + + unless (hasType($e)) { + # it must be an external type - all we can do is guess + # warning($e, "assuming alignment of unknown type '$e' is 4"); + return 4; + } + + my $dt = getType($e); + + if ($dt->{TYPE} eq "TYPEDEF") { + return align_type($dt->{DATA}); + } elsif ($dt->{TYPE} eq "ENUM") { + return align_type(Parse::Pidl::Typelist::enum_type_fn($dt)); + } elsif ($dt->{TYPE} eq "BITMAP") { + return align_type(Parse::Pidl::Typelist::bitmap_type_fn($dt)); + } elsif (($dt->{TYPE} eq "STRUCT") or ($dt->{TYPE} eq "UNION")) { + # Struct/union without body: assume 4 + return 4 unless (defined($dt->{ELEMENTS})); + return find_largest_alignment($dt); + } + + die("Unknown data type type $dt->{TYPE}"); +} + +sub ParseElement($$) +{ + my ($e, $pointer_default) = @_; + + $e->{TYPE} = expandAlias($e->{TYPE}); + + if (ref($e->{TYPE}) eq "HASH") { + $e->{TYPE} = ParseType($e->{TYPE}, $pointer_default); + } + + return { + NAME => $e->{NAME}, + TYPE => $e->{TYPE}, + PROPERTIES => $e->{PROPERTIES}, + LEVELS => GetElementLevelTable($e, $pointer_default), + REPRESENTATION_TYPE => ($e->{PROPERTIES}->{represent_as} or $e->{TYPE}), + ALIGN => align_type($e->{TYPE}), + ORIGINAL => $e + }; +} + +sub ParseStruct($$) +{ + my ($struct, $pointer_default) = @_; + my @elements = (); + my $surrounding = undef; + + return { + TYPE => "STRUCT", + NAME => $struct->{NAME}, + SURROUNDING_ELEMENT => undef, + ELEMENTS => undef, + PROPERTIES => $struct->{PROPERTIES}, + ORIGINAL => $struct, + ALIGN => undef + } unless defined($struct->{ELEMENTS}); + + CheckPointerTypes($struct, $pointer_default); + + foreach my $x (@{$struct->{ELEMENTS}}) + { + my $e = ParseElement($x, $pointer_default); + if ($x != $struct->{ELEMENTS}[-1] and + $e->{LEVELS}[0]->{IS_SURROUNDING}) { + fatal($x, "conformant member not at end of struct"); + } + push @elements, $e; + } + + my $e = $elements[-1]; + if (defined($e) and defined($e->{LEVELS}[0]->{IS_SURROUNDING}) and + $e->{LEVELS}[0]->{IS_SURROUNDING}) { + $surrounding = $e; + } + + if (defined $e->{TYPE} && $e->{TYPE} eq "string" + && property_matches($e, "flag", ".*LIBNDR_FLAG_STR_CONFORMANT.*")) { + $surrounding = $struct->{ELEMENTS}[-1]; + } + + my $align = undef; + if ($struct->{NAME}) { + $align = align_type($struct->{NAME}); + } + + return { + TYPE => "STRUCT", + NAME => $struct->{NAME}, + SURROUNDING_ELEMENT => $surrounding, + ELEMENTS => \@elements, + PROPERTIES => $struct->{PROPERTIES}, + ORIGINAL => $struct, + ALIGN => $align + }; +} + +sub ParseUnion($$) +{ + my ($e, $pointer_default) = @_; + my @elements = (); + my $hasdefault = 0; + my $switch_type = has_property($e, "switch_type"); + unless (defined($switch_type)) { $switch_type = "uint32"; } + if (has_property($e, "nodiscriminant")) { $switch_type = undef; } + + return { + TYPE => "UNION", + NAME => $e->{NAME}, + SWITCH_TYPE => $switch_type, + ELEMENTS => undef, + PROPERTIES => $e->{PROPERTIES}, + HAS_DEFAULT => $hasdefault, + ORIGINAL => $e + } unless defined($e->{ELEMENTS}); + + CheckPointerTypes($e, $pointer_default); + + foreach my $x (@{$e->{ELEMENTS}}) + { + my $t; + if ($x->{TYPE} eq "EMPTY") { + $t = { TYPE => "EMPTY" }; + } else { + $t = ParseElement($x, $pointer_default); + } + if (has_property($x, "default")) { + $t->{CASE} = "default"; + $hasdefault = 1; + } elsif (defined($x->{PROPERTIES}->{case})) { + $t->{CASE} = "case $x->{PROPERTIES}->{case}"; + } else { + die("Union element $x->{NAME} has neither default nor case property"); + } + push @elements, $t; + } + + return { + TYPE => "UNION", + NAME => $e->{NAME}, + SWITCH_TYPE => $switch_type, + ELEMENTS => \@elements, + PROPERTIES => $e->{PROPERTIES}, + HAS_DEFAULT => $hasdefault, + ORIGINAL => $e + }; +} + +sub ParseEnum($$) +{ + my ($e, $pointer_default) = @_; + + return { + TYPE => "ENUM", + NAME => $e->{NAME}, + BASE_TYPE => Parse::Pidl::Typelist::enum_type_fn($e), + ELEMENTS => $e->{ELEMENTS}, + PROPERTIES => $e->{PROPERTIES}, + ORIGINAL => $e + }; +} + +sub ParseBitmap($$) +{ + my ($e, $pointer_default) = @_; + + return { + TYPE => "BITMAP", + NAME => $e->{NAME}, + BASE_TYPE => Parse::Pidl::Typelist::bitmap_type_fn($e), + ELEMENTS => $e->{ELEMENTS}, + PROPERTIES => $e->{PROPERTIES}, + ORIGINAL => $e + }; +} + +sub ParseType($$) +{ + my ($d, $pointer_default) = @_; + + my $data = { + STRUCT => \&ParseStruct, + UNION => \&ParseUnion, + ENUM => \&ParseEnum, + BITMAP => \&ParseBitmap, + TYPEDEF => \&ParseTypedef, + }->{$d->{TYPE}}->($d, $pointer_default); + + return $data; +} + +sub ParseTypedef($$) +{ + my ($d, $pointer_default) = @_; + + if (defined($d->{DATA}->{PROPERTIES}) && !defined($d->{PROPERTIES})) { + $d->{PROPERTIES} = $d->{DATA}->{PROPERTIES}; + } + + my $data = ParseType($d->{DATA}, $pointer_default); + $data->{ALIGN} = align_type($d->{NAME}); + + return { + NAME => $d->{NAME}, + TYPE => $d->{TYPE}, + PROPERTIES => $d->{PROPERTIES}, + DATA => $data, + ORIGINAL => $d + }; +} + +sub ParseConst($$) +{ + my ($ndr,$d) = @_; + + return $d; +} + +sub ParseFunction($$$) +{ + my ($ndr,$d,$opnum) = @_; + my @elements = (); + my $rettype = undef; + my $thisopnum = undef; + + CheckPointerTypes($d, "ref"); + + if (not defined($d->{PROPERTIES}{noopnum})) { + $thisopnum = ${$opnum}; + ${$opnum}++; + } + + foreach my $x (@{$d->{ELEMENTS}}) { + my $e = ParseElement($x, $ndr->{PROPERTIES}->{pointer_default}); + push (@{$e->{DIRECTION}}, "in") if (has_property($x, "in")); + push (@{$e->{DIRECTION}}, "out") if (has_property($x, "out")); + + push (@elements, $e); + } + + if ($d->{RETURN_TYPE} ne "void") { + $rettype = expandAlias($d->{RETURN_TYPE}); + } + + my $async = 0; + if (has_property($d, "async")) { $async = 1; } + + return { + NAME => $d->{NAME}, + TYPE => "FUNCTION", + OPNUM => $thisopnum, + ASYNC => $async, + RETURN_TYPE => $rettype, + PROPERTIES => $d->{PROPERTIES}, + ELEMENTS => \@elements, + ORIGINAL => $d + }; +} + +sub CheckPointerTypes($$) +{ + my ($s,$default) = @_; + + return unless defined($s->{ELEMENTS}); + + foreach my $e (@{$s->{ELEMENTS}}) { + if ($e->{POINTERS} and not defined(pointer_type($e))) { + $e->{PROPERTIES}->{$default} = '1'; + } + } +} + +sub FindNestedTypes($$) +{ + sub FindNestedTypes($$); + my ($l, $t) = @_; + + return unless defined($t->{ELEMENTS}); + return if ($t->{TYPE} eq "ENUM"); + return if ($t->{TYPE} eq "BITMAP"); + + foreach (@{$t->{ELEMENTS}}) { + if (ref($_->{TYPE}) eq "HASH") { + push (@$l, $_->{TYPE}) if (defined($_->{TYPE}->{NAME})); + FindNestedTypes($l, $_->{TYPE}); + } + } +} + +sub ParseInterface($) +{ + my $idl = shift; + my @types = (); + my @consts = (); + my @functions = (); + my @endpoints; + my $opnum = 0; + my $version; + + if (not has_property($idl, "pointer_default")) { + # MIDL defaults to "ptr" in DCE compatible mode (/osf) + # and "unique" in Microsoft Extensions mode (default) + $idl->{PROPERTIES}->{pointer_default} = "unique"; + } + + foreach my $d (@{$idl->{DATA}}) { + if ($d->{TYPE} eq "FUNCTION") { + push (@functions, ParseFunction($idl, $d, \$opnum)); + } elsif ($d->{TYPE} eq "CONST") { + push (@consts, ParseConst($idl, $d)); + } else { + push (@types, ParseType($d, $idl->{PROPERTIES}->{pointer_default})); + FindNestedTypes(\@types, $d); + } + } + + $version = "0.0"; + + if(defined $idl->{PROPERTIES}->{version}) { + my @if_version = split(/\./, $idl->{PROPERTIES}->{version}); + if ($if_version[0] == $idl->{PROPERTIES}->{version}) { + $version = $idl->{PROPERTIES}->{version}; + } else { + $version = $if_version[1] << 16 | $if_version[0]; + } + } + + # If no endpoint is set, default to the interface name as a named pipe + if (!defined $idl->{PROPERTIES}->{endpoint}) { + push @endpoints, "\"ncacn_np:[\\\\pipe\\\\" . $idl->{NAME} . "]\""; + } else { + @endpoints = split /,/, $idl->{PROPERTIES}->{endpoint}; + } + + return { + NAME => $idl->{NAME}, + UUID => lc(has_property($idl, "uuid")), + VERSION => $version, + TYPE => "INTERFACE", + PROPERTIES => $idl->{PROPERTIES}, + FUNCTIONS => \@functions, + CONSTS => \@consts, + TYPES => \@types, + ENDPOINTS => \@endpoints + }; +} + +# Convert a IDL tree to a NDR tree +# Gives a result tree describing all that's necessary for easily generating +# NDR parsers / generators +sub Parse($) +{ + my $idl = shift; + + return undef unless (defined($idl)); + + Parse::Pidl::NDR::Validate($idl); + + my @ndr = (); + + foreach (@{$idl}) { + ($_->{TYPE} eq "CPP_QUOTE") && push(@ndr, $_); + ($_->{TYPE} eq "INTERFACE") && push(@ndr, ParseInterface($_)); + ($_->{TYPE} eq "IMPORT") && push(@ndr, $_); + } + + return \@ndr; +} + +sub GetNextLevel($$) +{ + my $e = shift; + my $fl = shift; + + my $seen = 0; + + foreach my $l (@{$e->{LEVELS}}) { + return $l if ($seen); + ($seen = 1) if ($l == $fl); + } + + return undef; +} + +sub GetPrevLevel($$) +{ + my ($e,$fl) = @_; + my $prev = undef; + + foreach my $l (@{$e->{LEVELS}}) { + (return $prev) if ($l == $fl); + $prev = $l; + } + + return undef; +} + +sub ContainsString($) +{ + my ($e) = @_; + + foreach my $l (@{$e->{LEVELS}}) { + return 1 if ($l->{TYPE} eq "ARRAY" and $l->{IS_ZERO_TERMINATED}); + } + + return 0; +} + +sub ContainsDeferred($$) +{ + my ($e,$l) = @_; + + return 1 if ($l->{CONTAINS_DEFERRED}); + + while ($l = GetNextLevel($e,$l)) + { + return 1 if ($l->{IS_DEFERRED}); + return 1 if ($l->{CONTAINS_DEFERRED}); + } + + return 0; +} + +sub el_name($) +{ + my $e = shift; + + if ($e->{PARENT} && $e->{PARENT}->{NAME}) { + return "$e->{PARENT}->{NAME}.$e->{NAME}"; + } + + if ($e->{PARENT} && $e->{PARENT}->{PARENT}->{NAME}) { + return "$e->{PARENT}->{PARENT}->{NAME}.$e->{NAME}"; + } + + if ($e->{PARENT}) { + return "$e->{PARENT}->{NAME}.$e->{NAME}"; + } + + return $e->{NAME}; +} + +################################### +# find a sibling var in a structure +sub find_sibling($$) +{ + my($e,$name) = @_; + my($fn) = $e->{PARENT}; + + if ($name =~ /\*(.*)/) { + $name = $1; + } + + for my $e2 (@{$fn->{ELEMENTS}}) { + return $e2 if ($e2->{NAME} eq $name); + } + + return undef; +} + +my %property_list = ( + # interface + "helpstring" => ["INTERFACE", "FUNCTION"], + "version" => ["INTERFACE"], + "uuid" => ["INTERFACE"], + "endpoint" => ["INTERFACE"], + "pointer_default" => ["INTERFACE"], + "helper" => ["INTERFACE"], + "authservice" => ["INTERFACE"], + + # dcom + "object" => ["INTERFACE"], + "local" => ["INTERFACE", "FUNCTION"], + "iid_is" => ["ELEMENT"], + "call_as" => ["FUNCTION"], + "idempotent" => ["FUNCTION"], + + # function + "noopnum" => ["FUNCTION"], + "in" => ["ELEMENT"], + "out" => ["ELEMENT"], + "async" => ["FUNCTION"], + + # pointer + "ref" => ["ELEMENT"], + "ptr" => ["ELEMENT"], + "unique" => ["ELEMENT"], + "ignore" => ["ELEMENT"], + "relative" => ["ELEMENT"], + "relative_base" => ["TYPEDEF"], + + "gensize" => ["TYPEDEF"], + "value" => ["ELEMENT"], + "flag" => ["ELEMENT", "TYPEDEF"], + + # generic + "public" => ["FUNCTION", "TYPEDEF"], + "nopush" => ["FUNCTION", "TYPEDEF"], + "nopull" => ["FUNCTION", "TYPEDEF"], + "nosize" => ["FUNCTION", "TYPEDEF"], + "noprint" => ["FUNCTION", "TYPEDEF"], + "noejs" => ["FUNCTION", "TYPEDEF"], + "todo" => ["FUNCTION"], + + # union + "switch_is" => ["ELEMENT"], + "switch_type" => ["ELEMENT", "TYPEDEF"], + "nodiscriminant" => ["TYPEDEF"], + "case" => ["ELEMENT"], + "default" => ["ELEMENT"], + + "represent_as" => ["ELEMENT"], + "transmit_as" => ["ELEMENT"], + + # subcontext + "subcontext" => ["ELEMENT"], + "subcontext_size" => ["ELEMENT"], + "compression" => ["ELEMENT"], + + # enum + "enum8bit" => ["TYPEDEF"], + "enum16bit" => ["TYPEDEF"], + "v1_enum" => ["TYPEDEF"], + + # bitmap + "bitmap8bit" => ["TYPEDEF"], + "bitmap16bit" => ["TYPEDEF"], + "bitmap32bit" => ["TYPEDEF"], + "bitmap64bit" => ["TYPEDEF"], + + # array + "range" => ["ELEMENT"], + "size_is" => ["ELEMENT"], + "string" => ["ELEMENT"], + "noheader" => ["ELEMENT"], + "charset" => ["ELEMENT"], + "length_is" => ["ELEMENT"], +); + +##################################################################### +# check for unknown properties +sub ValidProperties($$) +{ + my ($e,$t) = @_; + + return unless defined $e->{PROPERTIES}; + + foreach my $key (keys %{$e->{PROPERTIES}}) { + warning($e, el_name($e) . ": unknown property '$key'") + unless defined($property_list{$key}); + + fatal($e, el_name($e) . ": property '$key' not allowed on '$t'") + unless grep($t, @{$property_list{$key}}); + } +} + +sub mapToScalar($) +{ + sub mapToScalar($); + my $t = shift; + return $t->{NAME} if (ref($t) eq "HASH" and $t->{TYPE} eq "SCALAR"); + my $ti = getType($t); + + if (not defined ($ti)) { + return undef; + } elsif ($ti->{TYPE} eq "TYPEDEF") { + return mapToScalar($ti->{DATA}); + } elsif ($ti->{TYPE} eq "ENUM") { + return Parse::Pidl::Typelist::enum_type_fn($ti); + } elsif ($ti->{TYPE} eq "BITMAP") { + return Parse::Pidl::Typelist::bitmap_type_fn($ti); + } + + return undef; +} + +##################################################################### +# validate an element +sub ValidElement($) +{ + my $e = shift; + + ValidProperties($e,"ELEMENT"); + + # Check whether switches are used correctly. + if (my $switch = has_property($e, "switch_is")) { + my $e2 = find_sibling($e, $switch); + my $type = getType($e->{TYPE}); + + if (defined($type) and $type->{DATA}->{TYPE} ne "UNION") { + fatal($e, el_name($e) . ": switch_is() used on non-union type $e->{TYPE} which is a $type->{DATA}->{TYPE}"); + } + + if (not has_property($type->{DATA}, "nodiscriminant") and defined($e2)) { + my $discriminator_type = has_property($type->{DATA}, "switch_type"); + $discriminator_type = "uint32" unless defined ($discriminator_type); + + my $t1 = mapToScalar($discriminator_type); + + if (not defined($t1)) { + fatal($e, el_name($e) . ": unable to map discriminator type '$discriminator_type' to scalar"); + } + + my $t2 = mapToScalar($e2->{TYPE}); + if (not defined($t2)) { + fatal($e, el_name($e) . ": unable to map variable used for switch_is() to scalar"); + } + + if ($t1 ne $t2) { + warning($e, el_name($e) . ": switch_is() is of type $e2->{TYPE} ($t2), while discriminator type for union $type->{NAME} is $discriminator_type ($t1)"); + } + } + } + + if (has_property($e, "subcontext") and has_property($e, "represent_as")) { + fatal($e, el_name($e) . " : subcontext() and represent_as() can not be used on the same element"); + } + + if (has_property($e, "subcontext") and has_property($e, "transmit_as")) { + fatal($e, el_name($e) . " : subcontext() and transmit_as() can not be used on the same element"); + } + + if (has_property($e, "represent_as") and has_property($e, "transmit_as")) { + fatal($e, el_name($e) . " : represent_as() and transmit_as() can not be used on the same element"); + } + + if (has_property($e, "represent_as") and has_property($e, "value")) { + fatal($e, el_name($e) . " : represent_as() and value() can not be used on the same element"); + } + + if (has_property($e, "subcontext")) { + warning($e, "subcontext() is deprecated. Use represent_as() or transmit_as() instead"); + } + + if (defined (has_property($e, "subcontext_size")) and not defined(has_property($e, "subcontext"))) { + fatal($e, el_name($e) . " : subcontext_size() on non-subcontext element"); + } + + if (defined (has_property($e, "compression")) and not defined(has_property($e, "subcontext"))) { + fatal($e, el_name($e) . " : compression() on non-subcontext element"); + } + + if (!$e->{POINTERS} && ( + has_property($e, "ptr") or + has_property($e, "unique") or + has_property($e, "relative") or + has_property($e, "ref"))) { + fatal($e, el_name($e) . " : pointer properties on non-pointer element\n"); + } +} + +##################################################################### +# validate an enum +sub ValidEnum($) +{ + my ($enum) = @_; + + ValidProperties($enum, "ENUM"); +} + +##################################################################### +# validate a bitmap +sub ValidBitmap($) +{ + my ($bitmap) = @_; + + ValidProperties($bitmap, "BITMAP"); +} + +##################################################################### +# validate a struct +sub ValidStruct($) +{ + my($struct) = shift; + + ValidProperties($struct, "STRUCT"); + + return unless defined($struct->{ELEMENTS}); + + foreach my $e (@{$struct->{ELEMENTS}}) { + $e->{PARENT} = $struct; + ValidElement($e); + } +} + +##################################################################### +# parse a union +sub ValidUnion($) +{ + my($union) = shift; + + ValidProperties($union,"UNION"); + + if (has_property($union->{PARENT}, "nodiscriminant") and has_property($union->{PARENT}, "switch_type")) { + fatal($union->{PARENT}, $union->{PARENT}->{NAME} . ": switch_type() on union without discriminant"); + } + + return unless defined($union->{ELEMENTS}); + + foreach my $e (@{$union->{ELEMENTS}}) { + $e->{PARENT} = $union; + + if (defined($e->{PROPERTIES}->{default}) and + defined($e->{PROPERTIES}->{case})) { + fatal($e, "Union member $e->{NAME} can not have both default and case properties!"); + } + + unless (defined ($e->{PROPERTIES}->{default}) or + defined ($e->{PROPERTIES}->{case})) { + fatal($e, "Union member $e->{NAME} must have default or case property"); + } + + if (has_property($e, "ref")) { + fatal($e, el_name($e) . " : embedded ref pointers are not supported yet\n"); + } + + + ValidElement($e); + } +} + +##################################################################### +# parse a typedef +sub ValidTypedef($) +{ + my($typedef) = shift; + my $data = $typedef->{DATA}; + + ValidProperties($typedef, "TYPEDEF"); + + $data->{PARENT} = $typedef; + + ValidType($data) if (ref($data) eq "HASH"); +} + +##################################################################### +# validate a function +sub ValidFunction($) +{ + my($fn) = shift; + + ValidProperties($fn,"FUNCTION"); + + foreach my $e (@{$fn->{ELEMENTS}}) { + $e->{PARENT} = $fn; + if (has_property($e, "ref") && !$e->{POINTERS}) { + fatal($e, "[ref] variables must be pointers ($fn->{NAME}/$e->{NAME})"); + } + ValidElement($e); + } +} + +##################################################################### +# validate a type +sub ValidType($) +{ + my ($t) = @_; + + { + TYPEDEF => \&ValidTypedef, + STRUCT => \&ValidStruct, + UNION => \&ValidUnion, + ENUM => \&ValidEnum, + BITMAP => \&ValidBitmap + }->{$t->{TYPE}}->($t); +} + +##################################################################### +# parse the interface definitions +sub ValidInterface($) +{ + my($interface) = shift; + my($data) = $interface->{DATA}; + + if (has_property($interface, "helper")) { + warning($interface, "helper() is pidl-specific and deprecated. Use `include' instead"); + } + + ValidProperties($interface,"INTERFACE"); + + if (has_property($interface, "pointer_default")) { + if (not grep (/$interface->{PROPERTIES}->{pointer_default}/, + ("ref", "unique", "ptr"))) { + fatal($interface, "Unknown default pointer type `$interface->{PROPERTIES}->{pointer_default}'"); + } + } + + if (has_property($interface, "object")) { + if (has_property($interface, "version") && + $interface->{PROPERTIES}->{version} != 0) { + fatal($interface, "Object interfaces must have version 0.0 ($interface->{NAME})"); + } + + if (!defined($interface->{BASE}) && + not ($interface->{NAME} eq "IUnknown")) { + fatal($interface, "Object interfaces must all derive from IUnknown ($interface->{NAME})"); + } + } + + foreach my $d (@{$data}) { + ($d->{TYPE} eq "FUNCTION") && ValidFunction($d); + ($d->{TYPE} eq "TYPEDEF" or + $d->{TYPE} eq "STRUCT" or + $d->{TYPE} eq "UNION" or + $d->{TYPE} eq "ENUM" or + $d->{TYPE} eq "BITMAP") && ValidType($d); + } + +} + +##################################################################### +# Validate an IDL structure +sub Validate($) +{ + my($idl) = shift; + + foreach my $x (@{$idl}) { + ($x->{TYPE} eq "INTERFACE") && + ValidInterface($x); + ($x->{TYPE} eq "IMPORTLIB") && + fatal($x, "importlib() not supported"); + } +} + +sub is_charset_array($$) +{ + my ($e,$l) = @_; + + return 0 if ($l->{TYPE} ne "ARRAY"); + + my $nl = GetNextLevel($e,$l); + + return 0 unless ($nl->{TYPE} eq "DATA"); + + return has_property($e, "charset"); +} + + + +1; diff --git a/source/pidl/lib/Parse/Pidl/Samba3/ClientNDR.pm b/source/pidl/lib/Parse/Pidl/Samba3/ClientNDR.pm new file mode 100644 index 00000000000..684bd8a86d5 --- /dev/null +++ b/source/pidl/lib/Parse/Pidl/Samba3/ClientNDR.pm @@ -0,0 +1,246 @@ +################################################### +# Samba3 client generator for IDL structures +# on top of Samba4 style NDR functions +# Copyright jelmer@samba.org 2005-2006 +# Copyright gd@samba.org 2008 +# released under the GNU GPL + +package Parse::Pidl::Samba3::ClientNDR; + +use Exporter; +@ISA = qw(Exporter); +@EXPORT_OK = qw(ParseFunction $res $res_hdr ParseOutputArgument); + +use strict; +use Parse::Pidl qw(fatal warning); +use Parse::Pidl::Typelist qw(hasType getType mapTypeName scalar_is_reference); +use Parse::Pidl::Util qw(has_property is_constant ParseExpr); +use Parse::Pidl::NDR qw(GetPrevLevel GetNextLevel ContainsDeferred); +use Parse::Pidl::Samba4 qw(DeclLong); +use Parse::Pidl::Samba4::Header qw(GenerateFunctionInEnv); + +use vars qw($VERSION); +$VERSION = '0.01'; + +sub indent($) { my ($self) = @_; $self->{tabs}.="\t"; } +sub deindent($) { my ($self) = @_; $self->{tabs} = substr($self->{tabs}, 1); } +sub pidl($$) { my ($self,$txt) = @_; $self->{res} .= $txt ? "$self->{tabs}$txt\n" : "\n"; } +sub pidl_hdr($$) { my ($self, $txt) = @_; $self->{res_hdr} .= "$txt\n"; } +sub fn_declare($$) { my ($self,$n) = @_; $self->pidl($n); $self->pidl_hdr("$n;"); } + +sub genpad($) +{ + my ($s) = @_; + my $nt = int((length($s)+1)/8); + my $lt = ($nt*8)-1; + my $ns = (length($s)-$lt); + return "\t"x($nt)." "x($ns); +} + +sub new($) +{ + my ($class) = shift; + my $self = { res => "", res_hdr => "", tabs => "" }; + bless($self, $class); +} + +sub ElementDirection($) +{ + my ($e) = @_; + + return "[in,out]" if (has_property($e, "in") and has_property($e, "out")); + return "[in]" if (has_property($e, "in")); + return "[out]" if (has_property($e, "out")); + return "[in,out]"; +} + +sub HeaderProperties($$) +{ + my($props,$ignores) = @_; + my $ret = ""; + + foreach my $d (keys %{$props}) { + next if (grep(/^$d$/, @$ignores)); + if($props->{$d} ne "1") { + $ret.= "$d($props->{$d}),"; + } else { + $ret.="$d,"; + } + } + + if ($ret) { + return "[" . substr($ret, 0, -1) . "]"; + } +} + +sub ParseOutputArgument($$$) +{ + my ($self, $fn, $e) = @_; + my $level = 0; + + fatal($e->{ORIGINAL}, "[out] argument is not a pointer or array") if ($e->{LEVELS}[0]->{TYPE} ne "POINTER" and $e->{LEVELS}[0]->{TYPE} ne "ARRAY"); + + if ($e->{LEVELS}[0]->{TYPE} eq "POINTER") { + $level = 1; + if ($e->{LEVELS}[0]->{POINTER_TYPE} ne "ref") { + $self->pidl("if ($e->{NAME} && r.out.$e->{NAME}) {"); + $self->indent; + } + } + + if ($e->{LEVELS}[$level]->{TYPE} eq "ARRAY") { + # This is a call to GenerateFunctionInEnv intentionally. + # Since the data is being copied into a user-provided data + # structure, the user should be able to know the size beforehand + # to allocate a structure of the right size. + my $env = GenerateFunctionInEnv($fn, "r."); + my $size_is = ParseExpr($e->{LEVELS}[$level]->{SIZE_IS}, $env, $e->{ORIGINAL}); + $self->pidl("memcpy($e->{NAME}, r.out.$e->{NAME}, $size_is * sizeof(*$e->{NAME}));"); + } else { + $self->pidl("*$e->{NAME} = *r.out.$e->{NAME};"); + } + + if ($e->{LEVELS}[0]->{TYPE} eq "POINTER") { + if ($e->{LEVELS}[0]->{POINTER_TYPE} ne "ref") { + $self->deindent; + $self->pidl("}"); + } + } +} + +sub ParseFunction($$$) +{ + my ($self, $if, $fn) = @_; + + my $fn_args = ""; + my $uif = uc($if); + my $ufn = "NDR_".uc($fn->{NAME}); + my $fn_str = "NTSTATUS rpccli_$fn->{NAME}"; + my $pad = genpad($fn_str); + + $fn_args .= "struct rpc_pipe_client *cli,\n" . $pad . "TALLOC_CTX *mem_ctx"; + + foreach (@{$fn->{ELEMENTS}}) { + my $dir = ElementDirection($_); + my $prop = HeaderProperties($_->{PROPERTIES}, ["in", "out"]); + $fn_args .= ",\n" . $pad . DeclLong($_) . " /* $dir $prop */"; + } + + if (defined($fn->{RETURN_TYPE}) && ($fn->{RETURN_TYPE} eq "WERROR")) { + $fn_args .= ",\n" . $pad . "WERROR *werror"; + } + + $self->fn_declare("$fn_str($fn_args)"); + $self->pidl("{"); + $self->indent; + $self->pidl("struct $fn->{NAME} r;"); + $self->pidl("NTSTATUS status;"); + $self->pidl(""); + $self->pidl("/* In parameters */"); + + foreach (@{$fn->{ELEMENTS}}) { + if (grep(/in/, @{$_->{DIRECTION}})) { + $self->pidl("r.in.$_->{NAME} = $_->{NAME};"); + } + } + + $self->pidl(""); + $self->pidl("if (DEBUGLEVEL >= 10) {"); + $self->indent; + $self->pidl("NDR_PRINT_IN_DEBUG($fn->{NAME}, &r);"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $self->pidl("status = cli_do_rpc_ndr(cli,"); + $self->pidl("\t\t\tmem_ctx,"); + $self->pidl("\t\t\tPI_$uif,"); + $self->pidl("\t\t\t&ndr_table_$if,"); + $self->pidl("\t\t\t$ufn,"); + $self->pidl("\t\t\t&r);"); + $self->pidl(""); + + $self->pidl("if (!NT_STATUS_IS_OK(status)) {"); + $self->indent; + $self->pidl("return status;"); + $self->deindent; + $self->pidl("}"); + + $self->pidl(""); + $self->pidl("if (DEBUGLEVEL >= 10) {"); + $self->indent; + $self->pidl("NDR_PRINT_OUT_DEBUG($fn->{NAME}, &r);"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $self->pidl("if (NT_STATUS_IS_ERR(status)) {"); + $self->indent; + $self->pidl("return status;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $self->pidl("/* Return variables */"); + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless (grep(/out/, @{$e->{DIRECTION}})); + + $self->ParseOutputArgument($fn, $e); + + } + + $self->pidl(""); + $self->pidl("/* Return result */"); + if (not $fn->{RETURN_TYPE}) { + $self->pidl("return NT_STATUS_OK;"); + } elsif ($fn->{RETURN_TYPE} eq "NTSTATUS") { + $self->pidl("return r.out.result;"); + } elsif ($fn->{RETURN_TYPE} eq "WERROR") { + $self->pidl("if (werror) {"); + $self->indent; + $self->pidl("*werror = r.out.result;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $self->pidl("return werror_to_ntstatus(r.out.result);"); + } else { + warning($fn->{ORIGINAL}, "Unable to convert $fn->{RETURN_TYPE} to NTSTATUS"); + $self->pidl("return NT_STATUS_OK;"); + } + + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +sub ParseInterface($$) +{ + my ($self, $if) = @_; + + my $uif = uc($if->{NAME}); + + $self->pidl_hdr("#ifndef __CLI_$uif\__"); + $self->pidl_hdr("#define __CLI_$uif\__"); + $self->ParseFunction($if->{NAME}, $_) foreach (@{$if->{FUNCTIONS}}); + $self->pidl_hdr("#endif /* __CLI_$uif\__ */"); +} + +sub Parse($$$$) +{ + my($self,$ndr,$header,$ndr_header) = @_; + + $self->pidl("/*"); + $self->pidl(" * Unix SMB/CIFS implementation."); + $self->pidl(" * client auto-generated by pidl. DO NOT MODIFY!"); + $self->pidl(" */"); + $self->pidl(""); + $self->pidl("#include \"includes.h\""); + $self->pidl("#include \"$header\""); + $self->pidl_hdr("#include \"$ndr_header\""); + $self->pidl(""); + + foreach (@$ndr) { + $self->ParseInterface($_) if ($_->{TYPE} eq "INTERFACE"); + } + + return ($self->{res}, $self->{res_hdr}); +} + +1; diff --git a/source/pidl/lib/Parse/Pidl/Samba3/ServerNDR.pm b/source/pidl/lib/Parse/Pidl/Samba3/ServerNDR.pm new file mode 100644 index 00000000000..4af9da0f52f --- /dev/null +++ b/source/pidl/lib/Parse/Pidl/Samba3/ServerNDR.pm @@ -0,0 +1,268 @@ +################################################### +# Samba3 server generator for IDL structures +# on top of Samba4 style NDR functions +# Copyright jelmer@samba.org 2005-2006 +# released under the GNU GPL + +package Parse::Pidl::Samba3::ServerNDR; + +use Exporter; +@ISA = qw(Exporter); +@EXPORT_OK = qw(DeclLevel); + +use strict; +use Parse::Pidl qw(warning fatal); +use Parse::Pidl::Typelist qw(mapTypeName scalar_is_reference); +use Parse::Pidl::Util qw(ParseExpr has_property is_constant); +use Parse::Pidl::NDR qw(GetNextLevel); +use Parse::Pidl::Samba4 qw(ElementStars DeclLong); +use Parse::Pidl::Samba4::Header qw(GenerateFunctionOutEnv); + +use vars qw($VERSION); +$VERSION = '0.01'; + +my $res; +my $res_hdr; +my $tabs = ""; +sub indent() { $tabs.="\t"; } +sub deindent() { $tabs = substr($tabs, 1); } +sub pidl($) { my ($txt) = @_; $res .= $txt?$tabs.(shift)."\n":"\n"; } +sub pidl_hdr($) { $res_hdr .= (shift)."\n"; } +sub fn_declare($) { my ($n) = @_; pidl $n; pidl_hdr "$n;"; } + +sub DeclLevel($$) +{ + my ($e, $l) = @_; + my $res = ""; + + if (has_property($e, "charset")) { + $res .= "const char"; + } else { + $res .= mapTypeName($e->{TYPE}); + } + + my $stars = ElementStars($e, $l); + + $res .= " ".$stars unless ($stars eq ""); + + return $res; +} + +sub AllocOutVar($$$$) +{ + my ($e, $mem_ctx, $name, $env) = @_; + + my $l = $e->{LEVELS}[0]; + + # we skip pointer to arrays + if ($l->{TYPE} eq "POINTER") { + my $nl = GetNextLevel($e, $l); + $l = $nl if ($nl->{TYPE} eq "ARRAY"); + } + + # we don't support multi-dimentional arrays yet + if ($l->{TYPE} eq "ARRAY") { + my $nl = GetNextLevel($e, $l); + if ($nl->{TYPE} eq "ARRAY") { + fatal($e->{ORIGINAL},"multi-dimentional [out] arrays are not supported!"); + } + } + + if ($l->{TYPE} eq "ARRAY") { + my $size = ParseExpr($l->{SIZE_IS}, $env, $e); + pidl "$name = talloc_zero_array($mem_ctx, " . DeclLevel($e, 1) . ", $size);"; + } else { + pidl "$name = talloc_zero($mem_ctx, " . DeclLevel($e, 1) . ");"; + } + + pidl "if ($name == NULL) {"; + pidl "\ttalloc_free($mem_ctx);"; + pidl "\treturn false;"; + pidl "}"; + pidl ""; +} + +sub ParseFunction($$) +{ + my ($if,$fn) = @_; + + my $op = "NDR_".uc($fn->{NAME}); + + pidl "static bool api_$fn->{NAME}(pipes_struct *p)"; + pidl "{"; + indent; + pidl "const struct ndr_interface_call *call;"; + pidl "struct ndr_pull *pull;"; + pidl "struct ndr_push *push;"; + pidl "enum ndr_err_code ndr_err;"; + pidl "DATA_BLOB blob;"; + pidl "struct $fn->{NAME} *r;"; + pidl ""; + pidl "call = &ndr_table_$if->{NAME}.calls[$op];"; + pidl ""; + pidl "r = talloc(talloc_tos(), struct $fn->{NAME});"; + pidl "if (r == NULL) {"; + pidl "\treturn false;"; + pidl "}"; + pidl ""; + pidl "if (!prs_data_blob(&p->in_data.data, &blob, r)) {"; + pidl "\ttalloc_free(r);"; + pidl "\treturn false;"; + pidl "}"; + pidl ""; + pidl "pull = ndr_pull_init_blob(&blob, r);"; + pidl "if (pull == NULL) {"; + pidl "\ttalloc_free(r);"; + pidl "\treturn false;"; + pidl "}"; + pidl ""; + pidl "pull->flags |= LIBNDR_FLAG_REF_ALLOC;"; + pidl "ndr_err = call->ndr_pull(pull, NDR_IN, r);"; + pidl "if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {"; + pidl "\ttalloc_free(r);"; + pidl "\treturn false;"; + pidl "}"; + pidl ""; + pidl "if (DEBUGLEVEL >= 10) {"; + pidl "\tNDR_PRINT_IN_DEBUG($fn->{NAME}, r);"; + pidl "}"; + pidl ""; + + my $env = GenerateFunctionOutEnv($fn); + my $hasout = 0; + foreach (@{$fn->{ELEMENTS}}) { + if (grep(/out/, @{$_->{DIRECTION}})) { $hasout = 1; } + } + + pidl "ZERO_STRUCT(r->out);" if ($hasout); + + my $proto = "_$fn->{NAME}(pipes_struct *p, struct $fn->{NAME} *r"; + my $ret = "_$fn->{NAME}(p, r"; + foreach (@{$fn->{ELEMENTS}}) { + my @dir = @{$_->{DIRECTION}}; + if (grep(/in/, @dir) and grep(/out/, @dir)) { + pidl "r->out.$_->{NAME} = r->in.$_->{NAME};"; + } elsif (grep(/out/, @dir) and not + has_property($_, "represent_as")) { + AllocOutVar($_, "r", "r->out.$_->{NAME}", $env); + } + } + $ret .= ")"; + $proto .= ");"; + + if ($fn->{RETURN_TYPE}) { + $ret = "r->out.result = $ret"; + $proto = "$fn->{RETURN_TYPE} $proto"; + } else { + $proto = "void $proto"; + } + + pidl_hdr "$proto"; + pidl "$ret;"; + + pidl ""; + pidl "if (p->rng_fault_state) {"; + pidl "\ttalloc_free(r);"; + pidl "\t/* Return true here, srv_pipe_hnd.c will take care */"; + pidl "\treturn true;"; + pidl "}"; + pidl ""; + pidl "if (DEBUGLEVEL >= 10) {"; + pidl "\tNDR_PRINT_OUT_DEBUG($fn->{NAME}, r);"; + pidl "}"; + pidl ""; + pidl "push = ndr_push_init_ctx(r);"; + pidl "if (push == NULL) {"; + pidl "\ttalloc_free(r);"; + pidl "\treturn false;"; + pidl "}"; + pidl ""; + pidl "ndr_err = call->ndr_push(push, NDR_OUT, r);"; + pidl "if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {"; + pidl "\ttalloc_free(r);"; + pidl "\treturn false;"; + pidl "}"; + pidl ""; + pidl "blob = ndr_push_blob(push);"; + pidl "if (!prs_copy_data_in(&p->out_data.rdata, (const char *)blob.data, (uint32_t)blob.length)) {"; + pidl "\ttalloc_free(r);"; + pidl "\treturn false;"; + pidl "}"; + pidl ""; + pidl "talloc_free(r);"; + pidl ""; + pidl "return true;"; + deindent; + pidl "}"; + pidl ""; +} + +sub ParseInterface($) +{ + my $if = shift; + + my $uif = uc($if->{NAME}); + + pidl_hdr "#ifndef __SRV_$uif\__"; + pidl_hdr "#define __SRV_$uif\__"; + ParseFunction($if, $_) foreach (@{$if->{FUNCTIONS}}); + + pidl ""; + pidl "/* Tables */"; + pidl "static struct api_struct api_$if->{NAME}_cmds[] = "; + pidl "{"; + indent; + + foreach (@{$if->{FUNCTIONS}}) { + pidl "{\"" . uc($_->{NAME}) . "\", NDR_" . uc($_->{NAME}) . ", api_$_->{NAME}},"; + } + + deindent; + pidl "};"; + + pidl ""; + + pidl_hdr "void $if->{NAME}_get_pipe_fns(struct api_struct **fns, int *n_fns);"; + pidl "void $if->{NAME}_get_pipe_fns(struct api_struct **fns, int *n_fns)"; + pidl "{"; + indent; + pidl "*fns = api_$if->{NAME}_cmds;"; + pidl "*n_fns = sizeof(api_$if->{NAME}_cmds) / sizeof(struct api_struct);"; + deindent; + pidl "}"; + pidl ""; + + pidl_hdr "NTSTATUS rpc_$if->{NAME}_init(void);"; + pidl "NTSTATUS rpc_$if->{NAME}_init(void)"; + pidl "{"; + pidl "\treturn rpc_pipe_register_commands(SMB_RPC_INTERFACE_VERSION, \"$if->{NAME}\", \"$if->{NAME}\", api_$if->{NAME}_cmds, sizeof(api_$if->{NAME}_cmds) / sizeof(struct api_struct));"; + pidl "}"; + + pidl_hdr "#endif /* __SRV_$uif\__ */"; +} + +sub Parse($$$) +{ + my($ndr,$header,$ndr_header) = @_; + + $res = ""; + $res_hdr = ""; + + pidl "/*"; + pidl " * Unix SMB/CIFS implementation."; + pidl " * server auto-generated by pidl. DO NOT MODIFY!"; + pidl " */"; + pidl ""; + pidl "#include \"includes.h\""; + pidl "#include \"$header\""; + pidl_hdr "#include \"$ndr_header\""; + pidl ""; + + foreach (@$ndr) { + ParseInterface($_) if ($_->{TYPE} eq "INTERFACE"); + } + + return ($res, $res_hdr); +} + +1; diff --git a/source/pidl/lib/Parse/Pidl/Samba4.pm b/source/pidl/lib/Parse/Pidl/Samba4.pm new file mode 100644 index 00000000000..d42e01cdb04 --- /dev/null +++ b/source/pidl/lib/Parse/Pidl/Samba4.pm @@ -0,0 +1,119 @@ +################################################### +# Common Samba4 functions +# Copyright jelmer@samba.org 2006 +# released under the GNU GPL + +package Parse::Pidl::Samba4; + +require Exporter; +@ISA = qw(Exporter); +@EXPORT = qw(is_intree choose_header NumStars ElementStars ArrayBrackets DeclLong); + +use Parse::Pidl::Util qw(has_property is_constant); +use Parse::Pidl::NDR qw(GetNextLevel); +use Parse::Pidl::Typelist qw(mapTypeName scalar_is_reference); +use Parse::Pidl qw(fatal); +use strict; + +use vars qw($VERSION); +$VERSION = '0.01'; + +sub is_intree() +{ + my $srcdir = $ENV{srcdir}; + $srcdir = $srcdir ? "$srcdir/" : ""; + return 4 if (-f "${srcdir}kdc/kdc.c"); + return 3 if (-f "${srcdir}include/smb.h"); + return 0; +} + +# Return an #include line depending on whether this build is an in-tree +# build or not. +sub choose_header($$) +{ + my ($in,$out) = @_; + return "#include \"$in\"" if (is_intree()); + return "#include <$out>"; +} + +sub NumStars($;$) +{ + my ($e, $d) = @_; + $d = 0 unless defined($d); + my $n = 0; + + foreach my $l (@{$e->{LEVELS}}) { + next unless ($l->{TYPE} eq "POINTER"); + + my $nl = GetNextLevel($e, $l); + next if (defined($nl) and $nl->{TYPE} eq "ARRAY"); + + $n++; + } + + if ($n >= 1) { + $n-- if (scalar_is_reference($e->{TYPE})); + } + + foreach my $l (@{$e->{LEVELS}}) { + next unless ($l->{TYPE} eq "ARRAY"); + next if ($l->{IS_FIXED}) and not has_property($e, "charset"); + $n++; + } + + fatal($e->{ORIGINAL}, "Too few pointers $n < $d") if ($n < $d); + + $n -= $d; + + return $n; +} + +sub ElementStars($;$) +{ + my ($e, $d) = @_; + my $res = ""; + my $n = 0; + + $n = NumStars($e, $d); + $res .= "*" foreach (1..$n); + + return $res; +} + +sub ArrayBrackets($) +{ + my ($e) = @_; + my $res = ""; + + foreach my $l (@{$e->{LEVELS}}) { + next unless ($l->{TYPE} eq "ARRAY"); + next unless ($l->{IS_FIXED}) and not has_property($e, "charset"); + $res .= "[$l->{SIZE_IS}]"; + } + + return $res; +} + +sub DeclLong($) +{ + my ($e) = shift; + my $res = ""; + + if (has_property($e, "represent_as")) { + $res .= mapTypeName($e->{PROPERTIES}->{represent_as})." "; + } else { + if (has_property($e, "charset")) { + $res .= "const char "; + } else { + $res .= mapTypeName($e->{TYPE})." "; + } + + $res .= ElementStars($e); + } + $res .= $e->{NAME}; + $res .= ArrayBrackets($e); + + return $res; +} + +1; diff --git a/source/pidl/lib/Parse/Pidl/Samba4/EJS.pm b/source/pidl/lib/Parse/Pidl/Samba4/EJS.pm new file mode 100644 index 00000000000..b3231f9d70f --- /dev/null +++ b/source/pidl/lib/Parse/Pidl/Samba4/EJS.pm @@ -0,0 +1,874 @@ +################################################### +# EJS function wrapper generator +# Copyright jelmer@samba.org 2005 +# Copyright Andrew Tridgell 2005 +# released under the GNU GPL + +package Parse::Pidl::Samba4::EJS; + +use Exporter; +@ISA = qw(Exporter); +@EXPORT_OK = qw(check_null_pointer fn_declare TypeFunctionName); + +use strict; +use Parse::Pidl::Typelist qw(typeHasBody); +use Parse::Pidl::CUtil qw(get_pointer_to get_value_of); +use Parse::Pidl::Util qw(has_property ParseExpr); +use Parse::Pidl::NDR qw(GetPrevLevel GetNextLevel); +use Parse::Pidl::Samba4::Header qw(GenerateStructEnv GenerateFunctionInEnv + GenerateFunctionOutEnv); + +use vars qw($VERSION); +$VERSION = '0.01'; + +sub new($) { + my ($class) = @_; + my $self = { res => "", res_hdr => "", tabs => "", constants => {}}; + bless($self, $class); +} + +sub pidl_hdr ($$) +{ + my $self = shift; + $self->{res_hdr} .= shift; +} + +sub pidl($$) +{ + my ($self, $d) = @_; + if ($d) { + $self->{res} .= $self->{tabs}; + $self->{res} .= $d; + } + $self->{res} .= "\n"; +} + +sub indent($) +{ + my ($self) = @_; + $self->{tabs} .= "\t"; +} + +sub deindent($) +{ + my ($self) = @_; + $self->{tabs} = substr($self->{tabs}, 0, -1); +} + +##################################################################### +# check that a variable we get from ParseExpr isn't a null pointer +sub check_null_pointer($$) +{ + my ($self, $size) = @_; + if ($size =~ /^\*/) { + my $size2 = substr($size, 1); + $self->pidl("if ($size2 == NULL) return NT_STATUS_INVALID_PARAMETER_MIX;"); + } +} + +##################################################################### +# work out is a parse function should be declared static or not +sub fn_declare($$$) +{ + my ($self,$fn,$decl) = @_; + + if (has_property($fn, "public")) { + $self->pidl_hdr("$decl;\n"); + $self->pidl("_PUBLIC_ $decl"); + } else { + $self->pidl("static $decl"); + } +} + +########################### +# pull a scalar element +sub EjsPullScalar($$$$$$$) +{ + my ($self, $e, $l, $var, $name, $env) = @_; + + return if (has_property($e, "value")); + + if (ref($e->{TYPE}) eq "HASH" and not defined($e->{TYPE}->{NAME})) { + $self->EjsTypePull($e->{TYPE}, $var); + } else { + my $pl = Parse::Pidl::NDR::GetPrevLevel($e, $l); + $var = get_pointer_to($var); + # have to handle strings specially :( + if (Parse::Pidl::Typelist::scalar_is_reference($e->{TYPE}) + and (defined($pl) and $pl->{TYPE} eq "POINTER")) { + $var = get_pointer_to($var); + } + + my $t; + if (ref($e->{TYPE}) eq "HASH") { + $t = "$e->{TYPE}->{TYPE}_$e->{TYPE}->{NAME}"; + } else { + $t = $e->{TYPE}; + } + $self->pidl("EJS_CHECK(ejs_pull_$t(ejs, v, $name, $var));"); + } +} + +########################### +# pull a pointer element +sub EjsPullPointer($$$$$$) +{ + my ($self, $e, $l, $var, $name, $env) = @_; + $self->pidl("if (ejs_pull_null(ejs, v, $name)) {"); + $self->indent; + if ($l->{POINTER_TYPE} eq "ref") { + $self->pidl("return NT_STATUS_INVALID_PARAMETER_MIX;"); + } else { + $self->pidl("$var = NULL;"); + } + $self->deindent; + $self->pidl("} else {"); + $self->indent; + $self->pidl("EJS_ALLOC(ejs, $var);"); + $var = get_value_of($var); + $self->EjsPullElement($e, GetNextLevel($e, $l), $var, $name, $env); + $self->deindent; + $self->pidl("}"); +} + +########################### +# pull a string element +sub EjsPullString($$$$$$) +{ + my ($self, $e, $l, $var, $name, $env) = @_; + my $pl = GetPrevLevel($e, $l); + $var = get_pointer_to($var); + if (defined($pl) and $pl->{TYPE} eq "POINTER") { + $var = get_pointer_to($var); + } + $self->pidl("EJS_CHECK(ejs_pull_string(ejs, v, $name, $var));"); +} + +########################### +# pull an array element +sub EjsPullArray($$$$$$) +{ + my ($self, $e, $l, $var, $name, $env) = @_; + my $nl = GetNextLevel($e, $l); + my $length = ParseExpr($l->{LENGTH_IS}, $env, $e); + my $size = ParseExpr($l->{SIZE_IS}, $env, $e); + my $pl = GetPrevLevel($e, $l); + if ($pl && $pl->{TYPE} eq "POINTER") { + $var = get_pointer_to($var); + } + # uint8 arrays are treated as data blobs + if ($nl->{TYPE} eq 'DATA' && $e->{TYPE} eq 'uint8') { + if (!$l->{IS_FIXED}) { + $self->check_null_pointer($size); + $self->pidl("EJS_ALLOC_N(ejs, $var, $size);"); + } + $self->check_null_pointer($length); + $self->pidl("ejs_pull_array_uint8(ejs, v, $name, $var, $length);"); + return; + } + my $avar = $var . "[i]"; + $self->pidl("{"); + $self->indent; + $self->pidl("uint32_t i;"); + if (!$l->{IS_FIXED}) { + $self->pidl("EJS_ALLOC_N(ejs, $var, $size);"); + } + $self->pidl("for (i=0;i<$length;i++) {"); + $self->indent; + $self->pidl("char *id = talloc_asprintf(ejs, \"%s.%u\", $name, i);"); + $self->EjsPullElement($e, $nl, $avar, "id", $env); + $self->pidl("talloc_free(id);"); + $self->deindent; + $self->pidl("}"); + $self->pidl("ejs_push_uint32(ejs, v, $name \".length\", &i);"); + $self->deindent; + $self->pidl("}"); +} + +########################### +# pull a switch element +sub EjsPullSwitch($$$$$$) +{ + my ($self, $e, $l, $var, $name, $env) = @_; + my $switch_var = ParseExpr($l->{SWITCH_IS}, $env, $e); + $self->pidl("ejs_set_switch(ejs, $switch_var);"); + $self->EjsPullElement($e, GetNextLevel($e, $l), $var, $name, $env); +} + +########################### +# pull a structure element +sub EjsPullElement($$$$$$) +{ + my ($self, $e, $l, $var, $name, $env) = @_; + if (($l->{TYPE} eq "POINTER")) { + $self->EjsPullPointer($e, $l, $var, $name, $env); + } elsif (has_property($e, "charset")) { + $self->EjsPullString($e, $l, $var, $name, $env); + } elsif ($l->{TYPE} eq "ARRAY") { + $self->EjsPullArray($e, $l, $var, $name, $env); + } elsif ($l->{TYPE} eq "DATA") { + $self->EjsPullScalar($e, $l, $var, $name, $env); + } elsif (($l->{TYPE} eq "SWITCH")) { + $self->EjsPullSwitch($e, $l, $var, $name, $env); + } else { + $self->pidl("return ejs_panic(ejs, \"unhandled pull type $l->{TYPE}\");"); + } +} + +############################################# +# pull a structure/union element at top level +sub EjsPullElementTop($$$) +{ + my ($self, $e, $env) = @_; + my $l = $e->{LEVELS}[0]; + my $var = ParseExpr($e->{NAME}, $env, $e); + my $name = "\"$e->{NAME}\""; + $self->EjsPullElement($e, $l, $var, $name, $env); +} + +########################### +# pull a struct +sub EjsStructPull($$$) +{ + my ($self, $d, $varname) = @_; + my $env = GenerateStructEnv($d, $varname); + $self->pidl("EJS_CHECK(ejs_pull_struct_start(ejs, &v, name));"); + foreach my $e (@{$d->{ELEMENTS}}) { + $self->EjsPullElementTop($e, $env); + } +} + +########################### +# pull a union +sub EjsUnionPull($$$) +{ + my ($self, $d, $varname) = @_; + my $have_default = 0; + $self->pidl("EJS_CHECK(ejs_pull_struct_start(ejs, &v, name));"); + $self->pidl("switch (ejs->switch_var) {"); + $self->indent; + foreach my $e (@{$d->{ELEMENTS}}) { + if ($e->{CASE} eq "default") { + $have_default = 1; + } + $self->pidl("$e->{CASE}:"); + $self->indent; + if ($e->{TYPE} ne "EMPTY") { + $self->EjsPullElementTop($e, { $e->{NAME} => "$varname->$e->{NAME}"}); + } + $self->pidl("break;"); + $self->deindent; + } + if (! $have_default) { + $self->pidl("default:"); + $self->indent; + $self->pidl("return ejs_panic(ejs, \"Bad switch value\");"); + $self->deindent; + } + $self->deindent; + $self->pidl("}"); +} + +############################################## +# put the enum elements in the constants array +sub EjsEnumConstant($$) +{ + my ($self, $d) = @_; + return unless (defined($d->{ELEMENTS})); + my $v = 0; + foreach my $e (@{$d->{ELEMENTS}}) { + my $el = $e; + chomp $el; + if ($el =~ /^(.*)=\s*(.*)\s*$/) { + $el = $1; + $v = $2; + } + $self->{constants}->{$el} = $v; + $v++; + } +} + +########################### +# pull a enum +sub EjsEnumPull($$$) +{ + my ($self, $d, $varname) = @_; + $self->EjsEnumConstant($d); + $self->pidl("unsigned e;"); + $self->pidl("EJS_CHECK(ejs_pull_enum(ejs, v, name, &e));"); + $self->pidl("*$varname = e;"); +} + +########################### +# pull a bitmap +sub EjsBitmapPull($$$) +{ + my ($self, $d, $varname) = @_; + my $type_fn = $d->{BASE_TYPE}; + $self->pidl("EJS_CHECK(ejs_pull_$type_fn(ejs, v, name, $varname));"); +} + +sub EjsTypePullFunction($$$) +{ + sub EjsTypePullFunction($$$); + my ($self, $d, $name) = @_; + return if (has_property($d, "noejs")); + + if ($d->{TYPE} eq "TYPEDEF") { + $self->EjsTypePullFunction($d->{DATA}, $name); + return; + } + + if ($d->{TYPE} eq "STRUCT") { + $self->fn_declare($d, "NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, struct $name *r)"); + } elsif ($d->{TYPE} eq "UNION") { + $self->fn_declare($d, "NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, union $name *r)"); + } elsif ($d->{TYPE} eq "ENUM") { + $self->fn_declare($d, "NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, enum $name *r)"); + } elsif ($d->{TYPE} eq "BITMAP") { + my($type_decl) = Parse::Pidl::Typelist::mapTypeName($d->{BASE_TYPE}); + $self->fn_declare($d, "NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, $type_decl *r)"); + } + $self->pidl("{"); + $self->indent; + + $self->EjsTypePull($d, "r"); + + $self->pidl("return NT_STATUS_OK;"); + $self->deindent; + $self->pidl("}\n"); +} + +sub EjsTypePull($$$) +{ + my ($self, $d, $varname) = @_; + if ($d->{TYPE} eq 'STRUCT') { + $self->EjsStructPull($d, $varname); + } elsif ($d->{TYPE} eq 'UNION') { + $self->EjsUnionPull($d, $varname); + } elsif ($d->{TYPE} eq 'ENUM') { + $self->EjsEnumPull($d, $varname); + } elsif ($d->{TYPE} eq 'BITMAP') { + $self->EjsBitmapPull($d, $varname); + } else { + warn "Unhandled pull $varname of type $d->{TYPE}"; + } +} + +##################### +# generate a function +sub EjsPullFunction($$) +{ + my ($self, $d) = @_; + my $env = GenerateFunctionInEnv($d); + my $name = $d->{NAME}; + + $self->pidl("\nstatic NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, struct $name *r)"); + $self->pidl("{"); + $self->indent; + $self->pidl("EJS_CHECK(ejs_pull_struct_start(ejs, &v, \"input\"));"); + + # we pull non-array elements before array elements as arrays + # may have length_is() or size_is() properties that depend + # on the non-array elements + foreach my $e (@{$d->{ELEMENTS}}) { + next unless (grep(/in/, @{$e->{DIRECTION}})); + next if (has_property($e, "length_is") || has_property($e, "size_is")); + $self->EjsPullElementTop($e, $env); + } + + foreach my $e (@{$d->{ELEMENTS}}) { + next unless (grep(/in/, @{$e->{DIRECTION}})); + next unless (has_property($e, "length_is") || has_property($e, "size_is")); + $self->EjsPullElementTop($e, $env); + } + + $self->pidl("return NT_STATUS_OK;"); + $self->deindent; + $self->pidl("}\n"); +} + +########################### +# push a scalar element +sub EjsPushScalar($$$$$$) +{ + my ($self, $e, $l, $var, $name, $env) = @_; + + if (ref($e->{TYPE}) eq "HASH" and not defined($e->{TYPE}->{NAME})) { + $self->EjsTypePush($e->{TYPE}, get_pointer_to($var)); + } else { + # have to handle strings specially :( + my $pl = GetPrevLevel($e, $l); + + if ((not Parse::Pidl::Typelist::scalar_is_reference($e->{TYPE})) + or (defined($pl) and $pl->{TYPE} eq "POINTER")) { + $var = get_pointer_to($var); + } + + $self->pidl("EJS_CHECK(".TypeFunctionName("ejs_push", $e->{TYPE})."(ejs, v, $name, $var));"); + } +} + +########################### +# push a string element +sub EjsPushString($$$$$$) +{ + my ($self, $e, $l, $var, $name, $env) = @_; + my $pl = GetPrevLevel($e, $l); + if (defined($pl) and $pl->{TYPE} eq "POINTER") { + $var = get_pointer_to($var); + } + $self->pidl("EJS_CHECK(ejs_push_string(ejs, v, $name, $var));"); +} + +########################### +# push a pointer element +sub EjsPushPointer($$$$$$) +{ + my ($self, $e, $l, $var, $name, $env) = @_; + $self->pidl("if (NULL == $var) {"); + $self->indent; + if ($l->{POINTER_TYPE} eq "ref") { + $self->pidl("return NT_STATUS_INVALID_PARAMETER_MIX;"); + } else { + $self->pidl("EJS_CHECK(ejs_push_null(ejs, v, $name));"); + } + $self->deindent; + $self->pidl("} else {"); + $self->indent; + $var = get_value_of($var); + $self->EjsPushElement($e, GetNextLevel($e, $l), $var, $name, $env); + $self->deindent; + $self->pidl("}"); +} + +########################### +# push a switch element +sub EjsPushSwitch($$$$$$) +{ + my ($self, $e, $l, $var, $name, $env) = @_; + my $switch_var = ParseExpr($l->{SWITCH_IS}, $env, $e); + $self->pidl("ejs_set_switch(ejs, $switch_var);"); + $self->EjsPushElement($e, GetNextLevel($e, $l), $var, $name, $env); +} + +########################### +# push an array element +sub EjsPushArray($$$$$$) +{ + my ($self, $e, $l, $var, $name, $env) = @_; + my $nl = GetNextLevel($e, $l); + my $length = ParseExpr($l->{LENGTH_IS}, $env, $e); + my $pl = GetPrevLevel($e, $l); + if ($pl && $pl->{TYPE} eq "POINTER") { + $var = get_pointer_to($var); + } + # uint8 arrays are treated as data blobs + if ($nl->{TYPE} eq 'DATA' && $e->{TYPE} eq 'uint8') { + $self->check_null_pointer($length); + $self->pidl("ejs_push_array_uint8(ejs, v, $name, $var, $length);"); + return; + } + my $avar = $var . "[i]"; + $self->pidl("{"); + $self->indent; + $self->pidl("uint32_t i;"); + $self->pidl("for (i=0;i<$length;i++) {"); + $self->indent; + $self->pidl("const char *id = talloc_asprintf(ejs, \"%s.%u\", $name, i);"); + $self->EjsPushElement($e, $nl, $avar, "id", $env); + $self->deindent; + $self->pidl("}"); + $self->pidl("ejs_push_uint32(ejs, v, $name \".length\", &i);"); + $self->deindent; + $self->pidl("}"); +} + +################################ +# push a structure/union element +sub EjsPushElement($$$$$$) +{ + my ($self, $e, $l, $var, $name, $env) = @_; + if (($l->{TYPE} eq "POINTER")) { + $self->EjsPushPointer($e, $l, $var, $name, $env); + } elsif (has_property($e, "charset")) { + $self->EjsPushString($e, $l, $var, $name, $env); + } elsif ($l->{TYPE} eq "ARRAY") { + $self->EjsPushArray($e, $l, $var, $name, $env); + } elsif ($l->{TYPE} eq "DATA") { + $self->EjsPushScalar($e, $l, $var, $name, $env); + } elsif (($l->{TYPE} eq "SWITCH")) { + $self->EjsPushSwitch($e, $l, $var, $name, $env); + } else { + $self->pidl("return ejs_panic(ejs, \"unhandled push type $l->{TYPE}\");"); + } +} + +############################################# +# push a structure/union element at top level +sub EjsPushElementTop($$$) +{ + my ($self, $e, $env) = @_; + my $l = $e->{LEVELS}[0]; + my $var = ParseExpr($e->{NAME}, $env, $e); + my $name = "\"$e->{NAME}\""; + $self->EjsPushElement($e, $l, $var, $name, $env); +} + +########################### +# push a struct +sub EjsStructPush($$$) +{ + my ($self, $d, $varname) = @_; + my $env = GenerateStructEnv($d, $varname); + $self->pidl("EJS_CHECK(ejs_push_struct_start(ejs, &v, name));"); + foreach my $e (@{$d->{ELEMENTS}}) { + $self->EjsPushElementTop($e, $env); + } +} + +########################### +# push a union +sub EjsUnionPush($$$) +{ + my ($self, $d, $varname) = @_; + my $have_default = 0; + $self->pidl("EJS_CHECK(ejs_push_struct_start(ejs, &v, name));"); + $self->pidl("switch (ejs->switch_var) {"); + $self->indent; + foreach my $e (@{$d->{ELEMENTS}}) { + if ($e->{CASE} eq "default") { + $have_default = 1; + } + $self->pidl("$e->{CASE}:"); + $self->indent; + if ($e->{TYPE} ne "EMPTY") { + $self->EjsPushElementTop($e, { $e->{NAME} => "$varname->$e->{NAME}"} ); + } + $self->pidl("break;"); + $self->deindent; + } + if (! $have_default) { + $self->pidl("default:"); + $self->indent; + $self->pidl("return ejs_panic(ejs, \"Bad switch value\");"); + $self->deindent; + } + $self->deindent; + $self->pidl("}"); +} + +########################### +# push a enum +sub EjsEnumPush($$$) +{ + my ($self, $d, $varname) = @_; + $self->EjsEnumConstant($d); + $self->pidl("unsigned e = ".get_value_of($varname).";"); + $self->pidl("EJS_CHECK(ejs_push_enum(ejs, v, name, &e));"); +} + +########################### +# push a bitmap +sub EjsBitmapPush($$$) +{ + my ($self, $d, $varname) = @_; + return unless (defined($d->{ELEMENTS})); + my $type_fn = $d->{BASE_TYPE}; + # put the bitmap elements in the constants array + foreach my $e (@{$d->{ELEMENTS}}) { + if ($e =~ /^(\w*)\s*(.*)\s*$/) { + my $bname = $1; + my $v = $2; + $self->{constants}->{$bname} = $v; + } + } + $self->pidl("EJS_CHECK(ejs_push_$type_fn(ejs, v, name, $varname));"); +} + +sub EjsTypePushFunction($$$) +{ + sub EjsTypePushFunction($$$); + my ($self, $d, $name) = @_; + return if (has_property($d, "noejs")); + + my $var = undef; + my $dt = $d; + if ($dt->{TYPE} eq "TYPEDEF") { + $dt = $dt->{DATA}; + } + if ($dt->{TYPE} eq "STRUCT") { + $var = "const struct $name *r"; + } elsif ($dt->{TYPE} eq "UNION") { + $var = "const union $name *r"; + } elsif ($dt->{TYPE} eq "ENUM") { + $var = "const enum $name *r"; + } elsif ($dt->{TYPE} eq "BITMAP") { + my($type_decl) = Parse::Pidl::Typelist::mapTypeName($dt->{BASE_TYPE}); + $var = "const $type_decl *r"; + } + $self->fn_declare($d, "NTSTATUS ".TypeFunctionName("ejs_push", $d) . "(struct ejs_rpc *ejs, struct MprVar *v, const char *name, $var)"); + $self->pidl("{"); + $self->indent; + $self->EjsTypePush($d, "r"); + $self->pidl("return NT_STATUS_OK;"); + $self->deindent; + $self->pidl("}\n"); +} + +sub EjsTypePush($$$) +{ + sub EjsTypePush($$$); + my ($self, $d, $varname) = @_; + + if ($d->{TYPE} eq 'STRUCT') { + $self->EjsStructPush($d, $varname); + } elsif ($d->{TYPE} eq 'UNION') { + $self->EjsUnionPush($d, $varname); + } elsif ($d->{TYPE} eq 'ENUM') { + $self->EjsEnumPush($d, $varname); + } elsif ($d->{TYPE} eq 'BITMAP') { + $self->EjsBitmapPush($d, $varname); + } elsif ($d->{TYPE} eq 'TYPEDEF') { + $self->EjsTypePush($d->{DATA}, $varname); + } else { + warn "Unhandled push $varname of type $d->{TYPE}"; + } +} + +##################### +# generate a function +sub EjsPushFunction($$) +{ + my ($self, $d) = @_; + my $env = GenerateFunctionOutEnv($d); + + $self->pidl("\nstatic NTSTATUS ejs_push_$d->{NAME}(struct ejs_rpc *ejs, struct MprVar *v, const struct $d->{NAME} *r)"); + $self->pidl("{"); + $self->indent; + $self->pidl("EJS_CHECK(ejs_push_struct_start(ejs, &v, \"output\"));"); + + foreach my $e (@{$d->{ELEMENTS}}) { + next unless (grep(/out/, @{$e->{DIRECTION}})); + $self->EjsPushElementTop($e, $env); + } + + if ($d->{RETURN_TYPE}) { + $self->pidl("EJS_CHECK(".TypeFunctionName("ejs_push", $d->{RETURN_TYPE})."(ejs, v, \"result\", &r->out.result));"); + } + + $self->pidl("return NT_STATUS_OK;"); + $self->deindent; + $self->pidl("}\n"); +} + +################################# +# generate a ejs mapping function +sub EjsFunction($$$) +{ + my ($self, $d, $iface) = @_; + my $name = $d->{NAME}; + my $callnum = uc("NDR_$name"); + my $table = "&ndr_table_$iface"; + + $self->pidl("static int ejs_$name(int eid, int argc, struct MprVar **argv)"); + $self->pidl("{"); + $self->indent; + $self->pidl("return ejs_rpc_call(eid, argc, argv, $table, $callnum, (ejs_pull_function_t)ejs_pull_$name, (ejs_push_function_t)ejs_push_$name);"); + $self->deindent; + $self->pidl("}\n"); +} + +################### +# handle a constant +sub EjsConst($$) +{ + my ($self, $const) = @_; + $self->{constants}->{$const->{NAME}} = $const->{VALUE}; +} + +sub EjsImport +{ + my $self = shift; + my @imports = @_; + foreach (@imports) { + s/\.idl\"$//; + s/^\"//; + $self->pidl_hdr("#include \"librpc/gen_ndr/ndr_$_\_ejs\.h\"\n"); + } +} + +##################################################################### +# parse the interface definitions +sub EjsInterface($$$) +{ + my($self,$interface,$needed) = @_; + my @fns = (); + my $name = $interface->{NAME}; + + $self->pidl_hdr("#ifndef _HEADER_EJS_$interface->{NAME}\n"); + $self->pidl_hdr("#define _HEADER_EJS_$interface->{NAME}\n\n"); + + $self->pidl_hdr("\n"); + + foreach my $d (@{$interface->{TYPES}}) { + next unless (typeHasBody($d)); + ($needed->{TypeFunctionName("ejs_push", $d)}) && $self->EjsTypePushFunction($d, $d->{NAME}); + ($needed->{TypeFunctionName("ejs_pull", $d)}) && $self->EjsTypePullFunction($d, $d->{NAME}); + } + + foreach my $d (@{$interface->{FUNCTIONS}}) { + next if not defined($d->{OPNUM}); + next if has_property($d, "noejs"); + + $self->EjsPullFunction($d); + $self->EjsPushFunction($d); + $self->EjsFunction($d, $name); + + push (@fns, $d->{NAME}); + } + + foreach my $d (@{$interface->{CONSTS}}) { + $self->EjsConst($d); + } + + $self->pidl("static int ejs_$name\_init(int eid, int argc, struct MprVar **argv)"); + $self->pidl("{"); + $self->indent; + $self->pidl("struct MprVar *obj = mprInitObject(eid, \"$name\", argc, argv);"); + foreach (@fns) { + $self->pidl("mprSetCFunction(obj, \"$_\", ejs_$_);"); + } + foreach my $v (keys %{$self->{constants}}) { + my $value = $self->{constants}->{$v}; + if (substr($value, 0, 1) eq "\"") { + $self->pidl("mprSetVar(obj, \"$v\", mprString($value));"); + } else { + $self->pidl("mprSetVar(obj, \"$v\", mprCreateNumberVar($value));"); + } + } + $self->pidl("return ejs_rpc_init(obj, \"$name\");"); + $self->deindent; + $self->pidl("}\n"); + + $self->pidl("NTSTATUS ejs_init_$name(void)"); + $self->pidl("{"); + $self->indent; + $self->pidl("ejsDefineCFunction(-1, \"$name\_init\", ejs_$name\_init, NULL, MPR_VAR_SCRIPT_HANDLE);"); + $self->pidl("return NT_STATUS_OK;"); + $self->deindent; + $self->pidl("}"); + + $self->pidl_hdr("\n"); + $self->pidl_hdr("#endif /* _HEADER_EJS_$interface->{NAME} */\n"); +} + +##################################################################### +# parse a parsed IDL into a C header +sub Parse($$$) +{ + my($self,$ndr,$hdr) = @_; + + my $ejs_hdr = $hdr; + $ejs_hdr =~ s/.h$/_ejs.h/; + + $self->pidl_hdr("/* header auto-generated by pidl */\n\n"); + + $self->pidl(" +/* EJS wrapper functions auto-generated by pidl */ +#include \"includes.h\" +#include \"librpc/rpc/dcerpc.h\" +#include \"lib/appweb/ejs/ejs.h\" +#include \"scripting/ejs/ejsrpc.h\" +#include \"scripting/ejs/smbcalls.h\" +#include \"librpc/gen_ndr/ndr_misc_ejs.h\" +#include \"$hdr\" +#include \"$ejs_hdr\" + +"); + + my %needed = (); + + foreach my $x (@{$ndr}) { + ($x->{TYPE} eq "INTERFACE") && NeededInterface($x, \%needed); + } + + foreach my $x (@$ndr) { + ($x->{TYPE} eq "INTERFACE") && $self->EjsInterface($x, \%needed); + ($x->{TYPE} eq "IMPORT") && $self->EjsImport(@{$x->{PATHS}}); + } + + return ($self->{res_hdr}, $self->{res}); +} + +sub NeededFunction($$) +{ + my ($fn,$needed) = @_; + + $needed->{"ejs_pull_$fn->{NAME}"} = 1; + $needed->{"ejs_push_$fn->{NAME}"} = 1; + + foreach (@{$fn->{ELEMENTS}}) { + next if (has_property($_, "subcontext")); #FIXME: Support subcontexts + if (grep(/in/, @{$_->{DIRECTION}})) { + $needed->{TypeFunctionName("ejs_pull", $_->{TYPE})} = 1; + } + if (grep(/out/, @{$_->{DIRECTION}})) { + $needed->{TypeFunctionName("ejs_push", $_->{TYPE})} = 1; + } + } +} + +sub NeededType($$$) +{ + sub NeededType($$$); + my ($t,$needed,$req) = @_; + + NeededType($t->{DATA}, $needed, $req) if ($t->{TYPE} eq "TYPEDEF"); + + return unless (($t->{TYPE} eq "STRUCT") or ($t->{TYPE} eq "UNION")); + + return unless(typeHasBody($t)); + + foreach (@{$t->{ELEMENTS}}) { + next if (has_property($_, "subcontext")); #FIXME: Support subcontexts + my $n; + if (ref($_->{TYPE}) ne "HASH" or defined($_->{TYPE}->{NAME})) { + $needed->{TypeFunctionName("ejs_$req", $_->{TYPE})} = 1; + } + NeededType($_->{TYPE}, $needed, $req) if (ref($_->{TYPE}) eq "HASH"); + } +} + +##################################################################### +# work out what parse functions are needed +sub NeededInterface($$) +{ + my ($interface,$needed) = @_; + + NeededFunction($_, $needed) foreach (@{$interface->{FUNCTIONS}}); + + foreach (reverse @{$interface->{TYPES}}) { + if (has_property($_, "public")) { + $needed->{TypeFunctionName("ejs_pull", $_)} = not has_property($_, "noejs"); + $needed->{TypeFunctionName("ejs_push", $_)} = not has_property($_, "noejs"); + } + + NeededType($_, $needed, "pull") if ($needed->{TypeFunctionName("ejs_pull", $_)}); + NeededType($_, $needed, "push") if ($needed->{TypeFunctionName("ejs_push", $_)}); + } +} + +sub TypeFunctionName($$) +{ + my ($prefix, $t) = @_; + + return "$prefix\_$t->{NAME}" if (ref($t) eq "HASH" and + $t->{TYPE} eq "TYPEDEF"); + return "$prefix\_$t->{TYPE}_$t->{NAME}" if (ref($t) eq "HASH"); + return "$prefix\_$t"; +} + + + +1; diff --git a/source/pidl/lib/Parse/Pidl/Samba4/Header.pm b/source/pidl/lib/Parse/Pidl/Samba4/Header.pm new file mode 100644 index 00000000000..b19fdd3adfe --- /dev/null +++ b/source/pidl/lib/Parse/Pidl/Samba4/Header.pm @@ -0,0 +1,475 @@ +################################################### +# create C header files for an IDL structure +# Copyright tridge@samba.org 2000 +# Copyright jelmer@samba.org 2005 +# released under the GNU GPL + +package Parse::Pidl::Samba4::Header; + +require Exporter; + +@ISA = qw(Exporter); +@EXPORT_OK = qw(GenerateFunctionInEnv GenerateFunctionOutEnv EnvSubstituteValue GenerateStructEnv); + +use strict; +use Parse::Pidl qw(fatal); +use Parse::Pidl::Typelist qw(mapTypeName scalar_is_reference); +use Parse::Pidl::Util qw(has_property is_constant unmake_str ParseExpr); +use Parse::Pidl::Samba4 qw(is_intree ElementStars ArrayBrackets choose_header); + +use vars qw($VERSION); +$VERSION = '0.01'; + +my($res); +my($tab_depth); + +sub pidl($) { $res .= shift; } + +sub tabs() +{ + my $res = ""; + $res .="\t" foreach (1..$tab_depth); + return $res; +} + +##################################################################### +# parse a properties list +sub HeaderProperties($$) +{ + my($props,$ignores) = @_; + my $ret = ""; + + foreach my $d (keys %{$props}) { + next if (grep(/^$d$/, @$ignores)); + if($props->{$d} ne "1") { + $ret.= "$d($props->{$d}),"; + } else { + $ret.="$d,"; + } + } + + if ($ret) { + pidl "/* [" . substr($ret, 0, -1) . "] */"; + } +} + +##################################################################### +# parse a structure element +sub HeaderElement($) +{ + my($element) = shift; + + pidl tabs(); + if (has_property($element, "represent_as")) { + pidl mapTypeName($element->{PROPERTIES}->{represent_as})." "; + } else { + if (ref($element->{TYPE}) eq "HASH") { + HeaderType($element, $element->{TYPE}, $element->{TYPE}->{NAME}); + } else { + HeaderType($element, $element->{TYPE}, ""); + } + pidl " ".ElementStars($element); + } + pidl $element->{NAME}; + pidl ArrayBrackets($element); + + pidl ";"; + if (defined $element->{PROPERTIES}) { + HeaderProperties($element->{PROPERTIES}, ["in", "out"]); + } + pidl "\n"; +} + +##################################################################### +# parse a struct +sub HeaderStruct($$;$) +{ + my($struct,$name,$tail) = @_; + pidl "struct $name"; + pidl $tail if defined($tail) and not defined($struct->{ELEMENTS}); + return if (not defined($struct->{ELEMENTS})); + pidl " {\n"; + $tab_depth++; + my $el_count=0; + foreach (@{$struct->{ELEMENTS}}) { + HeaderElement($_); + $el_count++; + } + if ($el_count == 0) { + # some compilers can't handle empty structures + pidl tabs()."char _empty_;\n"; + } + $tab_depth--; + pidl tabs()."}"; + if (defined $struct->{PROPERTIES}) { + HeaderProperties($struct->{PROPERTIES}, []); + } + pidl $tail if defined($tail); +} + +##################################################################### +# parse a enum +sub HeaderEnum($$;$) +{ + my($enum,$name,$tail) = @_; + my $first = 1; + + pidl "enum $name"; + if (defined($enum->{ELEMENTS})) { + pidl "\n#ifndef USE_UINT_ENUMS\n"; + pidl " {\n"; + $tab_depth++; + foreach my $e (@{$enum->{ELEMENTS}}) { + unless ($first) { pidl ",\n"; } + $first = 0; + pidl tabs(); + pidl $e; + } + pidl "\n"; + $tab_depth--; + pidl "}"; + pidl "\n"; + pidl "#else\n"; + my $count = 0; + my $with_val = 0; + my $without_val = 0; + pidl " { __donnot_use_enum_$name=0x7FFFFFFF}\n"; + foreach my $e (@{$enum->{ELEMENTS}}) { + my $t = "$e"; + my $name; + my $value; + if ($t =~ /(.*)=(.*)/) { + $name = $1; + $value = $2; + $with_val = 1; + fatal($e->{ORIGINAL}, "you can't mix enum member with values and without values!") + unless ($without_val == 0); + } else { + $name = $t; + $value = $count++; + $without_val = 1; + fatal($e->{ORIGINAL}, "you can't mix enum member with values and without values!") + unless ($with_val == 0); + } + pidl "#define $name ( $value )\n"; + } + pidl "#endif\n"; + } + pidl $tail if defined($tail); +} + +##################################################################### +# parse a bitmap +sub HeaderBitmap($$) +{ + my($bitmap,$name) = @_; + + return unless defined($bitmap->{ELEMENTS}); + + pidl "/* bitmap $name */\n"; + pidl "#define $_\n" foreach (@{$bitmap->{ELEMENTS}}); + pidl "\n"; +} + +##################################################################### +# parse a union +sub HeaderUnion($$;$) +{ + my($union,$name,$tail) = @_; + my %done = (); + + pidl "union $name"; + pidl $tail if defined($tail) and not defined($union->{ELEMENTS}); + return if (not defined($union->{ELEMENTS})); + pidl " {\n"; + $tab_depth++; + foreach my $e (@{$union->{ELEMENTS}}) { + if ($e->{TYPE} ne "EMPTY") { + if (! defined $done{$e->{NAME}}) { + HeaderElement($e); + } + $done{$e->{NAME}} = 1; + } + } + $tab_depth--; + pidl "}"; + + if (defined $union->{PROPERTIES}) { + HeaderProperties($union->{PROPERTIES}, []); + } + pidl $tail if defined($tail); +} + +##################################################################### +# parse a type +sub HeaderType($$$;$) +{ + my($e,$data,$name,$tail) = @_; + if (ref($data) eq "HASH") { + ($data->{TYPE} eq "ENUM") && HeaderEnum($data, $name, $tail); + ($data->{TYPE} eq "BITMAP") && HeaderBitmap($data, $name); + ($data->{TYPE} eq "STRUCT") && HeaderStruct($data, $name, $tail); + ($data->{TYPE} eq "UNION") && HeaderUnion($data, $name, $tail); + return; + } + + if (has_property($e, "charset")) { + pidl "const char"; + } else { + pidl mapTypeName($e->{TYPE}); + } + pidl $tail if defined($tail); +} + +##################################################################### +# parse a typedef +sub HeaderTypedef($;$) +{ + my($typedef,$tail) = @_; + HeaderType($typedef, $typedef->{DATA}, $typedef->{NAME}, $tail) if defined ($typedef->{DATA}); +} + +##################################################################### +# parse a const +sub HeaderConst($) +{ + my($const) = shift; + if (!defined($const->{ARRAY_LEN}[0])) { + pidl "#define $const->{NAME}\t( $const->{VALUE} )\n"; + } else { + pidl "#define $const->{NAME}\t $const->{VALUE}\n"; + } +} + +sub ElementDirection($) +{ + my ($e) = @_; + + return "inout" if (has_property($e, "in") and has_property($e, "out")); + return "in" if (has_property($e, "in")); + return "out" if (has_property($e, "out")); + return "inout"; +} + +##################################################################### +# parse a function +sub HeaderFunctionInOut($$) +{ + my($fn,$prop) = @_; + + return unless defined($fn->{ELEMENTS}); + + foreach my $e (@{$fn->{ELEMENTS}}) { + HeaderElement($e) if (ElementDirection($e) eq $prop); + } +} + +##################################################################### +# determine if we need an "in" or "out" section +sub HeaderFunctionInOut_needed($$) +{ + my($fn,$prop) = @_; + + return 1 if ($prop eq "out" && defined($fn->{RETURN_TYPE})); + + return undef unless defined($fn->{ELEMENTS}); + + foreach my $e (@{$fn->{ELEMENTS}}) { + return 1 if (ElementDirection($e) eq $prop); + } + + return undef; +} + +my %headerstructs; + +##################################################################### +# parse a function +sub HeaderFunction($) +{ + my($fn) = shift; + + return if ($headerstructs{$fn->{NAME}}); + + $headerstructs{$fn->{NAME}} = 1; + + pidl "\nstruct $fn->{NAME} {\n"; + $tab_depth++; + my $needed = 0; + + if (HeaderFunctionInOut_needed($fn, "in") or + HeaderFunctionInOut_needed($fn, "inout")) { + pidl tabs()."struct {\n"; + $tab_depth++; + HeaderFunctionInOut($fn, "in"); + HeaderFunctionInOut($fn, "inout"); + $tab_depth--; + pidl tabs()."} in;\n\n"; + $needed++; + } + + if (HeaderFunctionInOut_needed($fn, "out") or + HeaderFunctionInOut_needed($fn, "inout")) { + pidl tabs()."struct {\n"; + $tab_depth++; + HeaderFunctionInOut($fn, "out"); + HeaderFunctionInOut($fn, "inout"); + if (defined($fn->{RETURN_TYPE})) { + pidl tabs().mapTypeName($fn->{RETURN_TYPE}) . " result;\n"; + } + $tab_depth--; + pidl tabs()."} out;\n\n"; + $needed++; + } + + if (!$needed) { + # sigh - some compilers don't like empty structures + pidl tabs()."int _dummy_element;\n"; + } + + $tab_depth--; + pidl "};\n\n"; +} + +sub HeaderImport +{ + my @imports = @_; + foreach (@imports) { + s/\.idl\"$//; + s/^\"//; + pidl choose_header("librpc/gen_ndr/$_\.h", "gen_ndr/$_.h") . "\n"; + } +} + +sub HeaderInclude +{ + my @includes = @_; + foreach (@includes) { + pidl "#include $_\n"; + } +} + +##################################################################### +# parse the interface definitions +sub HeaderInterface($) +{ + my($interface) = shift; + + pidl "#ifndef _HEADER_$interface->{NAME}\n"; + pidl "#define _HEADER_$interface->{NAME}\n\n"; + + foreach my $c (@{$interface->{CONSTS}}) { + HeaderConst($c); + } + + foreach my $t (@{$interface->{TYPES}}) { + HeaderTypedef($t, ";\n\n") if ($t->{TYPE} eq "TYPEDEF"); + HeaderStruct($t, $t->{NAME}, ";\n\n") if ($t->{TYPE} eq "STRUCT"); + HeaderUnion($t, $t->{NAME}, ";\n\n") if ($t->{TYPE} eq "UNION"); + HeaderEnum($t, $t->{NAME}, ";\n\n") if ($t->{TYPE} eq "ENUM"); + HeaderBitmap($t, $t->{NAME}) if ($t->{TYPE} eq "BITMAP"); + } + + foreach my $fn (@{$interface->{FUNCTIONS}}) { + HeaderFunction($fn); + } + + pidl "#endif /* _HEADER_$interface->{NAME} */\n"; +} + +sub HeaderQuote($) +{ + my($quote) = shift; + + pidl unmake_str($quote->{DATA}) . "\n"; +} + +##################################################################### +# parse a parsed IDL into a C header +sub Parse($) +{ + my($ndr) = shift; + $tab_depth = 0; + + $res = ""; + %headerstructs = (); + pidl "/* header auto-generated by pidl */\n\n"; + if (!is_intree()) { + pidl "#include <util/data_blob.h>\n"; + } + pidl "#include <stdint.h>\n"; + pidl "\n"; + + foreach (@{$ndr}) { + ($_->{TYPE} eq "CPP_QUOTE") && HeaderQuote($_); + ($_->{TYPE} eq "INTERFACE") && HeaderInterface($_); + ($_->{TYPE} eq "IMPORT") && HeaderImport(@{$_->{PATHS}}); + ($_->{TYPE} eq "INCLUDE") && HeaderInclude(@{$_->{PATHS}}); + } + + return $res; +} + +sub GenerateStructEnv($$) +{ + my ($x, $v) = @_; + my %env; + + foreach my $e (@{$x->{ELEMENTS}}) { + $env{$e->{NAME}} = "$v->$e->{NAME}"; + } + + $env{"this"} = $v; + + return \%env; +} + +sub EnvSubstituteValue($$) +{ + my ($env,$s) = @_; + + # Substitute the value() values in the env + foreach my $e (@{$s->{ELEMENTS}}) { + next unless (defined(my $v = has_property($e, "value"))); + + $env->{$e->{NAME}} = ParseExpr($v, $env, $e); + } + + return $env; +} + +sub GenerateFunctionInEnv($;$) +{ + my ($fn, $base) = @_; + my %env; + + $base = "r->" unless defined($base); + + foreach my $e (@{$fn->{ELEMENTS}}) { + if (grep (/in/, @{$e->{DIRECTION}})) { + $env{$e->{NAME}} = $base."in.$e->{NAME}"; + } + } + + return \%env; +} + +sub GenerateFunctionOutEnv($;$) +{ + my ($fn, $base) = @_; + my %env; + + $base = "r->" unless defined($base); + + foreach my $e (@{$fn->{ELEMENTS}}) { + if (grep (/out/, @{$e->{DIRECTION}})) { + $env{$e->{NAME}} = $base."out.$e->{NAME}"; + } elsif (grep (/in/, @{$e->{DIRECTION}})) { + $env{$e->{NAME}} = $base."in.$e->{NAME}"; + } + } + + return \%env; +} + +1; diff --git a/source/pidl/lib/Parse/Pidl/Samba4/NDR/Client.pm b/source/pidl/lib/Parse/Pidl/Samba4/NDR/Client.pm new file mode 100644 index 00000000000..14a5d41075a --- /dev/null +++ b/source/pidl/lib/Parse/Pidl/Samba4/NDR/Client.pm @@ -0,0 +1,156 @@ +################################################### +# client calls generator +# Copyright tridge@samba.org 2003 +# Copyright jelmer@samba.org 2005-2006 +# released under the GNU GPL + +package Parse::Pidl::Samba4::NDR::Client; + +use Parse::Pidl::Samba4 qw(choose_header is_intree); +use Parse::Pidl::Util qw(has_property); + +use vars qw($VERSION); +$VERSION = '0.01'; + +use strict; + +my($res,$res_hdr); + +sub ParseFunctionSend($$$) +{ + my ($interface, $fn, $name) = @_; + my $uname = uc $name; + + my $proto = "struct rpc_request *dcerpc_$name\_send(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, struct $name *r)"; + + $res_hdr .= "\n$proto;\n"; + + $res .= "$proto\n{\n"; + + if (has_property($fn, "todo")) { + $res .= "\treturn NULL;\n"; + } else { + $res .= " + if (p->conn->flags & DCERPC_DEBUG_PRINT_IN) { + NDR_PRINT_IN_DEBUG($name, r); + } + + return dcerpc_ndr_request_send(p, NULL, &ndr_table_$interface->{NAME}, NDR_$uname, mem_ctx, r); +"; + } + + $res .= "}\n\n"; +} + +sub ParseFunctionSync($$$) +{ + my ($interface, $fn, $name) = @_; + + my $proto = "NTSTATUS dcerpc_$name(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, struct $name *r)"; + + $res_hdr .= "\n$proto;\n"; + $res .= "$proto\n{\n"; + + if (has_property($fn, "todo")) { + $res .= "\treturn NT_STATUS_NOT_IMPLEMENTED;\n"; + } else { + $res .= " + struct rpc_request *req; + NTSTATUS status; + + req = dcerpc_$name\_send(p, mem_ctx, r); + if (req == NULL) return NT_STATUS_NO_MEMORY; + + status = dcerpc_ndr_request_recv(req); + + if (NT_STATUS_IS_OK(status) && (p->conn->flags & DCERPC_DEBUG_PRINT_OUT)) { + NDR_PRINT_OUT_DEBUG($name, r); + } +"; + + if (defined($fn->{RETURN_TYPE}) and $fn->{RETURN_TYPE} eq "NTSTATUS") { + $res .= "\tif (NT_STATUS_IS_OK(status)) status = r->out.result;\n"; + } + $res .= +" + return status; +"; + } + + $res .= "}\n\n"; +} + +##################################################################### +# parse a function +sub ParseFunction($$) +{ + my ($interface, $fn) = @_; + + ParseFunctionSend($interface, $fn, $fn->{NAME}); + ParseFunctionSync($interface, $fn, $fn->{NAME}); +} + +my %done; + +##################################################################### +# parse the interface definitions +sub ParseInterface($) +{ + my($interface) = shift; + + $res_hdr .= "#ifndef _HEADER_RPC_$interface->{NAME}\n"; + $res_hdr .= "#define _HEADER_RPC_$interface->{NAME}\n\n"; + + if (defined $interface->{PROPERTIES}->{uuid}) { + $res_hdr .= "extern const struct ndr_interface_table ndr_table_$interface->{NAME};\n"; + } + + $res .= "/* $interface->{NAME} - client functions generated by pidl */\n\n"; + + foreach my $fn (@{$interface->{FUNCTIONS}}) { + next if not defined($fn->{OPNUM}); + next if defined($done{$fn->{NAME}}); + ParseFunction($interface, $fn); + $done{$fn->{NAME}} = 1; + } + + $res_hdr .= "#endif /* _HEADER_RPC_$interface->{NAME} */\n"; + + return $res; +} + +sub Parse($$$$) +{ + my($ndr,$header,$ndr_header,$client_header) = @_; + + $res = ""; + $res_hdr = ""; + + $res .= "/* client functions auto-generated by pidl */\n"; + $res .= "\n"; + if (is_intree()) { + $res .= "#include \"includes.h\"\n"; + } else { + $res .= "#define _GNU_SOURCE\n"; + $res .= "#include <stdio.h>\n"; + $res .= "#include <stdbool.h>\n"; + $res .= "#include <stdlib.h>\n"; + $res .= "#include <stdint.h>\n"; + $res .= "#include <stdarg.h>\n"; + $res .= "#include <core/ntstatus.h>\n"; + } + $res .= "#include \"$ndr_header\"\n"; + $res .= "#include \"$client_header\"\n"; + $res .= "\n"; + + $res_hdr .= choose_header("librpc/rpc/dcerpc.h", "dcerpc.h")."\n"; + $res_hdr .= "#include \"$header\"\n"; + + foreach my $x (@{$ndr}) { + ($x->{TYPE} eq "INTERFACE") && ParseInterface($x); + } + + return ($res,$res_hdr); +} + +1; diff --git a/source/pidl/lib/Parse/Pidl/Samba4/NDR/Parser.pm b/source/pidl/lib/Parse/Pidl/Samba4/NDR/Parser.pm new file mode 100644 index 00000000000..a494a114707 --- /dev/null +++ b/source/pidl/lib/Parse/Pidl/Samba4/NDR/Parser.pm @@ -0,0 +1,2668 @@ +################################################### +# Samba4 NDR parser generator for IDL structures +# Copyright tridge@samba.org 2000-2003 +# Copyright tpot@samba.org 2001 +# Copyright jelmer@samba.org 2004-2006 +# released under the GNU GPL + +package Parse::Pidl::Samba4::NDR::Parser; + +require Exporter; +@ISA = qw(Exporter); +@EXPORT_OK = qw(check_null_pointer NeededFunction NeededElement NeededType $res NeededInterface TypeFunctionName ParseElementPrint); + +use strict; +use Parse::Pidl::Typelist qw(hasType getType mapTypeName typeHasBody); +use Parse::Pidl::Util qw(has_property ParseExpr ParseExprExt print_uuid); +use Parse::Pidl::CUtil qw(get_pointer_to get_value_of get_array_element); +use Parse::Pidl::NDR qw(GetPrevLevel GetNextLevel ContainsDeferred is_charset_array); +use Parse::Pidl::Samba4 qw(is_intree choose_header); +use Parse::Pidl::Samba4::Header qw(GenerateFunctionInEnv GenerateFunctionOutEnv EnvSubstituteValue GenerateStructEnv); +use Parse::Pidl qw(warning); + +use vars qw($VERSION); +$VERSION = '0.01'; + +# list of known types +my %typefamily; + +sub new($$) { + my ($class) = @_; + my $self = { res => "", res_hdr => "", deferred => [], tabs => "", defer_tabs => "" }; + bless($self, $class); +} + +sub get_typefamily($) +{ + my $n = shift; + return $typefamily{$n}; +} + +sub append_prefix($$) +{ + my ($e, $var_name) = @_; + my $pointers = 0; + my $arrays = 0; + + foreach my $l (@{$e->{LEVELS}}) { + if ($l->{TYPE} eq "POINTER") { + $pointers++; + } elsif ($l->{TYPE} eq "ARRAY") { + $arrays++; + if (($pointers == 0) and + (not $l->{IS_FIXED}) and + (not $l->{IS_INLINE})) { + return get_value_of($var_name); + } + } elsif ($l->{TYPE} eq "DATA") { + if (Parse::Pidl::Typelist::scalar_is_reference($l->{DATA_TYPE})) { + return get_value_of($var_name) unless ($pointers or $arrays); + } + } + } + + return $var_name; +} + +sub has_fast_array($$) +{ + my ($e,$l) = @_; + + return 0 if ($l->{TYPE} ne "ARRAY"); + + my $nl = GetNextLevel($e,$l); + return 0 unless ($nl->{TYPE} eq "DATA"); + return 0 unless (hasType($nl->{DATA_TYPE})); + + my $t = getType($nl->{DATA_TYPE}); + + # Only uint8 and string have fast array functions at the moment + return ($t->{NAME} eq "uint8") or ($t->{NAME} eq "string"); +} + + +#################################### +# pidl() is our basic output routine +sub pidl($$) +{ + my ($self, $d) = @_; + if ($d) { + $self->{res} .= $self->{tabs}; + $self->{res} .= $d; + } + $self->{res} .="\n"; +} + +sub pidl_hdr($$) { my ($self, $d) = @_; $self->{res_hdr} .= "$d\n"; } + +#################################### +# defer() is like pidl(), but adds to +# a deferred buffer which is then added to the +# output buffer at the end of the structure/union/function +# This is needed to cope with code that must be pushed back +# to the end of a block of elements +sub defer_indent($) { my ($self) = @_; $self->{defer_tabs}.="\t"; } +sub defer_deindent($) { my ($self) = @_; $self->{defer_tabs}=substr($self->{defer_tabs}, 0, -1); } + +sub defer($$) +{ + my ($self, $d) = @_; + if ($d) { + push(@{$self->{deferred}}, $self->{defer_tabs}.$d); + } +} + +######################################## +# add the deferred content to the current +# output +sub add_deferred($) +{ + my ($self) = @_; + $self->pidl($_) foreach (@{$self->{deferred}}); + $self->{deferred} = []; + $self->{defer_tabs} = ""; +} + +sub indent($) +{ + my ($self) = @_; + $self->{tabs} .= "\t"; +} + +sub deindent($) +{ + my ($self) = @_; + $self->{tabs} = substr($self->{tabs}, 0, -1); +} + +##################################################################### +# declare a function public or static, depending on its attributes +sub fn_declare($$$$) +{ + my ($self,$type,$fn,$decl) = @_; + + if (has_property($fn, "no$type")) { + $self->pidl_hdr("$decl;"); + return 0; + } + + if (has_property($fn, "public")) { + $self->pidl_hdr("$decl;"); + $self->pidl("_PUBLIC_ $decl"); + } else { + $self->pidl("static $decl"); + } + + return 1; +} + +################################################################### +# setup any special flags for an element or structure +sub start_flags($$) +{ + my ($self, $e) = @_; + my $flags = has_property($e, "flag"); + if (defined $flags) { + $self->pidl("{"); + $self->indent; + $self->pidl("uint32_t _flags_save_$e->{TYPE} = ndr->flags;"); + $self->pidl("ndr_set_flags(&ndr->flags, $flags);"); + } +} + +################################################################### +# end any special flags for an element or structure +sub end_flags($$) +{ + my ($self, $e) = @_; + my $flags = has_property($e, "flag"); + if (defined $flags) { + $self->pidl("ndr->flags = _flags_save_$e->{TYPE};"); + $self->deindent; + $self->pidl("}"); + } +} + +##################################################################### +# parse the data of an array - push side +sub ParseArrayPushHeader($$$$$$) +{ + my ($self,$e,$l,$ndr,$var_name,$env) = @_; + + my $size; + my $length; + + if ($l->{IS_ZERO_TERMINATED}) { + if (has_property($e, "charset")) { + $size = $length = "ndr_charset_length($var_name, CH_$e->{PROPERTIES}->{charset})"; + } else { + $size = $length = "ndr_string_length($var_name, sizeof(*$var_name))"; + } + } else { + $size = ParseExpr($l->{SIZE_IS}, $env, $e); + $length = ParseExpr($l->{LENGTH_IS}, $env, $e); + } + + if ((!$l->{IS_SURROUNDING}) and $l->{IS_CONFORMANT}) { + $self->pidl("NDR_CHECK(ndr_push_uint32($ndr, NDR_SCALARS, $size));"); + } + + if ($l->{IS_VARYING}) { + $self->pidl("NDR_CHECK(ndr_push_uint32($ndr, NDR_SCALARS, 0));"); # array offset + $self->pidl("NDR_CHECK(ndr_push_uint32($ndr, NDR_SCALARS, $length));"); + } + + return $length; +} + +sub check_fully_dereferenced($$) +{ + my ($element, $env) = @_; + + return sub ($) { + my $origvar = shift; + my $check = 0; + + # Figure out the number of pointers in $ptr + my $expandedvar = $origvar; + $expandedvar =~ s/^(\**)//; + my $ptr = $1; + + my $var = undef; + foreach (keys %$env) { + if ($env->{$_} eq $expandedvar) { + $var = $_; + last; + } + } + + return($origvar) unless (defined($var)); + my $e; + foreach (@{$element->{PARENT}->{ELEMENTS}}) { + if ($_->{NAME} eq $var) { + $e = $_; + last; + } + } + + $e or die("Environment doesn't match siblings"); + + # See if pointer at pointer level $level + # needs to be checked. + my $nump = 0; + foreach (@{$e->{LEVELS}}) { + if ($_->{TYPE} eq "POINTER") { + $nump = $_->{POINTER_INDEX}+1; + } + } + warning($element->{ORIGINAL}, "Got pointer for `$e->{NAME}', expected fully derefenced variable") if ($nump > length($ptr)); + return ($origvar); + } +} + +sub check_null_pointer($$$$) +{ + my ($element, $env, $print_fn, $return) = @_; + + return sub ($) { + my $expandedvar = shift; + my $check = 0; + + # Figure out the number of pointers in $ptr + $expandedvar =~ s/^(\**)//; + my $ptr = $1; + + my $var = undef; + foreach (keys %$env) { + if ($env->{$_} eq $expandedvar) { + $var = $_; + last; + } + } + + if (defined($var)) { + my $e; + # lookup ptr in $e + foreach (@{$element->{PARENT}->{ELEMENTS}}) { + if ($_->{NAME} eq $var) { + $e = $_; + last; + } + } + + $e or die("Environment doesn't match siblings"); + + # See if pointer at pointer level $level + # needs to be checked. + foreach my $l (@{$e->{LEVELS}}) { + if ($l->{TYPE} eq "POINTER" and + $l->{POINTER_INDEX} == length($ptr)) { + # No need to check ref pointers + $check = ($l->{POINTER_TYPE} ne "ref"); + last; + } + + if ($l->{TYPE} eq "DATA") { + warning($element, "too much dereferences for `$var'"); + } + } + } else { + warning($element, "unknown dereferenced expression `$expandedvar'"); + $check = 1; + } + + $print_fn->("if ($ptr$expandedvar == NULL) $return") if $check; + } +} + +##################################################################### +# parse an array - pull side +sub ParseArrayPullHeader($$$$$$) +{ + my ($self,$e,$l,$ndr,$var_name,$env) = @_; + + my $length; + my $size; + + if ($l->{IS_CONFORMANT}) { + $length = $size = "ndr_get_array_size($ndr, " . get_pointer_to($var_name) . ")"; + } elsif ($l->{IS_ZERO_TERMINATED}) { # Noheader arrays + $length = $size = "ndr_get_string_size($ndr, sizeof(*$var_name))"; + } else { + $length = $size = ParseExprExt($l->{SIZE_IS}, $env, $e->{ORIGINAL}, + check_null_pointer($e, $env, sub { $self->pidl(shift); }, + "return ndr_pull_error(ndr, NDR_ERR_INVALID_POINTER, \"NULL Pointer for size_is()\");"), + check_fully_dereferenced($e, $env)); + } + + if ((!$l->{IS_SURROUNDING}) and $l->{IS_CONFORMANT}) { + $self->pidl("NDR_CHECK(ndr_pull_array_size(ndr, " . get_pointer_to($var_name) . "));"); + } + + if ($l->{IS_VARYING}) { + $self->pidl("NDR_CHECK(ndr_pull_array_length($ndr, " . get_pointer_to($var_name) . "));"); + $length = "ndr_get_array_length($ndr, " . get_pointer_to($var_name) .")"; + } + + if ($length ne $size) { + $self->pidl("if ($length > $size) {"); + $self->indent; + $self->pidl("return ndr_pull_error($ndr, NDR_ERR_ARRAY_SIZE, \"Bad array size %u should exceed array length %u\", $size, $length);"); + $self->deindent; + $self->pidl("}"); + } + + if ($l->{IS_CONFORMANT} and not $l->{IS_ZERO_TERMINATED}) { + $self->defer("if ($var_name) {"); + $self->defer_indent; + my $size = ParseExprExt($l->{SIZE_IS}, $env, $e->{ORIGINAL}, + check_null_pointer($e, $env, sub { $self->defer(shift); }, + "return ndr_pull_error(ndr, NDR_ERR_INVALID_POINTER, \"NULL Pointer for size_is()\");"), + check_fully_dereferenced($e, $env)); + $self->defer("NDR_CHECK(ndr_check_array_size(ndr, (void*)" . get_pointer_to($var_name) . ", $size));"); + $self->defer_deindent; + $self->defer("}"); + } + + if ($l->{IS_VARYING} and not $l->{IS_ZERO_TERMINATED}) { + $self->defer("if ($var_name) {"); + $self->defer_indent; + my $length = ParseExprExt($l->{LENGTH_IS}, $env, $e->{ORIGINAL}, + check_null_pointer($e, $env, sub { $self->defer(shift); }, + "return ndr_pull_error(ndr, NDR_ERR_INVALID_POINTER, \"NULL Pointer for length_is()\");"), + check_fully_dereferenced($e, $env)); + $self->defer("NDR_CHECK(ndr_check_array_length(ndr, (void*)" . get_pointer_to($var_name) . ", $length));"); + $self->defer_deindent; + $self->defer("}"); + } + + if (not $l->{IS_FIXED} and not is_charset_array($e, $l)) { + $self->AllocateArrayLevel($e,$l,$ndr,$var_name,$size); + } + + return $length; +} + +sub compression_alg($$) +{ + my ($e, $l) = @_; + my ($alg, $clen, $dlen) = split(/,/, $l->{COMPRESSION}); + + return $alg; +} + +sub compression_clen($$$) +{ + my ($e, $l, $env) = @_; + my ($alg, $clen, $dlen) = split(/,/, $l->{COMPRESSION}); + + return ParseExpr($clen, $env, $e->{ORIGINAL}); +} + +sub compression_dlen($$$) +{ + my ($e,$l,$env) = @_; + my ($alg, $clen, $dlen) = split(/,/, $l->{COMPRESSION}); + + return ParseExpr($dlen, $env, $e->{ORIGINAL}); +} + +sub ParseCompressionPushStart($$$$$) +{ + my ($self,$e,$l,$ndr,$env) = @_; + my $comndr = "$ndr\_compressed"; + my $alg = compression_alg($e, $l); + my $dlen = compression_dlen($e, $l, $env); + + $self->pidl("{"); + $self->indent; + $self->pidl("struct ndr_push *$comndr;"); + $self->pidl("NDR_CHECK(ndr_push_compression_start($ndr, &$comndr, $alg, $dlen));"); + + return $comndr; +} + +sub ParseCompressionPushEnd($$$$$) +{ + my ($self,$e,$l,$ndr,$env) = @_; + my $comndr = "$ndr\_compressed"; + my $alg = compression_alg($e, $l); + my $dlen = compression_dlen($e, $l, $env); + + $self->pidl("NDR_CHECK(ndr_push_compression_end($ndr, $comndr, $alg, $dlen));"); + $self->deindent; + $self->pidl("}"); +} + +sub ParseCompressionPullStart($$$$$) +{ + my ($self,$e,$l,$ndr,$env) = @_; + my $comndr = "$ndr\_compressed"; + my $alg = compression_alg($e, $l); + my $dlen = compression_dlen($e, $l, $env); + + $self->pidl("{"); + $self->indent; + $self->pidl("struct ndr_pull *$comndr;"); + $self->pidl("NDR_CHECK(ndr_pull_compression_start($ndr, &$comndr, $alg, $dlen));"); + + return $comndr; +} + +sub ParseCompressionPullEnd($$$$$) +{ + my ($self,$e,$l,$ndr,$env) = @_; + my $comndr = "$ndr\_compressed"; + my $alg = compression_alg($e, $l); + my $dlen = compression_dlen($e, $l, $env); + + $self->pidl("NDR_CHECK(ndr_pull_compression_end($ndr, $comndr, $alg, $dlen));"); + $self->deindent; + $self->pidl("}"); +} + +sub ParseSubcontextPushStart($$$$$) +{ + my ($self,$e,$l,$ndr,$env) = @_; + my $subndr = "_ndr_$e->{NAME}"; + my $subcontext_size = ParseExpr($l->{SUBCONTEXT_SIZE}, $env, $e->{ORIGINAL}); + + $self->pidl("{"); + $self->indent; + $self->pidl("struct ndr_push *$subndr;"); + $self->pidl("NDR_CHECK(ndr_push_subcontext_start($ndr, &$subndr, $l->{HEADER_SIZE}, $subcontext_size));"); + + if (defined $l->{COMPRESSION}) { + $subndr = $self->ParseCompressionPushStart($e, $l, $subndr, $env); + } + + return $subndr; +} + +sub ParseSubcontextPushEnd($$$$$) +{ + my ($self,$e,$l,$ndr,$env) = @_; + my $subndr = "_ndr_$e->{NAME}"; + my $subcontext_size = ParseExpr($l->{SUBCONTEXT_SIZE}, $env, $e->{ORIGINAL}); + + if (defined $l->{COMPRESSION}) { + $self->ParseCompressionPushEnd($e, $l, $subndr, $env); + } + + $self->pidl("NDR_CHECK(ndr_push_subcontext_end($ndr, $subndr, $l->{HEADER_SIZE}, $subcontext_size));"); + $self->deindent; + $self->pidl("}"); +} + +sub ParseSubcontextPullStart($$$$$) +{ + my ($self,$e,$l,$ndr,$env) = @_; + my $subndr = "_ndr_$e->{NAME}"; + my $subcontext_size = ParseExpr($l->{SUBCONTEXT_SIZE}, $env, $e->{ORIGINAL}); + + $self->pidl("{"); + $self->indent; + $self->pidl("struct ndr_pull *$subndr;"); + $self->pidl("NDR_CHECK(ndr_pull_subcontext_start($ndr, &$subndr, $l->{HEADER_SIZE}, $subcontext_size));"); + + if (defined $l->{COMPRESSION}) { + $subndr = $self->ParseCompressionPullStart($e, $l, $subndr, $env); + } + + return $subndr; +} + +sub ParseSubcontextPullEnd($$$$$) +{ + my ($self,$e,$l,$ndr,$env) = @_; + my $subndr = "_ndr_$e->{NAME}"; + my $subcontext_size = ParseExpr($l->{SUBCONTEXT_SIZE}, $env, $e->{ORIGINAL}); + + if (defined $l->{COMPRESSION}) { + $self->ParseCompressionPullEnd($e, $l, $subndr, $env); + } + + $self->pidl("NDR_CHECK(ndr_pull_subcontext_end($ndr, $subndr, $l->{HEADER_SIZE}, $subcontext_size));"); + $self->deindent; + $self->pidl("}"); +} + +sub ParseElementPushLevel +{ + my ($self,$e,$l,$ndr,$var_name,$env,$primitives,$deferred) = @_; + + my $ndr_flags = CalcNdrFlags($l, $primitives, $deferred); + + if ($l->{TYPE} eq "ARRAY" and ($l->{IS_CONFORMANT} or $l->{IS_VARYING})) { + $var_name = get_pointer_to($var_name); + } + + if (defined($ndr_flags)) { + if ($l->{TYPE} eq "SUBCONTEXT") { + my $subndr = $self->ParseSubcontextPushStart($e, $l, $ndr, $env); + $self->ParseElementPushLevel($e, GetNextLevel($e, $l), $subndr, $var_name, $env, 1, 1); + $self->ParseSubcontextPushEnd($e, $l, $ndr, $env); + } elsif ($l->{TYPE} eq "POINTER") { + $self->ParsePtrPush($e, $l, $var_name); + } elsif ($l->{TYPE} eq "ARRAY") { + my $length = $self->ParseArrayPushHeader($e, $l, $ndr, $var_name, $env); + + my $nl = GetNextLevel($e, $l); + + # Allow speedups for arrays of scalar types + if (is_charset_array($e,$l)) { + $self->pidl("NDR_CHECK(ndr_push_charset($ndr, $ndr_flags, $var_name, $length, sizeof(" . mapTypeName($nl->{DATA_TYPE}) . "), CH_$e->{PROPERTIES}->{charset}));"); + return; + } elsif (has_fast_array($e,$l)) { + $self->pidl("NDR_CHECK(ndr_push_array_$nl->{DATA_TYPE}($ndr, $ndr_flags, $var_name, $length));"); + return; + } + } elsif ($l->{TYPE} eq "SWITCH") { + $self->ParseSwitchPush($e, $l, $ndr, $var_name, $env); + } elsif ($l->{TYPE} eq "DATA") { + $self->ParseDataPush($e, $l, $ndr, $var_name, $primitives, $deferred); + } + } + + if ($l->{TYPE} eq "POINTER" and $deferred) { + if ($l->{POINTER_TYPE} ne "ref") { + $self->pidl("if ($var_name) {"); + $self->indent; + if ($l->{POINTER_TYPE} eq "relative") { + $self->pidl("NDR_CHECK(ndr_push_relative_ptr2(ndr, $var_name));"); + } + } + $var_name = get_value_of($var_name); + $self->ParseElementPushLevel($e, GetNextLevel($e, $l), $ndr, $var_name, $env, 1, 1); + + if ($l->{POINTER_TYPE} ne "ref") { + $self->deindent; + $self->pidl("}"); + } + } elsif ($l->{TYPE} eq "ARRAY" and not has_fast_array($e,$l) and + not is_charset_array($e, $l)) { + my $length = ParseExpr($l->{LENGTH_IS}, $env, $e->{ORIGINAL}); + my $counter = "cntr_$e->{NAME}_$l->{LEVEL_INDEX}"; + + $var_name = get_array_element($var_name, $counter); + + if (($primitives and not $l->{IS_DEFERRED}) or ($deferred and $l->{IS_DEFERRED})) { + $self->pidl("for ($counter = 0; $counter < $length; $counter++) {"); + $self->indent; + $self->ParseElementPushLevel($e, GetNextLevel($e, $l), $ndr, $var_name, $env, 1, 0); + $self->deindent; + $self->pidl("}"); + } + + if ($deferred and ContainsDeferred($e, $l)) { + $self->pidl("for ($counter = 0; $counter < $length; $counter++) {"); + $self->indent; + $self->ParseElementPushLevel($e, GetNextLevel($e, $l), $ndr, $var_name, $env, 0, 1); + $self->deindent; + $self->pidl("}"); + } + } elsif ($l->{TYPE} eq "SWITCH") { + $self->ParseElementPushLevel($e, GetNextLevel($e, $l), $ndr, $var_name, $env, $primitives, $deferred); + } +} + +##################################################################### +# parse scalars in a structure element +sub ParseElementPush($$$$$$) +{ + my ($self,$e,$ndr,$env,$primitives,$deferred) = @_; + my $subndr = undef; + + my $var_name = $env->{$e->{NAME}}; + + return unless $primitives or ($deferred and ContainsDeferred($e, $e->{LEVELS}[0])); + + # Representation type is different from transmit_as + if ($e->{REPRESENTATION_TYPE} ne $e->{TYPE}) { + $self->pidl("{"); + $self->indent; + my $transmit_name = "_transmit_$e->{NAME}"; + $self->pidl(mapTypeName($e->{TYPE}) ." $transmit_name;"); + $self->pidl("NDR_CHECK(ndr_$e->{REPRESENTATION_TYPE}_to_$e->{TYPE}($var_name, " . get_pointer_to($transmit_name) . "));"); + $var_name = $transmit_name; + } + + $var_name = append_prefix($e, $var_name); + + $self->start_flags($e); + + if (defined(my $value = has_property($e, "value"))) { + $var_name = ParseExpr($value, $env, $e->{ORIGINAL}); + } + + $self->ParseElementPushLevel($e, $e->{LEVELS}[0], $ndr, $var_name, $env, $primitives, $deferred); + + $self->end_flags($e); + + if ($e->{REPRESENTATION_TYPE} ne $e->{TYPE}) { + $self->deindent; + $self->pidl("}"); + } +} + +##################################################################### +# parse a pointer in a struct element or function +sub ParsePtrPush($$$$) +{ + my ($self,$e,$l,$var_name) = @_; + + if ($l->{POINTER_TYPE} eq "ref") { + $self->pidl("if ($var_name == NULL) {"); + $self->indent; + $self->pidl("return ndr_push_error(ndr, NDR_ERR_INVALID_POINTER, \"NULL [ref] pointer\");"); + $self->deindent; + $self->pidl("}"); + if ($l->{LEVEL} eq "EMBEDDED") { + $self->pidl("NDR_CHECK(ndr_push_ref_ptr(ndr));"); + } + } elsif ($l->{POINTER_TYPE} eq "relative") { + $self->pidl("NDR_CHECK(ndr_push_relative_ptr1(ndr, $var_name));"); + } elsif ($l->{POINTER_TYPE} eq "unique") { + $self->pidl("NDR_CHECK(ndr_push_unique_ptr(ndr, $var_name));"); + } elsif ($l->{POINTER_TYPE} eq "full") { + $self->pidl("NDR_CHECK(ndr_push_full_ptr(ndr, $var_name));"); + } else { + die("Unhandled pointer type $l->{POINTER_TYPE}"); + } +} + +sub need_pointer_to($$$) +{ + my ($e, $l, $scalar_only) = @_; + + my $t; + if (ref($l->{DATA_TYPE})) { + $t = "$l->{DATA_TYPE}->{TYPE}_$l->{DATA_TYPE}->{NAME}"; + } else { + $t = $l->{DATA_TYPE}; + } + + if (not Parse::Pidl::Typelist::is_scalar($t)) { + return 1 if $scalar_only; + } + + my $arrays = 0; + + foreach my $tl (@{$e->{LEVELS}}) { + last if $l == $tl; + if ($tl->{TYPE} eq "ARRAY") { + $arrays++; + } + } + + if (Parse::Pidl::Typelist::scalar_is_reference($t)) { + return 1 unless $arrays; + } + + return 0; +} + +sub ParseDataPrint($$$$) +{ + my ($self, $e, $l, $var_name) = @_; + + if (not ref($l->{DATA_TYPE}) or defined($l->{DATA_TYPE}->{NAME})) { + + if (need_pointer_to($e, $l, 1)) { + $var_name = get_pointer_to($var_name); + } + + $self->pidl(TypeFunctionName("ndr_print", $l->{DATA_TYPE})."(ndr, \"$e->{NAME}\", $var_name);"); + } else { + $self->ParseTypePrint($l->{DATA_TYPE}, $var_name); + } +} + +##################################################################### +# print scalars in a structure element +sub ParseElementPrint($$$$) +{ + my($self, $e, $var_name, $env) = @_; + + return if (has_property($e, "noprint")); + + if ($e->{REPRESENTATION_TYPE} ne $e->{TYPE}) { + $self->pidl("ndr_print_$e->{REPRESENTATION_TYPE}(ndr, \"$e->{NAME}\", $var_name);"); + return; + } + + $var_name = append_prefix($e, $var_name); + + if (defined(my $value = has_property($e, "value"))) { + $var_name = "(ndr->flags & LIBNDR_PRINT_SET_VALUES)?" . ParseExpr($value,$env, $e->{ORIGINAL}) . ":$var_name"; + } + + foreach my $l (@{$e->{LEVELS}}) { + if ($l->{TYPE} eq "POINTER") { + $self->pidl("ndr_print_ptr(ndr, \"$e->{NAME}\", $var_name);"); + $self->pidl("ndr->depth++;"); + if ($l->{POINTER_TYPE} ne "ref") { + $self->pidl("if ($var_name) {"); + $self->indent; + } + $var_name = get_value_of($var_name); + } elsif ($l->{TYPE} eq "ARRAY") { + my $length; + + if ($l->{IS_CONFORMANT} or $l->{IS_VARYING}) { + $var_name = get_pointer_to($var_name); + } + + if ($l->{IS_ZERO_TERMINATED}) { + $length = "ndr_string_length($var_name, sizeof(*$var_name))"; + } else { + $length = ParseExprExt($l->{LENGTH_IS}, $env, $e->{ORIGINAL}, + check_null_pointer($e, $env, sub { $self->pidl(shift); }, "return;"), check_fully_dereferenced($e, $env)); + } + + if (is_charset_array($e,$l)) { + $self->pidl("ndr_print_string(ndr, \"$e->{NAME}\", $var_name);"); + last; + } elsif (has_fast_array($e, $l)) { + my $nl = GetNextLevel($e, $l); + $self->pidl("ndr_print_array_$nl->{DATA_TYPE}(ndr, \"$e->{NAME}\", $var_name, $length);"); + last; + } else { + my $counter = "cntr_$e->{NAME}_$l->{LEVEL_INDEX}"; + + $self->pidl("ndr->print(ndr, \"\%s: ARRAY(\%d)\", \"$e->{NAME}\", $length);"); + $self->pidl("ndr->depth++;"); + $self->pidl("for ($counter=0;$counter<$length;$counter++) {"); + $self->indent; + $self->pidl("char *idx_$l->{LEVEL_INDEX}=NULL;"); + $self->pidl("if (asprintf(&idx_$l->{LEVEL_INDEX}, \"[\%d]\", $counter) != -1) {"); + $self->indent; + + $var_name = get_array_element($var_name, $counter); + } + } elsif ($l->{TYPE} eq "DATA") { + $self->ParseDataPrint($e, $l, $var_name); + } elsif ($l->{TYPE} eq "SWITCH") { + my $switch_var = ParseExprExt($l->{SWITCH_IS}, $env, $e->{ORIGINAL}, + check_null_pointer($e, $env, sub { $self->pidl(shift); }, "return;"), check_fully_dereferenced($e, $env)); + $self->pidl("ndr_print_set_switch_value(ndr, " . get_pointer_to($var_name) . ", $switch_var);"); + } + } + + foreach my $l (reverse @{$e->{LEVELS}}) { + if ($l->{TYPE} eq "POINTER") { + if ($l->{POINTER_TYPE} ne "ref") { + $self->deindent; + $self->pidl("}"); + } + $self->pidl("ndr->depth--;"); + } elsif (($l->{TYPE} eq "ARRAY") + and not is_charset_array($e,$l) + and not has_fast_array($e,$l)) { + $self->pidl("free(idx_$l->{LEVEL_INDEX});"); + $self->deindent; + $self->pidl("}"); + $self->deindent; + $self->pidl("}"); + $self->pidl("ndr->depth--;"); + } + } +} + +##################################################################### +# parse scalars in a structure element - pull size +sub ParseSwitchPull($$$$$$) +{ + my($self,$e,$l,$ndr,$var_name,$env) = @_; + my $switch_var = ParseExprExt($l->{SWITCH_IS}, $env, $e->{ORIGINAL}, + check_null_pointer($e, $env, sub { $self->pidl(shift); }, + "return ndr_pull_error(ndr, NDR_ERR_INVALID_POINTER, \"NULL Pointer for switch_is()\");"), + check_fully_dereferenced($e, $env)); + + $var_name = get_pointer_to($var_name); + $self->pidl("NDR_CHECK(ndr_pull_set_switch_value($ndr, $var_name, $switch_var));"); +} + +##################################################################### +# push switch element +sub ParseSwitchPush($$$$$$) +{ + my($self,$e,$l,$ndr,$var_name,$env) = @_; + my $switch_var = ParseExprExt($l->{SWITCH_IS}, $env, $e->{ORIGINAL}, + check_null_pointer($e, $env, sub { $self->pidl(shift); }, + "return ndr_push_error(ndr, NDR_ERR_INVALID_POINTER, \"NULL Pointer for switch_is()\");"), + check_fully_dereferenced($e, $env)); + + $var_name = get_pointer_to($var_name); + $self->pidl("NDR_CHECK(ndr_push_set_switch_value($ndr, $var_name, $switch_var));"); +} + +sub ParseDataPull($$$$$$$) +{ + my ($self,$e,$l,$ndr,$var_name,$primitives,$deferred) = @_; + + if (not ref($l->{DATA_TYPE}) or defined($l->{DATA_TYPE}->{NAME})) { + + my $ndr_flags = CalcNdrFlags($l, $primitives, $deferred); + + if (need_pointer_to($e, $l, 0)) { + $var_name = get_pointer_to($var_name); + } + + $var_name = get_pointer_to($var_name); + + $self->pidl("NDR_CHECK(".TypeFunctionName("ndr_pull", $l->{DATA_TYPE})."($ndr, $ndr_flags, $var_name));"); + + if (my $range = has_property($e, "range")) { + $var_name = get_value_of($var_name); + my ($low, $high) = split(/,/, $range, 2); + $self->pidl("if ($var_name < $low || $var_name > $high) {"); + $self->pidl("\treturn ndr_pull_error($ndr, NDR_ERR_RANGE, \"value out of range\");"); + $self->pidl("}"); + } + } else { + $self->ParseTypePull($l->{DATA_TYPE}, $var_name, $primitives, $deferred); + } +} + +sub ParseDataPush($$$$$$$) +{ + my ($self,$e,$l,$ndr,$var_name,$primitives,$deferred) = @_; + + if (not ref($l->{DATA_TYPE}) or defined($l->{DATA_TYPE}->{NAME})) { + + my $ndr_flags = CalcNdrFlags($l, $primitives, $deferred); + + # strings are passed by value rather than reference + if (need_pointer_to($e, $l, 1)) { + $var_name = get_pointer_to($var_name); + } + + $self->pidl("NDR_CHECK(".TypeFunctionName("ndr_push", $l->{DATA_TYPE})."($ndr, $ndr_flags, $var_name));"); + } else { + $self->ParseTypePush($l->{DATA_TYPE}, $var_name, $primitives, $deferred); + } +} + +sub CalcNdrFlags($$$) +{ + my ($l,$primitives,$deferred) = @_; + + my $scalars = 0; + my $buffers = 0; + + # Add NDR_SCALARS if this one is deferred + # and deferreds may be pushed + $scalars = 1 if ($l->{IS_DEFERRED} and $deferred); + + # Add NDR_SCALARS if this one is not deferred and + # primitives may be pushed + $scalars = 1 if (!$l->{IS_DEFERRED} and $primitives); + + # Add NDR_BUFFERS if this one contains deferred stuff + # and deferreds may be pushed + $buffers = 1 if ($l->{CONTAINS_DEFERRED} and $deferred); + + return "NDR_SCALARS|NDR_BUFFERS" if ($scalars and $buffers); + return "NDR_SCALARS" if ($scalars); + return "NDR_BUFFERS" if ($buffers); + return undef; +} + +sub ParseMemCtxPullFlags($$$$) +{ + my ($self, $e, $l) = @_; + + return undef unless ($l->{TYPE} eq "POINTER" or $l->{TYPE} eq "ARRAY"); + + return undef if ($l->{TYPE} eq "ARRAY" and $l->{IS_FIXED}); + return undef if has_fast_array($e, $l); + return undef if is_charset_array($e, $l); + + my $mem_flags = "0"; + + if (($l->{TYPE} eq "POINTER") and ($l->{POINTER_TYPE} eq "ref")) { + my $nl = GetNextLevel($e, $l); + my $next_is_array = ($nl->{TYPE} eq "ARRAY"); + my $next_is_string = (($nl->{TYPE} eq "DATA") and + ($nl->{DATA_TYPE} eq "string")); + if ($next_is_array or $next_is_string) { + return undef; + } elsif ($l->{LEVEL} eq "TOP") { + $mem_flags = "LIBNDR_FLAG_REF_ALLOC"; + } + } + + return $mem_flags; +} + +sub ParseMemCtxPullStart($$$$) +{ + my ($self, $e, $l, $ptr_name) = @_; + + my $mem_r_ctx = "_mem_save_$e->{NAME}_$l->{LEVEL_INDEX}"; + my $mem_c_ctx = $ptr_name; + my $mem_c_flags = $self->ParseMemCtxPullFlags($e, $l); + + return unless defined($mem_c_flags); + + $self->pidl("$mem_r_ctx = NDR_PULL_GET_MEM_CTX(ndr);"); + $self->pidl("NDR_PULL_SET_MEM_CTX(ndr, $mem_c_ctx, $mem_c_flags);"); +} + +sub ParseMemCtxPullEnd($$$) +{ + my ($self, $e, $l) = @_; + + my $mem_r_ctx = "_mem_save_$e->{NAME}_$l->{LEVEL_INDEX}"; + my $mem_r_flags = $self->ParseMemCtxPullFlags($e, $l); + + return unless defined($mem_r_flags); + + $self->pidl("NDR_PULL_SET_MEM_CTX(ndr, $mem_r_ctx, $mem_r_flags);"); +} + +sub CheckStringTerminator($$$$$) +{ + my ($self,$ndr,$e,$l,$length) = @_; + my $nl = GetNextLevel($e, $l); + + # Make sure last element is zero! + $self->pidl("NDR_CHECK(ndr_check_string_terminator($ndr, $length, sizeof($nl->{DATA_TYPE}_t)));"); +} + +sub ParseElementPullLevel +{ + my($self,$e,$l,$ndr,$var_name,$env,$primitives,$deferred) = @_; + + my $ndr_flags = CalcNdrFlags($l, $primitives, $deferred); + + if ($l->{TYPE} eq "ARRAY" and ($l->{IS_VARYING} or $l->{IS_CONFORMANT})) { + $var_name = get_pointer_to($var_name); + } + + # Only pull something if there's actually something to be pulled + if (defined($ndr_flags)) { + if ($l->{TYPE} eq "SUBCONTEXT") { + my $subndr = $self->ParseSubcontextPullStart($e, $l, $ndr, $env); + $self->ParseElementPullLevel($e, GetNextLevel($e,$l), $subndr, $var_name, $env, 1, 1); + $self->ParseSubcontextPullEnd($e, $l, $ndr, $env); + } elsif ($l->{TYPE} eq "ARRAY") { + my $length = $self->ParseArrayPullHeader($e, $l, $ndr, $var_name, $env); + + my $nl = GetNextLevel($e, $l); + + if (is_charset_array($e,$l)) { + if ($l->{IS_ZERO_TERMINATED}) { + $self->CheckStringTerminator($ndr, $e, $l, $length); + } + $self->pidl("NDR_CHECK(ndr_pull_charset($ndr, $ndr_flags, ".get_pointer_to($var_name).", $length, sizeof(" . mapTypeName($nl->{DATA_TYPE}) . "), CH_$e->{PROPERTIES}->{charset}));"); + return; + } elsif (has_fast_array($e, $l)) { + if ($l->{IS_ZERO_TERMINATED}) { + $self->CheckStringTerminator($ndr,$e,$l,$length); + } + $self->pidl("NDR_CHECK(ndr_pull_array_$nl->{DATA_TYPE}($ndr, $ndr_flags, $var_name, $length));"); + return; + } + } elsif ($l->{TYPE} eq "POINTER") { + $self->ParsePtrPull($e, $l, $ndr, $var_name); + } elsif ($l->{TYPE} eq "SWITCH") { + $self->ParseSwitchPull($e, $l, $ndr, $var_name, $env); + } elsif ($l->{TYPE} eq "DATA") { + $self->ParseDataPull($e, $l, $ndr, $var_name, $primitives, $deferred); + } + } + + # add additional constructions + if ($l->{TYPE} eq "POINTER" and $deferred) { + if ($l->{POINTER_TYPE} ne "ref") { + $self->pidl("if ($var_name) {"); + $self->indent; + + if ($l->{POINTER_TYPE} eq "relative") { + $self->pidl("uint32_t _relative_save_offset;"); + $self->pidl("_relative_save_offset = ndr->offset;"); + $self->pidl("NDR_CHECK(ndr_pull_relative_ptr2(ndr, $var_name));"); + } + } + + $self->ParseMemCtxPullStart($e, $l, $var_name); + + $var_name = get_value_of($var_name); + $self->ParseElementPullLevel($e, GetNextLevel($e,$l), $ndr, $var_name, $env, 1, 1); + + $self->ParseMemCtxPullEnd($e,$l); + + if ($l->{POINTER_TYPE} ne "ref") { + if ($l->{POINTER_TYPE} eq "relative") { + $self->pidl("ndr->offset = _relative_save_offset;"); + } + $self->deindent; + $self->pidl("}"); + } + } elsif ($l->{TYPE} eq "ARRAY" and + not has_fast_array($e,$l) and not is_charset_array($e, $l)) { + my $length = ParseExpr($l->{LENGTH_IS}, $env, $e->{ORIGINAL}); + my $counter = "cntr_$e->{NAME}_$l->{LEVEL_INDEX}"; + my $array_name = $var_name; + + $var_name = get_array_element($var_name, $counter); + + $self->ParseMemCtxPullStart($e, $l, $array_name); + + if (($primitives and not $l->{IS_DEFERRED}) or ($deferred and $l->{IS_DEFERRED})) { + my $nl = GetNextLevel($e,$l); + + if ($l->{IS_ZERO_TERMINATED}) { + $self->CheckStringTerminator($ndr,$e,$l,$length); + } + + $self->pidl("for ($counter = 0; $counter < $length; $counter++) {"); + $self->indent; + $self->ParseElementPullLevel($e, $nl, $ndr, $var_name, $env, 1, 0); + $self->deindent; + $self->pidl("}"); + } + + if ($deferred and ContainsDeferred($e, $l)) { + $self->pidl("for ($counter = 0; $counter < $length; $counter++) {"); + $self->indent; + $self->ParseElementPullLevel($e,GetNextLevel($e,$l), $ndr, $var_name, $env, 0, 1); + $self->deindent; + $self->pidl("}"); + } + + $self->ParseMemCtxPullEnd($e, $l); + + } elsif ($l->{TYPE} eq "SWITCH") { + $self->ParseElementPullLevel($e, GetNextLevel($e,$l), $ndr, $var_name, $env, $primitives, $deferred); + } +} + +##################################################################### +# parse scalars in a structure element - pull size +sub ParseElementPull($$$$$$) +{ + my($self,$e,$ndr,$env,$primitives,$deferred) = @_; + + my $var_name = $env->{$e->{NAME}}; + my $represent_name; + my $transmit_name; + + return unless $primitives or ($deferred and ContainsDeferred($e, $e->{LEVELS}[0])); + + if ($e->{REPRESENTATION_TYPE} ne $e->{TYPE}) { + $self->pidl("{"); + $self->indent; + $represent_name = $var_name; + $transmit_name = "_transmit_$e->{NAME}"; + $var_name = $transmit_name; + $self->pidl(mapTypeName($e->{TYPE})." $var_name;"); + } + + $var_name = append_prefix($e, $var_name); + + $self->start_flags($e); + + $self->ParseElementPullLevel($e,$e->{LEVELS}[0],$ndr,$var_name,$env,$primitives,$deferred); + + $self->end_flags($e); + + # Representation type is different from transmit_as + if ($e->{REPRESENTATION_TYPE} ne $e->{TYPE}) { + $self->pidl("NDR_CHECK(ndr_$e->{TYPE}_to_$e->{REPRESENTATION_TYPE}($transmit_name, ".get_pointer_to($represent_name)."));"); + $self->deindent; + $self->pidl("}"); + } +} + +##################################################################### +# parse a pointer in a struct element or function +sub ParsePtrPull($$$$$) +{ + my($self, $e,$l,$ndr,$var_name) = @_; + + my $nl = GetNextLevel($e, $l); + my $next_is_array = ($nl->{TYPE} eq "ARRAY"); + my $next_is_string = (($nl->{TYPE} eq "DATA") and + ($nl->{DATA_TYPE} eq "string")); + + if ($l->{POINTER_TYPE} eq "ref" and $l->{LEVEL} eq "TOP") { + + if (!$next_is_array and !$next_is_string) { + $self->pidl("if (ndr->flags & LIBNDR_FLAG_REF_ALLOC) {"); + $self->pidl("\tNDR_PULL_ALLOC($ndr, $var_name);"); + $self->pidl("}"); + } + + return; + } elsif ($l->{POINTER_TYPE} eq "ref" and $l->{LEVEL} eq "EMBEDDED") { + $self->pidl("NDR_CHECK(ndr_pull_ref_ptr($ndr, &_ptr_$e->{NAME}));"); + } elsif (($l->{POINTER_TYPE} eq "unique") or + ($l->{POINTER_TYPE} eq "relative") or + ($l->{POINTER_TYPE} eq "full")) { + $self->pidl("NDR_CHECK(ndr_pull_generic_ptr($ndr, &_ptr_$e->{NAME}));"); + } else { + die("Unhandled pointer type $l->{POINTER_TYPE}"); + } + + $self->pidl("if (_ptr_$e->{NAME}) {"); + $self->indent; + + # Don't do this for arrays, they're allocated at the actual level + # of the array + unless ($next_is_array or $next_is_string) { + $self->pidl("NDR_PULL_ALLOC($ndr, $var_name);"); + } else { + # FIXME: Yes, this is nasty. + # We allocate an array twice + # - once just to indicate that it's there, + # - then the real allocation... + $self->pidl("NDR_PULL_ALLOC($ndr, $var_name);"); + } + + #$self->pidl("memset($var_name, 0, sizeof($var_name));"); + if ($l->{POINTER_TYPE} eq "relative") { + $self->pidl("NDR_CHECK(ndr_pull_relative_ptr1($ndr, $var_name, _ptr_$e->{NAME}));"); + } + $self->deindent; + $self->pidl("} else {"); + $self->pidl("\t$var_name = NULL;"); + $self->pidl("}"); +} + +sub ParseStructPushPrimitives($$$$) +{ + my ($self, $struct, $varname, $env) = @_; + + # see if the structure contains a conformant array. If it + # does, then it must be the last element of the structure, and + # we need to push the conformant length early, as it fits on + # the wire before the structure (and even before the structure + # alignment) + if (defined($struct->{SURROUNDING_ELEMENT})) { + my $e = $struct->{SURROUNDING_ELEMENT}; + + if (defined($e->{LEVELS}[0]) and + $e->{LEVELS}[0]->{TYPE} eq "ARRAY") { + my $size; + + if ($e->{LEVELS}[0]->{IS_ZERO_TERMINATED}) { + if (has_property($e, "charset")) { + $size = "ndr_charset_length($varname->$e->{NAME}, CH_$e->{PROPERTIES}->{charset})"; + } else { + $size = "ndr_string_length($varname->$e->{NAME}, sizeof(*$varname->$e->{NAME}))"; + } + } else { + $size = ParseExpr($e->{LEVELS}[0]->{SIZE_IS}, $env, $e->{ORIGINAL}); + } + + $self->pidl("NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, $size));"); + } else { + $self->pidl("NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, ndr_string_array_size(ndr, $varname->$e->{NAME})));"); + } + } + + $self->pidl("NDR_CHECK(ndr_push_align(ndr, $struct->{ALIGN}));"); + + if (defined($struct->{PROPERTIES}{relative_base})) { + # set the current offset as base for relative pointers + # and store it based on the toplevel struct/union + $self->pidl("NDR_CHECK(ndr_push_setup_relative_base_offset1(ndr, $varname, ndr->offset));"); + } + + $self->ParseElementPush($_, "ndr", $env, 1, 0) foreach (@{$struct->{ELEMENTS}}); +} + +sub ParseStructPushDeferred($$$) +{ + my ($self, $struct, $varname, $env) = @_; + if (defined($struct->{PROPERTIES}{relative_base})) { + # retrieve the current offset as base for relative pointers + # based on the toplevel struct/union + $self->pidl("NDR_CHECK(ndr_push_setup_relative_base_offset2(ndr, $varname));"); + } + $self->ParseElementPush($_, "ndr", $env, 0, 1) foreach (@{$struct->{ELEMENTS}}); +} + +##################################################################### +# parse a struct +sub ParseStructPush($$$) +{ + my ($self, $struct, $varname) = @_; + + return unless defined($struct->{ELEMENTS}); + + my $env = GenerateStructEnv($struct, $varname); + + EnvSubstituteValue($env, $struct); + + $self->DeclareArrayVariables($_) foreach (@{$struct->{ELEMENTS}}); + + $self->start_flags($struct); + + $self->pidl("if (ndr_flags & NDR_SCALARS) {"); + $self->indent; + $self->ParseStructPushPrimitives($struct, $varname, $env); + $self->deindent; + $self->pidl("}"); + + $self->pidl("if (ndr_flags & NDR_BUFFERS) {"); + $self->indent; + $self->ParseStructPushDeferred($struct, $varname, $env); + $self->deindent; + $self->pidl("}"); + + $self->end_flags($struct); +} + +##################################################################### +# generate a push function for an enum +sub ParseEnumPush($$$) +{ + my($self,$enum,$varname) = @_; + my($type_fn) = $enum->{BASE_TYPE}; + + $self->start_flags($enum); + $self->pidl("NDR_CHECK(ndr_push_$type_fn(ndr, NDR_SCALARS, $varname));"); + $self->end_flags($enum); +} + +##################################################################### +# generate a pull function for an enum +sub ParseEnumPull($$$) +{ + my($self,$enum,$varname) = @_; + my($type_fn) = $enum->{BASE_TYPE}; + my($type_v_decl) = mapTypeName($type_fn); + + $self->pidl("$type_v_decl v;"); + $self->start_flags($enum); + $self->pidl("NDR_CHECK(ndr_pull_$type_fn(ndr, NDR_SCALARS, &v));"); + $self->pidl("*$varname = v;"); + + $self->end_flags($enum); +} + +##################################################################### +# generate a print function for an enum +sub ParseEnumPrint($$$$) +{ + my($self,$enum,$name,$varname) = @_; + + $self->pidl("const char *val = NULL;"); + $self->pidl(""); + + $self->start_flags($enum); + + $self->pidl("switch ($varname) {"); + $self->indent; + my $els = \@{$enum->{ELEMENTS}}; + foreach my $i (0 .. $#{$els}) { + my $e = ${$els}[$i]; + chomp $e; + if ($e =~ /^(.*)=/) { + $e = $1; + } + $self->pidl("case $e: val = \"$e\"; break;"); + } + + $self->deindent; + $self->pidl("}"); + + $self->pidl("ndr_print_enum(ndr, name, \"$enum->{TYPE}\", val, $varname);"); + + $self->end_flags($enum); +} + +sub DeclEnum($$$$) +{ + my ($e,$t,$name,$varname) = @_; + return "enum $name " . + ($t eq "pull"?"*":"") . $varname; +} + +$typefamily{ENUM} = { + DECL => \&DeclEnum, + PUSH_FN_BODY => \&ParseEnumPush, + PULL_FN_BODY => \&ParseEnumPull, + PRINT_FN_BODY => \&ParseEnumPrint, +}; + +##################################################################### +# generate a push function for a bitmap +sub ParseBitmapPush($$$) +{ + my($self,$bitmap,$varname) = @_; + my($type_fn) = $bitmap->{BASE_TYPE}; + + $self->start_flags($bitmap); + + $self->pidl("NDR_CHECK(ndr_push_$type_fn(ndr, NDR_SCALARS, $varname));"); + + $self->end_flags($bitmap); +} + +##################################################################### +# generate a pull function for an bitmap +sub ParseBitmapPull($$$) +{ + my($self,$bitmap,$varname) = @_; + my $type_fn = $bitmap->{BASE_TYPE}; + my($type_decl) = mapTypeName($bitmap->{BASE_TYPE}); + + $self->pidl("$type_decl v;"); + $self->start_flags($bitmap); + $self->pidl("NDR_CHECK(ndr_pull_$type_fn(ndr, NDR_SCALARS, &v));"); + $self->pidl("*$varname = v;"); + + $self->end_flags($bitmap); +} + +##################################################################### +# generate a print function for an bitmap +sub ParseBitmapPrintElement($$$$$) +{ + my($self,$e,$bitmap,$name,$varname) = @_; + my($type_decl) = mapTypeName($bitmap->{BASE_TYPE}); + my($type_fn) = $bitmap->{BASE_TYPE}; + my($flag); + + if ($e =~ /^(\w+) .*$/) { + $flag = "$1"; + } else { + die "Bitmap: \"$name\" invalid Flag: \"$e\""; + } + + $self->pidl("ndr_print_bitmap_flag(ndr, sizeof($type_decl), \"$flag\", $flag, $varname);"); +} + +##################################################################### +# generate a print function for an bitmap +sub ParseBitmapPrint($$$$) +{ + my($self,$bitmap,$name,$varname) = @_; + my($type_decl) = mapTypeName($bitmap->{TYPE}); + my($type_fn) = $bitmap->{BASE_TYPE}; + + $self->start_flags($bitmap); + + $self->pidl("ndr_print_$type_fn(ndr, name, $varname);"); + + $self->pidl("ndr->depth++;"); + foreach my $e (@{$bitmap->{ELEMENTS}}) { + $self->ParseBitmapPrintElement($e, $bitmap, $name, $varname); + } + $self->pidl("ndr->depth--;"); + + $self->end_flags($bitmap); +} + +sub DeclBitmap($$$$) +{ + my ($e,$t,$name,$varname) = @_; + return mapTypeName(Parse::Pidl::Typelist::bitmap_type_fn($e)) . + ($t eq "pull"?" *":" ") . $varname; +} + +$typefamily{BITMAP} = { + DECL => \&DeclBitmap, + PUSH_FN_BODY => \&ParseBitmapPush, + PULL_FN_BODY => \&ParseBitmapPull, + PRINT_FN_BODY => \&ParseBitmapPrint, +}; + +##################################################################### +# generate a struct print function +sub ParseStructPrint($$$$) +{ + my($self,$struct,$name,$varname) = @_; + + return unless defined $struct->{ELEMENTS}; + + my $env = GenerateStructEnv($struct, $varname); + + $self->DeclareArrayVariables($_) foreach (@{$struct->{ELEMENTS}}); + + $self->pidl("ndr_print_struct(ndr, name, \"$name\");"); + + $self->start_flags($struct); + + $self->pidl("ndr->depth++;"); + + $self->ParseElementPrint($_, $env->{$_->{NAME}}, $env) + foreach (@{$struct->{ELEMENTS}}); + $self->pidl("ndr->depth--;"); + + $self->end_flags($struct); +} + +sub DeclarePtrVariables($$) +{ + my ($self,$e) = @_; + foreach my $l (@{$e->{LEVELS}}) { + if ($l->{TYPE} eq "POINTER" and + not ($l->{POINTER_TYPE} eq "ref" and $l->{LEVEL} eq "TOP")) { + $self->pidl("uint32_t _ptr_$e->{NAME};"); + last; + } + } +} + +sub DeclareArrayVariables($$) +{ + my ($self,$e) = @_; + + foreach my $l (@{$e->{LEVELS}}) { + next if has_fast_array($e,$l); + next if is_charset_array($e,$l); + if ($l->{TYPE} eq "ARRAY") { + $self->pidl("uint32_t cntr_$e->{NAME}_$l->{LEVEL_INDEX};"); + } + } +} + +sub DeclareMemCtxVariables($$) +{ + my ($self,$e) = @_; + foreach my $l (@{$e->{LEVELS}}) { + my $mem_flags = $self->ParseMemCtxPullFlags($e, $l); + if (defined($mem_flags)) { + $self->pidl("TALLOC_CTX *_mem_save_$e->{NAME}_$l->{LEVEL_INDEX};"); + } + } +} + +sub ParseStructPullPrimitives($$$$) +{ + my($self,$struct,$varname,$env) = @_; + + if (defined $struct->{SURROUNDING_ELEMENT}) { + $self->pidl("NDR_CHECK(ndr_pull_array_size(ndr, &$varname->$struct->{SURROUNDING_ELEMENT}->{NAME}));"); + } + + $self->pidl("NDR_CHECK(ndr_pull_align(ndr, $struct->{ALIGN}));"); + + if (defined($struct->{PROPERTIES}{relative_base})) { + # set the current offset as base for relative pointers + # and store it based on the toplevel struct/union + $self->pidl("NDR_CHECK(ndr_pull_setup_relative_base_offset1(ndr, $varname, ndr->offset));"); + } + + $self->ParseElementPull($_, "ndr", $env, 1, 0) foreach (@{$struct->{ELEMENTS}}); + + $self->add_deferred(); +} + +sub ParseStructPullDeferred($$$$) +{ + my ($self,$struct,$varname,$env) = @_; + + if (defined($struct->{PROPERTIES}{relative_base})) { + # retrieve the current offset as base for relative pointers + # based on the toplevel struct/union + $self->pidl("NDR_CHECK(ndr_pull_setup_relative_base_offset2(ndr, $varname));"); + } + foreach my $e (@{$struct->{ELEMENTS}}) { + $self->ParseElementPull($e, "ndr", $env, 0, 1); + } + + $self->add_deferred(); +} + +##################################################################### +# parse a struct - pull side +sub ParseStructPull($$$) +{ + my($self,$struct,$varname) = @_; + + return unless defined $struct->{ELEMENTS}; + + # declare any internal pointers we need + foreach my $e (@{$struct->{ELEMENTS}}) { + $self->DeclarePtrVariables($e); + $self->DeclareArrayVariables($e); + $self->DeclareMemCtxVariables($e); + } + + $self->start_flags($struct); + + my $env = GenerateStructEnv($struct, $varname); + + $self->pidl("if (ndr_flags & NDR_SCALARS) {"); + $self->indent; + $self->ParseStructPullPrimitives($struct,$varname,$env); + $self->deindent; + $self->pidl("}"); + $self->pidl("if (ndr_flags & NDR_BUFFERS) {"); + $self->indent; + $self->ParseStructPullDeferred($struct,$varname,$env); + $self->deindent; + $self->pidl("}"); + + $self->end_flags($struct); +} + +##################################################################### +# calculate size of ndr struct +sub ParseStructNdrSize($$$$) +{ + my ($self,$t, $name, $varname) = @_; + my $sizevar; + + if (my $flags = has_property($t, "flag")) { + $self->pidl("flags |= $flags;"); + } + $self->pidl("return ndr_size_struct($varname, flags, (ndr_push_flags_fn_t)ndr_push_$name);"); +} + +sub DeclStruct($$$$) +{ + my ($e,$t,$name,$varname) = @_; + return ($t ne "pull"?"const ":"") . "struct $name *$varname"; +} + +sub ArgsStructNdrSize($$$) +{ + my ($d, $name, $varname) = @_; + return "const struct $name *$varname, int flags"; +} + +$typefamily{STRUCT} = { + PUSH_FN_BODY => \&ParseStructPush, + DECL => \&DeclStruct, + PULL_FN_BODY => \&ParseStructPull, + PRINT_FN_BODY => \&ParseStructPrint, + SIZE_FN_BODY => \&ParseStructNdrSize, + SIZE_FN_ARGS => \&ArgsStructNdrSize, +}; + +##################################################################### +# calculate size of ndr struct +sub ParseUnionNdrSize($$$) +{ + my ($self, $t, $name, $varname) = @_; + my $sizevar; + + if (my $flags = has_property($t, "flag")) { + $self->pidl("flags |= $flags;"); + } + + $self->pidl("return ndr_size_union($varname, flags, level, (ndr_push_flags_fn_t)ndr_push_$name);"); +} + +sub ParseUnionPushPrimitives($$$) +{ + my ($self, $e, $varname) = @_; + + my $have_default = 0; + + $self->pidl("int level = ndr_push_get_switch_value(ndr, $varname);"); + + if (defined($e->{SWITCH_TYPE})) { + $self->pidl("NDR_CHECK(ndr_push_$e->{SWITCH_TYPE}(ndr, NDR_SCALARS, level));"); + } + + $self->pidl("switch (level) {"); + $self->indent; + foreach my $el (@{$e->{ELEMENTS}}) { + if ($el->{CASE} eq "default") { + $have_default = 1; + } + $self->pidl("$el->{CASE}: {"); + + if ($el->{TYPE} ne "EMPTY") { + $self->indent; + if (defined($e->{PROPERTIES}{relative_base})) { + $self->pidl("NDR_CHECK(ndr_push_align(ndr, $el->{ALIGN}));"); + # set the current offset as base for relative pointers + # and store it based on the toplevel struct/union + $self->pidl("NDR_CHECK(ndr_push_setup_relative_base_offset1(ndr, $varname, ndr->offset));"); + } + $self->DeclareArrayVariables($el); + $self->ParseElementPush($el, "ndr", {$el->{NAME} => "$varname->$el->{NAME}"}, 1, 0); + $self->deindent; + } + $self->pidl("break; }"); + $self->pidl(""); + } + if (! $have_default) { + $self->pidl("default:"); + $self->pidl("\treturn ndr_push_error(ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value \%u\", level);"); + } + $self->deindent; + $self->pidl("}"); +} + +sub ParseUnionPushDeferred($$$) +{ + my ($self,$e,$varname) = @_; + + my $have_default = 0; + + $self->pidl("int level = ndr_push_get_switch_value(ndr, $varname);"); + if (defined($e->{PROPERTIES}{relative_base})) { + # retrieve the current offset as base for relative pointers + # based on the toplevel struct/union + $self->pidl("NDR_CHECK(ndr_push_setup_relative_base_offset2(ndr, $varname));"); + } + $self->pidl("switch (level) {"); + $self->indent; + foreach my $el (@{$e->{ELEMENTS}}) { + if ($el->{CASE} eq "default") { + $have_default = 1; + } + + $self->pidl("$el->{CASE}:"); + if ($el->{TYPE} ne "EMPTY") { + $self->indent; + $self->ParseElementPush($el, "ndr", {$el->{NAME} => "$varname->$el->{NAME}"}, 0, 1); + $self->deindent; + } + $self->pidl("break;"); + $self->pidl(""); + } + if (! $have_default) { + $self->pidl("default:"); + $self->pidl("\treturn ndr_push_error(ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value \%u\", level);"); + } + $self->deindent; + $self->pidl("}"); +} + +##################################################################### +# parse a union - push side +sub ParseUnionPush($$$) +{ + my ($self,$e,$varname) = @_; + my $have_default = 0; + + $self->start_flags($e); + + $self->pidl("if (ndr_flags & NDR_SCALARS) {"); + $self->indent; + $self->ParseUnionPushPrimitives($e, $varname); + $self->deindent; + $self->pidl("}"); + $self->pidl("if (ndr_flags & NDR_BUFFERS) {"); + $self->indent; + $self->ParseUnionPushDeferred($e, $varname); + $self->deindent; + $self->pidl("}"); + $self->end_flags($e); +} + +##################################################################### +# print a union +sub ParseUnionPrint($$$$) +{ + my ($self,$e,$name,$varname) = @_; + my $have_default = 0; + + $self->pidl("int level;"); + foreach my $el (@{$e->{ELEMENTS}}) { + $self->DeclareArrayVariables($el); + } + + $self->start_flags($e); + + $self->pidl("level = ndr_print_get_switch_value(ndr, $varname);"); + + $self->pidl("ndr_print_union(ndr, name, level, \"$name\");"); + + $self->pidl("switch (level) {"); + $self->indent; + foreach my $el (@{$e->{ELEMENTS}}) { + if ($el->{CASE} eq "default") { + $have_default = 1; + } + $self->pidl("$el->{CASE}:"); + if ($el->{TYPE} ne "EMPTY") { + $self->indent; + $self->ParseElementPrint($el, "$varname->$el->{NAME}", {}); + $self->deindent; + } + $self->pidl("break;"); + $self->pidl(""); + } + if (! $have_default) { + $self->pidl("default:"); + $self->pidl("\tndr_print_bad_level(ndr, name, level);"); + } + $self->deindent; + $self->pidl("}"); + + $self->end_flags($e); +} + +sub ParseUnionPullPrimitives($$$$) +{ + my ($self,$e,$varname,$switch_type) = @_; + my $have_default = 0; + + if (defined($switch_type)) { + $self->pidl("NDR_CHECK(ndr_pull_$switch_type(ndr, NDR_SCALARS, &_level));"); + $self->pidl("if (_level != level) {"); + $self->pidl("\treturn ndr_pull_error(ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value %u for $varname\", _level);"); + $self->pidl("}"); + } + + $self->pidl("switch (level) {"); + $self->indent; + foreach my $el (@{$e->{ELEMENTS}}) { + if ($el->{CASE} eq "default") { + $have_default = 1; + } + $self->pidl("$el->{CASE}: {"); + + if ($el->{TYPE} ne "EMPTY") { + $self->indent; + $self->DeclarePtrVariables($el); + $self->DeclareArrayVariables($el); + if (defined($e->{PROPERTIES}{relative_base})) { + $self->pidl("NDR_CHECK(ndr_pull_align(ndr, $el->{ALIGN}));"); + # set the current offset as base for relative pointers + # and store it based on the toplevel struct/union + $self->pidl("NDR_CHECK(ndr_pull_setup_relative_base_offset1(ndr, $varname, ndr->offset));"); + } + $self->ParseElementPull($el, "ndr", {$el->{NAME} => "$varname->$el->{NAME}"}, 1, 0); + $self->deindent; + } + $self->pidl("break; }"); + $self->pidl(""); + } + if (! $have_default) { + $self->pidl("default:"); + $self->pidl("\treturn ndr_pull_error(ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value \%u\", level);"); + } + $self->deindent; + $self->pidl("}"); +} + +sub ParseUnionPullDeferred($$$) +{ + my ($self,$e,$varname) = @_; + my $have_default = 0; + + if (defined($e->{PROPERTIES}{relative_base})) { + # retrieve the current offset as base for relative pointers + # based on the toplevel struct/union + $self->pidl("NDR_CHECK(ndr_pull_setup_relative_base_offset2(ndr, $varname));"); + } + $self->pidl("switch (level) {"); + $self->indent; + foreach my $el (@{$e->{ELEMENTS}}) { + if ($el->{CASE} eq "default") { + $have_default = 1; + } + + $self->pidl("$el->{CASE}:"); + if ($el->{TYPE} ne "EMPTY") { + $self->indent; + $self->ParseElementPull($el, "ndr", {$el->{NAME} => "$varname->$el->{NAME}"}, 0, 1); + $self->deindent; + } + $self->pidl("break;"); + $self->pidl(""); + } + if (! $have_default) { + $self->pidl("default:"); + $self->pidl("\treturn ndr_pull_error(ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value \%u\", level);"); + } + $self->deindent; + $self->pidl("}"); + + +} + +##################################################################### +# parse a union - pull side +sub ParseUnionPull($$$) +{ + my ($self,$e,$varname) = @_; + my $switch_type = $e->{SWITCH_TYPE}; + + $self->pidl("int level;"); + if (defined($switch_type)) { + if (Parse::Pidl::Typelist::typeIs($switch_type, "ENUM")) { + $switch_type = Parse::Pidl::Typelist::enum_type_fn(getType($switch_type)->{DATA}); + } + $self->pidl(mapTypeName($switch_type) . " _level;"); + } + + my %double_cases = (); + foreach my $el (@{$e->{ELEMENTS}}) { + next if ($el->{TYPE} eq "EMPTY"); + next if ($double_cases{"$el->{NAME}"}); + $self->DeclareMemCtxVariables($el); + $double_cases{"$el->{NAME}"} = 1; + } + + $self->start_flags($e); + + $self->pidl("level = ndr_pull_get_switch_value(ndr, $varname);"); + + $self->pidl("if (ndr_flags & NDR_SCALARS) {"); + $self->indent; + $self->ParseUnionPullPrimitives($e,$varname,$switch_type); + $self->deindent; + $self->pidl("}"); + + $self->pidl("if (ndr_flags & NDR_BUFFERS) {"); + $self->indent; + $self->ParseUnionPullDeferred($e,$varname); + $self->deindent; + $self->pidl("}"); + + $self->add_deferred(); + + $self->end_flags($e); +} + +sub DeclUnion($$$$) +{ + my ($e,$t,$name,$varname) = @_; + return ($t ne "pull"?"const ":"") . "union $name *$varname"; +} + +sub ArgsUnionNdrSize($$) +{ + my ($d,$name) = @_; + return "const union $name *r, uint32_t level, int flags"; +} + +$typefamily{UNION} = { + PUSH_FN_BODY => \&ParseUnionPush, + DECL => \&DeclUnion, + PULL_FN_BODY => \&ParseUnionPull, + PRINT_FN_BODY => \&ParseUnionPrint, + SIZE_FN_ARGS => \&ArgsUnionNdrSize, + SIZE_FN_BODY => \&ParseUnionNdrSize, +}; + +##################################################################### +# parse a typedef - push side +sub ParseTypedefPush($$$) +{ + my($self,$e,$varname) = @_; + + $typefamily{$e->{DATA}->{TYPE}}->{PUSH_FN_BODY}->($self, $e->{DATA}, $varname); +} + +##################################################################### +# parse a typedef - pull side +sub ParseTypedefPull($$$) +{ + my($self,$e,$varname) = @_; + + $typefamily{$e->{DATA}->{TYPE}}->{PULL_FN_BODY}->($self, $e->{DATA}, $varname); +} + +##################################################################### +# parse a typedef - print side +sub ParseTypedefPrint($$$$) +{ + my($self,$e,$name,$varname) = @_; + + $typefamily{$e->{DATA}->{TYPE}}->{PRINT_FN_BODY}->($self, $e->{DATA}, $name, $varname); +} + +##################################################################### +## calculate the size of a structure +sub ParseTypedefNdrSize($$$$) +{ + my($self,$t,$name,$varname) = @_; + + $typefamily{$t->{DATA}->{TYPE}}->{SIZE_FN_BODY}->($self, $t->{DATA}, $name, $varname); +} + +sub DeclTypedef($$$$) +{ + my ($e, $t, $name, $varname) = @_; + + return $typefamily{$e->{DATA}->{TYPE}}->{DECL}->($e->{DATA}, $t, $name, $varname); +} + +sub ArgsTypedefNdrSize($$$) +{ + my ($d, $name, $varname) = @_; + return $typefamily{$d->{DATA}->{TYPE}}->{SIZE_FN_ARGS}->($d->{DATA}, $name, $varname); +} + +$typefamily{TYPEDEF} = { + PUSH_FN_BODY => \&ParseTypedefPush, + DECL => \&DeclTypedef, + PULL_FN_BODY => \&ParseTypedefPull, + PRINT_FN_BODY => \&ParseTypedefPrint, + SIZE_FN_ARGS => \&ArgsTypedefNdrSize, + SIZE_FN_BODY => \&ParseTypedefNdrSize, +}; + +##################################################################### +# parse a function - print side +sub ParseFunctionPrint($$) +{ + my($self, $fn) = @_; + + $self->pidl_hdr("void ndr_print_$fn->{NAME}(struct ndr_print *ndr, const char *name, int flags, const struct $fn->{NAME} *r);"); + + return if has_property($fn, "noprint"); + + $self->pidl("_PUBLIC_ void ndr_print_$fn->{NAME}(struct ndr_print *ndr, const char *name, int flags, const struct $fn->{NAME} *r)"); + $self->pidl("{"); + $self->indent; + + foreach my $e (@{$fn->{ELEMENTS}}) { + $self->DeclareArrayVariables($e); + } + + $self->pidl("ndr_print_struct(ndr, name, \"$fn->{NAME}\");"); + $self->pidl("ndr->depth++;"); + + $self->pidl("if (flags & NDR_SET_VALUES) {"); + $self->pidl("\tndr->flags |= LIBNDR_PRINT_SET_VALUES;"); + $self->pidl("}"); + + $self->pidl("if (flags & NDR_IN) {"); + $self->indent; + $self->pidl("ndr_print_struct(ndr, \"in\", \"$fn->{NAME}\");"); + $self->pidl("ndr->depth++;"); + + my $env = GenerateFunctionInEnv($fn); + + foreach my $e (@{$fn->{ELEMENTS}}) { + if (grep(/in/,@{$e->{DIRECTION}})) { + $self->ParseElementPrint($e, $env->{$e->{NAME}}, $env); + } + } + $self->pidl("ndr->depth--;"); + $self->deindent; + $self->pidl("}"); + + $self->pidl("if (flags & NDR_OUT) {"); + $self->indent; + $self->pidl("ndr_print_struct(ndr, \"out\", \"$fn->{NAME}\");"); + $self->pidl("ndr->depth++;"); + + $env = GenerateFunctionOutEnv($fn); + foreach my $e (@{$fn->{ELEMENTS}}) { + if (grep(/out/,@{$e->{DIRECTION}})) { + $self->ParseElementPrint($e, $env->{$e->{NAME}}, $env); + } + } + if ($fn->{RETURN_TYPE}) { + $self->pidl("ndr_print_$fn->{RETURN_TYPE}(ndr, \"result\", r->out.result);"); + } + $self->pidl("ndr->depth--;"); + $self->deindent; + $self->pidl("}"); + + $self->pidl("ndr->depth--;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +##################################################################### +# parse a function +sub ParseFunctionPush($$) +{ + my($self, $fn) = @_; + + $self->fn_declare("push", $fn, "enum ndr_err_code ndr_push_$fn->{NAME}(struct ndr_push *ndr, int flags, const struct $fn->{NAME} *r)") or return; + + return if has_property($fn, "nopush"); + + $self->pidl("{"); + $self->indent; + + foreach my $e (@{$fn->{ELEMENTS}}) { + $self->DeclareArrayVariables($e); + } + + $self->pidl("if (flags & NDR_IN) {"); + $self->indent; + + my $env = GenerateFunctionInEnv($fn); + + EnvSubstituteValue($env, $fn); + + foreach my $e (@{$fn->{ELEMENTS}}) { + if (grep(/in/,@{$e->{DIRECTION}})) { + $self->ParseElementPush($e, "ndr", $env, 1, 1); + } + } + + $self->deindent; + $self->pidl("}"); + + $self->pidl("if (flags & NDR_OUT) {"); + $self->indent; + + $env = GenerateFunctionOutEnv($fn); + foreach my $e (@{$fn->{ELEMENTS}}) { + if (grep(/out/,@{$e->{DIRECTION}})) { + $self->ParseElementPush($e, "ndr", $env, 1, 1); + } + } + + if ($fn->{RETURN_TYPE}) { + $self->pidl("NDR_CHECK(ndr_push_$fn->{RETURN_TYPE}(ndr, NDR_SCALARS, r->out.result));"); + } + + $self->deindent; + $self->pidl("}"); + $self->pidl("return NDR_ERR_SUCCESS;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +sub AllocateArrayLevel($$$$$$) +{ + my ($self,$e,$l,$ndr,$var,$size) = @_; + + my $pl = GetPrevLevel($e, $l); + if (defined($pl) and + $pl->{TYPE} eq "POINTER" and + $pl->{POINTER_TYPE} eq "ref" + and not $l->{IS_ZERO_TERMINATED}) { + $self->pidl("if (ndr->flags & LIBNDR_FLAG_REF_ALLOC) {"); + $self->pidl("\tNDR_PULL_ALLOC_N($ndr, $var, $size);"); + $self->pidl("}"); + if (grep(/in/,@{$e->{DIRECTION}}) and + grep(/out/,@{$e->{DIRECTION}})) { + $self->pidl("memcpy(r->out.$e->{NAME}, r->in.$e->{NAME}, $size * sizeof(*r->in.$e->{NAME}));"); + } + return; + } + + $self->pidl("NDR_PULL_ALLOC_N($ndr, $var, $size);"); +} + +##################################################################### +# parse a function +sub ParseFunctionPull($$) +{ + my($self,$fn) = @_; + + # pull function args + $self->fn_declare("pull", $fn, "enum ndr_err_code ndr_pull_$fn->{NAME}(struct ndr_pull *ndr, int flags, struct $fn->{NAME} *r)") or return; + + $self->pidl("{"); + $self->indent; + + # declare any internal pointers we need + foreach my $e (@{$fn->{ELEMENTS}}) { + $self->DeclarePtrVariables($e); + $self->DeclareArrayVariables($e); + } + + my %double_cases = (); + foreach my $e (@{$fn->{ELEMENTS}}) { + next if ($e->{TYPE} eq "EMPTY"); + next if ($double_cases{"$e->{NAME}"}); + $self->DeclareMemCtxVariables($e); + $double_cases{"$e->{NAME}"} = 1; + } + + $self->pidl("if (flags & NDR_IN) {"); + $self->indent; + + # auto-init the out section of a structure. I originally argued that + # this was a bad idea as it hides bugs, but coping correctly + # with initialisation and not wiping ref vars is turning + # out to be too tricky (tridge) + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless grep(/out/, @{$e->{DIRECTION}}); + $self->pidl("ZERO_STRUCT(r->out);"); + $self->pidl(""); + last; + } + + my $env = GenerateFunctionInEnv($fn); + + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless (grep(/in/, @{$e->{DIRECTION}})); + $self->ParseElementPull($e, "ndr", $env, 1, 1); + } + + # allocate the "simple" out ref variables. FIXME: Shouldn't this have it's + # own flag rather than be in NDR_IN ? + + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless (grep(/out/, @{$e->{DIRECTION}})); + next unless ($e->{LEVELS}[0]->{TYPE} eq "POINTER" and + $e->{LEVELS}[0]->{POINTER_TYPE} eq "ref"); + next if (($e->{LEVELS}[1]->{TYPE} eq "DATA") and + ($e->{LEVELS}[1]->{DATA_TYPE} eq "string")); + next if (($e->{LEVELS}[1]->{TYPE} eq "ARRAY") + and $e->{LEVELS}[1]->{IS_ZERO_TERMINATED}); + + if ($e->{LEVELS}[1]->{TYPE} eq "ARRAY") { + my $size = ParseExprExt($e->{LEVELS}[1]->{SIZE_IS}, $env, $e->{ORIGINAL}, + check_null_pointer($e, $env, sub { $self->pidl(shift); }, + "return ndr_pull_error(ndr, NDR_ERR_INVALID_POINTER, \"NULL Pointer for size_is()\");"), + check_fully_dereferenced($e, $env)); + $self->pidl("NDR_PULL_ALLOC_N(ndr, r->out.$e->{NAME}, $size);"); + + if (grep(/in/, @{$e->{DIRECTION}})) { + $self->pidl("memcpy(r->out.$e->{NAME}, r->in.$e->{NAME}, ($size) * sizeof(*r->in.$e->{NAME}));"); + } else { + $self->pidl("memset(r->out.$e->{NAME}, 0, ($size) * sizeof(*r->out.$e->{NAME}));"); + } + } else { + $self->pidl("NDR_PULL_ALLOC(ndr, r->out.$e->{NAME});"); + + if (grep(/in/, @{$e->{DIRECTION}})) { + $self->pidl("*r->out.$e->{NAME} = *r->in.$e->{NAME};"); + } else { + $self->pidl("ZERO_STRUCTP(r->out.$e->{NAME});"); + } + } + } + + $self->add_deferred(); + $self->deindent; + $self->pidl("}"); + + $self->pidl("if (flags & NDR_OUT) {"); + $self->indent; + + $env = GenerateFunctionOutEnv($fn); + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless grep(/out/, @{$e->{DIRECTION}}); + $self->ParseElementPull($e, "ndr", $env, 1, 1); + } + + if ($fn->{RETURN_TYPE}) { + $self->pidl("NDR_CHECK(ndr_pull_$fn->{RETURN_TYPE}(ndr, NDR_SCALARS, &r->out.result));"); + } + + $self->add_deferred(); + $self->deindent; + $self->pidl("}"); + + $self->pidl("return NDR_ERR_SUCCESS;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +sub AuthServiceStruct($$$) +{ + my ($self, $ifacename, $authservice) = @_; + my @a = split /,/, $authservice; + my $authservice_count = $#a + 1; + + $self->pidl("static const char * const $ifacename\_authservice_strings[] = {"); + foreach my $ap (@a) { + $self->pidl("\t$ap, "); + } + $self->pidl("};"); + $self->pidl(""); + + $self->pidl("static const struct ndr_interface_string_array $ifacename\_authservices = {"); + $self->pidl("\t.count\t= $authservice_count,"); + $self->pidl("\t.names\t= $ifacename\_authservice_strings"); + $self->pidl("};"); + $self->pidl(""); +} + +##################################################################### +# produce a function call table +sub FunctionTable($$) +{ + my($self,$interface) = @_; + my $count = 0; + my $uname = uc $interface->{NAME}; + + return if ($#{$interface->{FUNCTIONS}}+1 == 0); + return unless defined ($interface->{PROPERTIES}->{uuid}); + + $self->pidl("static const struct ndr_interface_call $interface->{NAME}\_calls[] = {"); + foreach my $d (@{$interface->{FUNCTIONS}}) { + next if not defined($d->{OPNUM}); + $self->pidl("\t{"); + $self->pidl("\t\t\"$d->{NAME}\","); + $self->pidl("\t\tsizeof(struct $d->{NAME}),"); + $self->pidl("\t\t(ndr_push_flags_fn_t) ndr_push_$d->{NAME},"); + $self->pidl("\t\t(ndr_pull_flags_fn_t) ndr_pull_$d->{NAME},"); + $self->pidl("\t\t(ndr_print_function_t) ndr_print_$d->{NAME},"); + $self->pidl("\t\t".($d->{ASYNC}?"true":"false").","); + $self->pidl("\t},"); + $count++; + } + $self->pidl("\t{ NULL, 0, NULL, NULL, NULL, false }"); + $self->pidl("};"); + $self->pidl(""); + + $self->pidl("static const char * const $interface->{NAME}\_endpoint_strings[] = {"); + foreach my $ep (@{$interface->{ENDPOINTS}}) { + $self->pidl("\t$ep, "); + } + my $endpoint_count = $#{$interface->{ENDPOINTS}}+1; + + $self->pidl("};"); + $self->pidl(""); + + $self->pidl("static const struct ndr_interface_string_array $interface->{NAME}\_endpoints = {"); + $self->pidl("\t.count\t= $endpoint_count,"); + $self->pidl("\t.names\t= $interface->{NAME}\_endpoint_strings"); + $self->pidl("};"); + $self->pidl(""); + + if (! defined $interface->{PROPERTIES}->{authservice}) { + $interface->{PROPERTIES}->{authservice} = "\"host\""; + } + + $self->AuthServiceStruct($interface->{NAME}, + $interface->{PROPERTIES}->{authservice}); + + $self->pidl("\nconst struct ndr_interface_table ndr_table_$interface->{NAME} = {"); + $self->pidl("\t.name\t\t= \"$interface->{NAME}\","); + $self->pidl("\t.syntax_id\t= {"); + $self->pidl("\t\t" . print_uuid($interface->{UUID}) .","); + $self->pidl("\t\tNDR_$uname\_VERSION"); + $self->pidl("\t},"); + $self->pidl("\t.helpstring\t= NDR_$uname\_HELPSTRING,"); + $self->pidl("\t.num_calls\t= $count,"); + $self->pidl("\t.calls\t\t= $interface->{NAME}\_calls,"); + $self->pidl("\t.endpoints\t= &$interface->{NAME}\_endpoints,"); + $self->pidl("\t.authservices\t= &$interface->{NAME}\_authservices"); + $self->pidl("};"); + $self->pidl(""); + +} + +##################################################################### +# generate include statements for imported idl files +sub HeaderImport +{ + my $self = shift; + my @imports = @_; + foreach (@imports) { + s/\.idl\"$//; + s/^\"//; + $self->pidl(choose_header("librpc/gen_ndr/ndr_$_\.h", "gen_ndr/ndr_$_.h")); + } +} + +##################################################################### +# generate include statements for included header files +sub HeaderInclude +{ + my $self = shift; + my @includes = @_; + foreach (@includes) { + $self->pidl_hdr("#include $_"); + } +} + +##################################################################### +# generate prototypes and defines for the interface definitions +# FIXME: these prototypes are for the DCE/RPC client functions, not the +# NDR parser and so do not belong here, technically speaking +sub HeaderInterface($$$) +{ + my($self,$interface,$needed) = @_; + + my $count = 0; + + if ($needed->{"compression"}) { + $self->pidl(choose_header("librpc/ndr/ndr_compression.h", "ndr/compression.h")); + } + + if (has_property($interface, "object")) { + $self->pidl(choose_header("librpc/gen_ndr/ndr_orpc.h", "ndr/orpc.h")); + } + + if (defined $interface->{PROPERTIES}->{helper}) { + $self->HeaderInclude(split /,/, $interface->{PROPERTIES}->{helper}); + } + + if (defined $interface->{PROPERTIES}->{uuid}) { + my $name = uc $interface->{NAME}; + $self->pidl_hdr("#define NDR_$name\_UUID " . + Parse::Pidl::Util::make_str(lc($interface->{PROPERTIES}->{uuid}))); + + if(!defined $interface->{PROPERTIES}->{version}) { $interface->{PROPERTIES}->{version} = "0.0"; } + $self->pidl_hdr("#define NDR_$name\_VERSION $interface->{PROPERTIES}->{version}"); + + $self->pidl_hdr("#define NDR_$name\_NAME \"$interface->{NAME}\""); + + if(!defined $interface->{PROPERTIES}->{helpstring}) { $interface->{PROPERTIES}->{helpstring} = "NULL"; } + $self->pidl_hdr("#define NDR_$name\_HELPSTRING $interface->{PROPERTIES}->{helpstring}"); + + $self->pidl_hdr("extern const struct ndr_interface_table ndr_table_$interface->{NAME};"); + } + + foreach (@{$interface->{FUNCTIONS}}) { + next if has_property($_, "noopnum"); + next if grep(/$_->{NAME}/,@{$interface->{INHERITED_FUNCTIONS}}); + my $u_name = uc $_->{NAME}; + + my $val = sprintf("0x%02x", $count); + if (defined($interface->{BASE})) { + $val .= " + NDR_" . uc $interface->{BASE} . "_CALL_COUNT"; + } + + $self->pidl_hdr("#define NDR_$u_name ($val)"); + + $self->pidl_hdr(""); + $count++; + } + + my $val = $count; + + if (defined($interface->{BASE})) { + $val .= " + NDR_" . uc $interface->{BASE} . "_CALL_COUNT"; + } + + $self->pidl_hdr("#define NDR_" . uc $interface->{NAME} . "_CALL_COUNT ($val)"); + +} + +sub ParseTypePush($$$$$) +{ + my ($self,$e,$varname, $primitives, $deferred) = @_; + + # save the old relative_base_offset + $self->pidl("uint32_t _save_relative_base_offset = ndr_push_get_relative_base_offset(ndr);") if defined(has_property($e, "relative_base")); + $typefamily{$e->{TYPE}}->{PUSH_FN_BODY}->($self, $e, $varname); + # restore the old relative_base_offset + $self->pidl("ndr_push_restore_relative_base_offset(ndr, _save_relative_base_offset);") if defined(has_property($e, "relative_base")); +} + +sub ParseTypePushFunction($$$) +{ + my ($self, $e, $varname) = @_; + + my $args = $typefamily{$e->{TYPE}}->{DECL}->($e, "push", $e->{NAME}, $varname); + $self->fn_declare("push", $e, "enum ndr_err_code ".TypeFunctionName("ndr_push", $e)."(struct ndr_push *ndr, int ndr_flags, $args)") or return; + + $self->pidl("{"); + $self->indent; + $self->ParseTypePush($e, $varname, 1, 1); + $self->pidl("return NDR_ERR_SUCCESS;"); + $self->deindent; + $self->pidl("}"); + $self->pidl("");; +} + +sub ParseTypePull($$$$$) +{ + my ($self, $e, $varname, $primitives, $deferred) = @_; + + # save the old relative_base_offset + $self->pidl("uint32_t _save_relative_base_offset = ndr_pull_get_relative_base_offset(ndr);") if defined(has_property($e, "relative_base")); + $typefamily{$e->{TYPE}}->{PULL_FN_BODY}->($self, $e, $varname); + # restore the old relative_base_offset + $self->pidl("ndr_pull_restore_relative_base_offset(ndr, _save_relative_base_offset);") if defined(has_property($e, "relative_base")); +} + +sub ParseTypePullFunction($$) +{ + my ($self, $e, $varname) = @_; + + my $args = $typefamily{$e->{TYPE}}->{DECL}->($e, "pull", $e->{NAME}, $varname); + + $self->fn_declare("pull", $e, "enum ndr_err_code ".TypeFunctionName("ndr_pull", $e)."(struct ndr_pull *ndr, int ndr_flags, $args)") or return; + + $self->pidl("{"); + $self->indent; + $self->ParseTypePull($e, $varname, 1, 1); + $self->pidl("return NDR_ERR_SUCCESS;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +sub ParseTypePrint($$$) +{ + my ($self, $e, $varname) = @_; + + $typefamily{$e->{TYPE}}->{PRINT_FN_BODY}->($self, $e, $e->{NAME}, $varname); +} + +sub ParseTypePrintFunction($$$) +{ + my ($self, $e, $varname) = @_; + my $args = $typefamily{$e->{TYPE}}->{DECL}->($e, "print", $e->{NAME}, $varname); + + $self->pidl_hdr("void ".TypeFunctionName("ndr_print", $e)."(struct ndr_print *ndr, const char *name, $args);"); + + return if (has_property($e, "noprint")); + + $self->pidl("_PUBLIC_ void ".TypeFunctionName("ndr_print", $e)."(struct ndr_print *ndr, const char *name, $args)"); + $self->pidl("{"); + $self->indent; + $self->ParseTypePrint($e, $varname); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +sub ParseTypeNdrSize($$) +{ + my ($self,$t) = @_; + + my $varname = "r"; + my $tf = $typefamily{$t->{TYPE}}; + my $args = $tf->{SIZE_FN_ARGS}->($t, $t->{NAME}, $varname); + + $self->fn_declare("size", $t, "size_t ndr_size_$t->{NAME}($args)") or return; + + $self->pidl("{"); + $self->indent; + $typefamily{$t->{TYPE}}->{SIZE_FN_BODY}->($self,$t, $t->{NAME}, $varname); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +##################################################################### +# parse the interface definitions +sub ParseInterface($$$) +{ + my($self,$interface,$needed) = @_; + + $self->pidl_hdr("#ifndef _HEADER_NDR_$interface->{NAME}"); + $self->pidl_hdr("#define _HEADER_NDR_$interface->{NAME}"); + + $self->pidl_hdr(""); + + $self->HeaderInterface($interface, $needed); + + # Typedefs + foreach my $d (@{$interface->{TYPES}}) { + next unless(typeHasBody($d)); + + ($needed->{TypeFunctionName("ndr_push", $d)}) && $self->ParseTypePushFunction($d, "r"); + ($needed->{TypeFunctionName("ndr_pull", $d)}) && $self->ParseTypePullFunction($d, "r"); + ($needed->{TypeFunctionName("ndr_print", $d)}) && $self->ParseTypePrintFunction($d, "r"); + + # Make sure we don't generate a function twice... + $needed->{TypeFunctionName("ndr_push", $d)} = + $needed->{TypeFunctionName("ndr_pull", $d)} = + $needed->{TypeFunctionName("ndr_print", $d)} = 0; + + ($needed->{"ndr_size_$d->{NAME}"}) && $self->ParseTypeNdrSize($d); + } + + # Functions + foreach my $d (@{$interface->{FUNCTIONS}}) { + ($needed->{"ndr_push_$d->{NAME}"}) && $self->ParseFunctionPush($d); + ($needed->{"ndr_pull_$d->{NAME}"}) && $self->ParseFunctionPull($d); + ($needed->{"ndr_print_$d->{NAME}"}) && $self->ParseFunctionPrint($d); + + # Make sure we don't generate a function twice... + $needed->{"ndr_push_$d->{NAME}"} = $needed->{"ndr_pull_$d->{NAME}"} = + $needed->{"ndr_print_$d->{NAME}"} = 0; + } + + $self->FunctionTable($interface); + + $self->pidl_hdr("#endif /* _HEADER_NDR_$interface->{NAME} */"); +} + +sub GenerateIncludes($) +{ + my ($self) = @_; + if (is_intree()) { + $self->pidl("#include \"includes.h\""); + } else { + $self->pidl("#define _GNU_SOURCE"); + $self->pidl("#include <stdint.h>"); + $self->pidl("#include <stdlib.h>"); + $self->pidl("#include <stdio.h>"); + $self->pidl("#include <stdbool.h>"); + $self->pidl("#include <stdarg.h>"); + $self->pidl("#include <string.h>"); + } +} + +##################################################################### +# parse a parsed IDL structure back into an IDL file +sub Parse($$$$) +{ + my($self, $ndr,$gen_header,$ndr_header) = @_; + + $self->pidl_hdr("/* header auto-generated by pidl */"); + $self->pidl_hdr(""); + $self->pidl_hdr(choose_header("librpc/ndr/libndr.h", "ndr.h")); + $self->pidl_hdr("#include \"$gen_header\"") if ($gen_header); + $self->pidl_hdr(""); + + $self->pidl("/* parser auto-generated by pidl */"); + $self->pidl(""); + $self->GenerateIncludes(); + $self->pidl("#include \"$ndr_header\"") if ($ndr_header); + $self->pidl(""); + + my %needed = (); + + foreach (@{$ndr}) { + ($_->{TYPE} eq "INTERFACE") && NeededInterface($_, \%needed); + } + + foreach (@{$ndr}) { + ($_->{TYPE} eq "INTERFACE") && $self->ParseInterface($_, \%needed); + ($_->{TYPE} eq "IMPORT") && $self->HeaderImport(@{$_->{PATHS}}); + ($_->{TYPE} eq "INCLUDE") && $self->HeaderInclude(@{$_->{PATHS}}); + } + + return ($self->{res_hdr}, $self->{res}); +} + +sub NeededElement($$$) +{ + my ($e, $dir, $needed) = @_; + + return if ($e->{TYPE} eq "EMPTY"); + + return if (ref($e->{TYPE}) eq "HASH" and + not defined($e->{TYPE}->{NAME})); + + my ($t, $rt); + if (ref($e->{TYPE}) eq "HASH") { + $t = $e->{TYPE}->{TYPE}."_".$e->{TYPE}->{NAME}; + } else { + $t = $e->{TYPE}; + } + + if (ref($e->{REPRESENTATION_TYPE}) eq "HASH") { + $rt = $e->{REPRESENTATION_TYPE}->{TYPE}."_".$e->{REPRESENTATION_TYPE}->{NAME}; + } else { + $rt = $e->{REPRESENTATION_TYPE}; + } + + die ("$e->{NAME} $t, $rt FOO") unless ($rt ne ""); + + my @fn = (); + if ($dir eq "print") { + push(@fn, TypeFunctionName("ndr_print", $e->{REPRESENTATION_TYPE})); + } elsif ($dir eq "pull") { + push (@fn, TypeFunctionName("ndr_pull", $e->{TYPE})); + push (@fn, "ndr_$t\_to_$rt") + if ($rt ne $t); + } elsif ($dir eq "push") { + push (@fn, TypeFunctionName("ndr_push", $e->{TYPE})); + push (@fn, "ndr_$rt\_to_$t") + if ($rt ne $t); + } else { + die("invalid direction `$dir'"); + } + + foreach (@fn) { + unless (defined($needed->{$_})) { + $needed->{$_} = 1; + } + } +} + +sub NeededFunction($$) +{ + my ($fn,$needed) = @_; + $needed->{"ndr_pull_$fn->{NAME}"} = 1; + $needed->{"ndr_push_$fn->{NAME}"} = 1; + $needed->{"ndr_print_$fn->{NAME}"} = 1; + foreach my $e (@{$fn->{ELEMENTS}}) { + $e->{PARENT} = $fn; + NeededElement($e, $_, $needed) foreach ("pull", "push", "print"); + } +} + +sub NeededType($$$) +{ + sub NeededType($$$); + my ($t,$needed,$req) = @_; + + NeededType($t->{DATA}, $needed, $req) if ($t->{TYPE} eq "TYPEDEF"); + + if ($t->{TYPE} eq "STRUCT" or $t->{TYPE} eq "UNION") { + return unless defined($t->{ELEMENTS}); + for my $e (@{$t->{ELEMENTS}}) { + $e->{PARENT} = $t; + if (has_property($e, "compression")) { + $needed->{"compression"} = 1; + } + NeededElement($e, $req, $needed); + NeededType($e->{TYPE}, $needed, $req) if (ref($e->{TYPE}) eq "HASH"); + } + } +} + +##################################################################### +# work out what parse functions are needed +sub NeededInterface($$) +{ + my ($interface,$needed) = @_; + NeededFunction($_, $needed) foreach (@{$interface->{FUNCTIONS}}); + foreach (reverse @{$interface->{TYPES}}) { + if (has_property($_, "public")) { + $needed->{TypeFunctionName("ndr_pull", $_)} = $needed->{TypeFunctionName("ndr_push", $_)} = + $needed->{TypeFunctionName("ndr_print", $_)} = 1; + } + + NeededType($_, $needed, "pull") if ($needed->{TypeFunctionName("ndr_pull", $_)}); + NeededType($_, $needed, "push") if ($needed->{TypeFunctionName("ndr_push", $_)}); + NeededType($_, $needed, "print") if ($needed->{TypeFunctionName("ndr_print", $_)}); + if (has_property($_, "gensize")) { + $needed->{"ndr_size_$_->{NAME}"} = 1; + } + } +} + +sub TypeFunctionName($$) +{ + my ($prefix, $t) = @_; + + return "$prefix\_$t->{NAME}" if (ref($t) eq "HASH" and + $t->{TYPE} eq "TYPEDEF"); + return "$prefix\_$t->{TYPE}_$t->{NAME}" if (ref($t) eq "HASH"); + return "$prefix\_$t"; +} + +1; diff --git a/source/pidl/lib/Parse/Pidl/Samba4/NDR/Server.pm b/source/pidl/lib/Parse/Pidl/Samba4/NDR/Server.pm new file mode 100644 index 00000000000..b084ec525fa --- /dev/null +++ b/source/pidl/lib/Parse/Pidl/Samba4/NDR/Server.pm @@ -0,0 +1,328 @@ +################################################### +# server boilerplate generator +# Copyright tridge@samba.org 2003 +# Copyright metze@samba.org 2004 +# released under the GNU GPL + +package Parse::Pidl::Samba4::NDR::Server; + +use strict; +use Parse::Pidl::Util; + +use vars qw($VERSION); +$VERSION = '0.01'; + +my($res); + +sub pidl($) +{ + $res .= shift; +} + + +##################################################### +# generate the switch statement for function dispatch +sub gen_dispatch_switch($) +{ + my $interface = shift; + + foreach my $fn (@{$interface->{FUNCTIONS}}) { + next if not defined($fn->{OPNUM}); + + pidl "\tcase $fn->{OPNUM}: {\n"; + pidl "\t\tstruct $fn->{NAME} *r2 = (struct $fn->{NAME} *)r;\n"; + pidl "\t\tif (DEBUGLEVEL >= 10) {\n"; + pidl "\t\t\tNDR_PRINT_FUNCTION_DEBUG($fn->{NAME}, NDR_IN, r2);\n"; + pidl "\t\t}\n"; + if ($fn->{RETURN_TYPE} && $fn->{RETURN_TYPE} ne "void") { + pidl "\t\tr2->out.result = dcesrv_$fn->{NAME}(dce_call, mem_ctx, r2);\n"; + } else { + pidl "\t\tdcesrv_$fn->{NAME}(dce_call, mem_ctx, r2);\n"; + } + pidl "\t\tif (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {\n"; + pidl "\t\t\tDEBUG(5,(\"function $fn->{NAME} will reply async\\n\"));\n"; + pidl "\t\t}\n"; + pidl "\t\tbreak;\n\t}\n"; + } +} + +##################################################### +# generate the switch statement for function reply +sub gen_reply_switch($) +{ + my $interface = shift; + + foreach my $fn (@{$interface->{FUNCTIONS}}) { + next if not defined($fn->{OPNUM}); + + pidl "\tcase $fn->{OPNUM}: {\n"; + pidl "\t\tstruct $fn->{NAME} *r2 = (struct $fn->{NAME} *)r;\n"; + pidl "\t\tif (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {\n"; + pidl "\t\t\tDEBUG(5,(\"function $fn->{NAME} replied async\\n\"));\n"; + pidl "\t\t}\n"; + pidl "\t\tif (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {\n"; + pidl "\t\t\tNDR_PRINT_FUNCTION_DEBUG($fn->{NAME}, NDR_OUT | NDR_SET_VALUES, r2);\n"; + pidl "\t\t}\n"; + pidl "\t\tif (dce_call->fault_code != 0) {\n"; + pidl "\t\t\tDEBUG(2,(\"dcerpc_fault %s in $fn->{NAME}\\n\", dcerpc_errstr(mem_ctx, dce_call->fault_code)));\n"; + pidl "\t\t}\n"; + pidl "\t\tbreak;\n\t}\n"; + } +} + +##################################################################### +# produce boilerplate code for a interface +sub Boilerplate_Iface($) +{ + my($interface) = shift; + my $name = $interface->{NAME}; + my $uname = uc $name; + my $uuid = lc($interface->{PROPERTIES}->{uuid}); + my $if_version = $interface->{PROPERTIES}->{version}; + + pidl " +static NTSTATUS $name\__op_bind(struct dcesrv_call_state *dce_call, const struct dcesrv_interface *iface) +{ +#ifdef DCESRV_INTERFACE_$uname\_BIND + return DCESRV_INTERFACE_$uname\_BIND(dce_call,iface); +#else + return NT_STATUS_OK; +#endif +} + +static void $name\__op_unbind(struct dcesrv_connection_context *context, const struct dcesrv_interface *iface) +{ +#ifdef DCESRV_INTERFACE_$uname\_UNBIND + DCESRV_INTERFACE_$uname\_UNBIND(context, iface); +#else + return; +#endif +} + +static NTSTATUS $name\__op_ndr_pull(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct ndr_pull *pull, void **r) +{ + enum ndr_err_code ndr_err; + uint16_t opnum = dce_call->pkt.u.request.opnum; + + dce_call->fault_code = 0; + + if (opnum >= ndr_table_$name.num_calls) { + dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NET_WRITE_FAULT; + } + + *r = talloc_named(mem_ctx, + ndr_table_$name.calls[opnum].struct_size, + \"struct %s\", + ndr_table_$name.calls[opnum].name); + NT_STATUS_HAVE_NO_MEMORY(*r); + + /* unravel the NDR for the packet */ + ndr_err = ndr_table_$name.calls[opnum].ndr_pull(pull, NDR_IN, *r); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + dcerpc_log_packet(&ndr_table_$name, opnum, NDR_IN, + &dce_call->pkt.u.request.stub_and_verifier); + dce_call->fault_code = DCERPC_FAULT_NDR; + return NT_STATUS_NET_WRITE_FAULT; + } + + return NT_STATUS_OK; +} + +static NTSTATUS $name\__op_dispatch(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r) +{ + uint16_t opnum = dce_call->pkt.u.request.opnum; + + switch (opnum) { +"; + gen_dispatch_switch($interface); + +pidl " + default: + dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR; + break; + } + + if (dce_call->fault_code != 0) { + dcerpc_log_packet(&ndr_table_$name, opnum, NDR_IN, + &dce_call->pkt.u.request.stub_and_verifier); + return NT_STATUS_NET_WRITE_FAULT; + } + + return NT_STATUS_OK; +} + +static NTSTATUS $name\__op_reply(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r) +{ + uint16_t opnum = dce_call->pkt.u.request.opnum; + + switch (opnum) { +"; + gen_reply_switch($interface); + +pidl " + default: + dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR; + break; + } + + if (dce_call->fault_code != 0) { + dcerpc_log_packet(&ndr_table_$name, opnum, NDR_IN, + &dce_call->pkt.u.request.stub_and_verifier); + return NT_STATUS_NET_WRITE_FAULT; + } + + return NT_STATUS_OK; +} + +static NTSTATUS $name\__op_ndr_push(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct ndr_push *push, const void *r) +{ + enum ndr_err_code ndr_err; + uint16_t opnum = dce_call->pkt.u.request.opnum; + + ndr_err = ndr_table_$name.calls[opnum].ndr_push(push, NDR_OUT, r); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + dce_call->fault_code = DCERPC_FAULT_NDR; + return NT_STATUS_NET_WRITE_FAULT; + } + + return NT_STATUS_OK; +} + +const struct dcesrv_interface dcesrv\_$name\_interface = { + .name = \"$name\", + .syntax_id = {".print_uuid($uuid).",$if_version}, + .bind = $name\__op_bind, + .unbind = $name\__op_unbind, + .ndr_pull = $name\__op_ndr_pull, + .dispatch = $name\__op_dispatch, + .reply = $name\__op_reply, + .ndr_push = $name\__op_ndr_push +}; + +"; +} + +##################################################################### +# produce boilerplate code for an endpoint server +sub Boilerplate_Ep_Server($) +{ + my($interface) = shift; + my $name = $interface->{NAME}; + my $uname = uc $name; + + pidl " +static NTSTATUS $name\__op_init_server(struct dcesrv_context *dce_ctx, const struct dcesrv_endpoint_server *ep_server) +{ + int i; + + for (i=0;i<ndr_table_$name.endpoints->count;i++) { + NTSTATUS ret; + const char *name = ndr_table_$name.endpoints->names[i]; + + ret = dcesrv_interface_register(dce_ctx, name, &dcesrv_$name\_interface, NULL); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(1,(\"$name\_op_init_server: failed to register endpoint \'%s\'\\n\",name)); + return ret; + } + } + + return NT_STATUS_OK; +} + +static bool $name\__op_interface_by_uuid(struct dcesrv_interface *iface, const struct GUID *uuid, uint32_t if_version) +{ + if (dcesrv_$name\_interface.syntax_id.if_version == if_version && + GUID_equal(\&dcesrv\_$name\_interface.syntax_id.uuid, uuid)) { + memcpy(iface,&dcesrv\_$name\_interface, sizeof(*iface)); + return true; + } + + return false; +} + +static bool $name\__op_interface_by_name(struct dcesrv_interface *iface, const char *name) +{ + if (strcmp(dcesrv_$name\_interface.name, name)==0) { + memcpy(iface, &dcesrv_$name\_interface, sizeof(*iface)); + return true; + } + + return false; +} + +NTSTATUS dcerpc_server_$name\_init(void) +{ + NTSTATUS ret; + struct dcesrv_endpoint_server ep_server; + + /* fill in our name */ + ep_server.name = \"$name\"; + + /* fill in all the operations */ + ep_server.init_server = $name\__op_init_server; + + ep_server.interface_by_uuid = $name\__op_interface_by_uuid; + ep_server.interface_by_name = $name\__op_interface_by_name; + + /* register ourselves with the DCERPC subsystem. */ + ret = dcerpc_register_ep_server(&ep_server); + + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(0,(\"Failed to register \'$name\' endpoint server!\\n\")); + return ret; + } + + return ret; +} + +"; +} + +##################################################################### +# dcerpc server boilerplate from a parsed IDL structure +sub ParseInterface($) +{ + my($interface) = shift; + my $count = 0; + + if (!defined $interface->{PROPERTIES}->{uuid}) { + return $res; + } + + if (!defined $interface->{PROPERTIES}->{version}) { + $interface->{PROPERTIES}->{version} = "0.0"; + } + + foreach my $fn (@{$interface->{FUNCTIONS}}) { + if (defined($fn->{OPNUM})) { $count++; } + } + + if ($count == 0) { + return $res; + } + + $res .= "/* $interface->{NAME} - dcerpc server boilerplate generated by pidl */\n\n"; + Boilerplate_Iface($interface); + Boilerplate_Ep_Server($interface); + + return $res; +} + +sub Parse($$) +{ + my($ndr,$header) = @_; + + $res = ""; + $res .= "/* server functions auto-generated by pidl */\n"; + $res .= "#include \"$header\"\n"; + $res .= "\n"; + + foreach my $x (@{$ndr}) { + ParseInterface($x) if ($x->{TYPE} eq "INTERFACE" and not defined($x->{PROPERTIES}{object})); + } + + return $res; +} + +1; diff --git a/source/pidl/lib/Parse/Pidl/Samba4/Python.pm b/source/pidl/lib/Parse/Pidl/Samba4/Python.pm new file mode 100644 index 00000000000..176104a7826 --- /dev/null +++ b/source/pidl/lib/Parse/Pidl/Samba4/Python.pm @@ -0,0 +1,1213 @@ +################################################### +# Python function wrapper generator +# Copyright jelmer@samba.org 2007-2008 +# released under the GNU GPL + +package Parse::Pidl::Samba4::Python; + +use Exporter; +@ISA = qw(Exporter); + +use strict; +use Parse::Pidl::Typelist qw(hasType resolveType getType mapTypeName expandAlias); +use Parse::Pidl::Util qw(has_property ParseExpr); +use Parse::Pidl::NDR qw(GetPrevLevel GetNextLevel ContainsDeferred is_charset_array); +use Parse::Pidl::CUtil qw(get_value_of get_pointer_to); +use Parse::Pidl::Samba4::Header qw(GenerateFunctionInEnv GenerateFunctionOutEnv EnvSubstituteValue GenerateStructEnv); + +use vars qw($VERSION); +$VERSION = '0.01'; + +sub new($) { + my ($class) = @_; + my $self = { res => "", res_hdr => "", tabs => "", constants => {}, + module_methods => [], module_objects => [], ready_types => [], + readycode => [] }; + bless($self, $class); +} + +sub pidl_hdr ($$) +{ + my $self = shift; + $self->{res_hdr} .= shift; +} + +sub pidl($$) +{ + my ($self, $d) = @_; + if ($d) { + $self->{res} .= $self->{tabs}; + $self->{res} .= $d; + } + $self->{res} .= "\n"; +} + +sub indent($) +{ + my ($self) = @_; + $self->{tabs} .= "\t"; +} + +sub deindent($) +{ + my ($self) = @_; + $self->{tabs} = substr($self->{tabs}, 0, -1); +} + +sub Import +{ + my $self = shift; + my @imports = @_; + foreach (@imports) { + s/\.idl\"$//; + s/^\"//; + $self->pidl_hdr("#include \"librpc/gen_ndr/py_$_\.h\"\n"); + } +} + +sub Const($$) +{ + my ($self, $const) = @_; + $self->register_constant($const->{NAME}, $const->{DTYPE}, $const->{VALUE}); +} + +sub register_constant($$$$) +{ + my ($self, $name, $type, $value) = @_; + + $self->{constants}->{$name} = [$type, $value]; +} + +sub EnumAndBitmapConsts($$$) +{ + my ($self, $name, $d) = @_; + + return unless (defined($d->{ELEMENTS})); + + foreach my $e (@{$d->{ELEMENTS}}) { + $e =~ /^([A-Za-z0-9_]+)/; + my $cname = $1; + + $self->register_constant($cname, $d, $cname); + } +} + +sub FromUnionToPythonFunction($$$$) +{ + my ($self, $mem_ctx, $type, $switch, $name) = @_; + + $self->pidl("PyObject *ret;"); + $self->pidl(""); + + $self->pidl("switch ($switch) {"); + $self->indent; + + foreach my $e (@{$type->{ELEMENTS}}) { + $self->pidl("$e->{CASE}:"); + + $self->indent; + + if ($e->{NAME}) { + $self->ConvertObjectToPython($mem_ctx, {}, $e, "$name->$e->{NAME}", "ret", "return NULL;"); + } else { + $self->pidl("ret = Py_None;"); + } + + $self->pidl("return ret;"); + $self->pidl(""); + + $self->deindent; + } + + $self->deindent; + $self->pidl("}"); + + $self->pidl("PyErr_SetString(PyExc_TypeError, \"unknown union level\");"); + $self->pidl("return NULL;"); +} + +sub FromPythonToUnionFunction($$$$$) +{ + my ($self, $type, $typename, $switch, $mem_ctx, $name) = @_; + + my $has_default = 0; + + $self->pidl("$typename *ret = talloc_zero($mem_ctx, $typename);"); + + $self->pidl("switch ($switch) {"); + $self->indent; + + foreach my $e (@{$type->{ELEMENTS}}) { + $self->pidl("$e->{CASE}:"); + if ($e->{CASE} eq "default") { $has_default = 1; } + $self->indent; + if ($e->{NAME}) { + $self->ConvertObjectFromPython({}, $mem_ctx, $e, $name, "ret->$e->{NAME}", "talloc_free(ret); return NULL;"); + } + $self->pidl("break;"); + $self->deindent; + $self->pidl(""); + } + + if (!$has_default) { + $self->pidl("default:"); + $self->indent; + $self->pidl("PyErr_SetString(PyExc_TypeError, \"invalid union level value\");"); + $self->pidl("talloc_free(ret);"); + $self->pidl("ret = NULL;"); + $self->deindent; + } + + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $self->pidl("return ret;"); +} + +sub PythonStruct($$$$$$) +{ + my ($self, $modulename, $prettyname, $name, $cname, $d) = @_; + + my $env = GenerateStructEnv($d, "object"); + + $self->pidl(""); + + my $getsetters = "NULL"; + + if ($#{$d->{ELEMENTS}} > -1) { + foreach my $e (@{$d->{ELEMENTS}}) { + my $varname = "object->$e->{NAME}"; + $self->pidl("static PyObject *py_$name\_get_$e->{NAME}(PyObject *obj, void *closure)"); + $self->pidl("{"); + $self->indent; + $self->pidl("$cname *object = py_talloc_get_ptr(obj);"); + $self->pidl("PyObject *py_$e->{NAME};"); + $self->ConvertObjectToPython("py_talloc_get_mem_ctx(obj)", $env, $e, $varname, "py_$e->{NAME}", "return NULL;"); + $self->pidl("return py_$e->{NAME};"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("static int py_$name\_set_$e->{NAME}(PyObject *py_obj, PyObject *value, void *closure)"); + $self->pidl("{"); + $self->indent; + $self->pidl("$cname *object = py_talloc_get_ptr(py_obj);"); + my $mem_ctx = "py_talloc_get_mem_ctx(py_obj)"; + my $l = $e->{LEVELS}[0]; + my $nl = GetNextLevel($e, $l); + if ($l->{TYPE} eq "POINTER" and + not ($nl->{TYPE} eq "ARRAY" and ($nl->{IS_FIXED} or is_charset_array($e, $nl))) and + not ($nl->{TYPE} eq "DATA" and Parse::Pidl::Typelist::scalar_is_reference($nl->{DATA_TYPE}))) { + $self->pidl("talloc_free($varname);"); + } + $self->ConvertObjectFromPython($env, $mem_ctx, $e, "value", $varname, "return -1;"); + $self->pidl("return 0;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + } + + $getsetters = "py_$name\_getsetters"; + $self->pidl("static PyGetSetDef ".$getsetters."[] = {"); + $self->indent; + foreach my $e (@{$d->{ELEMENTS}}) { + $self->pidl("{ discard_const_p(char, \"$e->{NAME}\"), py_$name\_get_$e->{NAME}, py_$name\_set_$e->{NAME} },"); + } + $self->pidl("{ NULL }"); + $self->deindent; + $self->pidl("};"); + $self->pidl(""); + } + + $self->pidl("static PyObject *py_$name\_new(PyTypeObject *self, PyObject *args, PyObject *kwargs)"); + $self->pidl("{"); + $self->indent; + $self->pidl("$cname *ret = talloc_zero(NULL, $cname);"); + $self->pidl("return py_talloc_import(&$name\_Type, ret);"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + my $py_methods = "NULL"; + + # If the struct is not public there ndr_pull/ndr_push functions will + # be static so not callable from here + if (has_property($d, "public")) { + $self->pidl("static PyObject *py_$name\_ndr_pack(PyObject *py_obj)"); + $self->pidl("{"); + $self->indent; + $self->pidl("$cname *object = py_talloc_get_ptr(py_obj);"); + $self->pidl("DATA_BLOB blob;"); + $self->pidl("enum ndr_err_code err;"); + $self->pidl("err = ndr_push_struct_blob(&blob, py_talloc_get_mem_ctx(py_obj), NULL, object, (ndr_push_flags_fn_t)ndr_push_$name);"); + $self->pidl("if (err != NDR_ERR_SUCCESS) {"); + $self->indent; + $self->pidl("PyErr_SetNdrError(err);"); + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $self->pidl("return PyString_FromStringAndSize((char *)blob.data, blob.length);"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("static PyObject *py_$name\_ndr_unpack(PyObject *py_obj, PyObject *args)"); + $self->pidl("{"); + $self->indent; + $self->pidl("$cname *object = py_talloc_get_ptr(py_obj);"); + $self->pidl("DATA_BLOB blob;"); + $self->pidl("enum ndr_err_code err;"); + $self->pidl("if (!PyArg_ParseTuple(args, \"s#:__ndr_unpack__\", &blob.data, &blob.length))"); + $self->pidl("\treturn NULL;"); + $self->pidl(""); + $self->pidl("err = ndr_pull_struct_blob_all(&blob, py_talloc_get_mem_ctx(py_obj), NULL, object, (ndr_pull_flags_fn_t)ndr_pull_$name);"); + $self->pidl("if (err != NDR_ERR_SUCCESS) {"); + $self->indent; + $self->pidl("PyErr_SetNdrError(err);"); + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $self->pidl("return Py_None;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $py_methods = "py_$name\_methods"; + $self->pidl("static PyMethodDef $py_methods\[] = {"); + $self->indent; + $self->pidl("{ \"__ndr_pack__\", (PyCFunction)py_$name\_ndr_pack, METH_NOARGS, \"S.pack() -> blob\\nNDR pack\" },"); + $self->pidl("{ \"__ndr_unpack__\", (PyCFunction)py_$name\_ndr_unpack, METH_VARARGS, \"S.unpack(blob) -> None\\nNDR unpack\" },"); + $self->pidl("{ NULL, NULL, 0, NULL }"); + $self->deindent; + $self->pidl("};"); + $self->pidl(""); + } + + $self->pidl_hdr("PyAPI_DATA(PyTypeObject) $name\_Type;\n"); + $self->pidl_hdr("#define $name\_Check(op) PyObject_TypeCheck(op, &$name\_Type)\n"); + $self->pidl_hdr("#define $name\_CheckExact(op) ((op)->ob_type == &$name\_Type)\n"); + $self->pidl_hdr("\n"); + my $docstring = ($self->DocString($d, $name) or "NULL"); + my $typeobject = "$name\_Type"; + $self->pidl("PyTypeObject $typeobject = {"); + $self->indent; + $self->pidl("PyObject_HEAD_INIT(NULL) 0,"); + $self->pidl(".tp_name = \"$modulename.$prettyname\","); + $self->pidl(".tp_basicsize = sizeof(py_talloc_Object),"); + $self->pidl(".tp_dealloc = py_talloc_dealloc,"); + $self->pidl(".tp_getset = $getsetters,"); + $self->pidl(".tp_repr = py_talloc_default_repr,"); + $self->pidl(".tp_doc = $docstring,"); + $self->pidl(".tp_methods = $py_methods,"); + $self->pidl(".tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,"); + $self->pidl(".tp_new = py_$name\_new,"); + $self->deindent; + $self->pidl("};"); + + $self->pidl(""); + + return "&$typeobject"; +} + +sub get_metadata_var($) +{ + my ($e) = @_; + sub get_var($) { my $x = shift; $x =~ s/\*//g; return $x; } + + if (has_property($e, "length_is")) { + return get_var($e->{PROPERTIES}->{length_is}); + } elsif (has_property($e, "size_is")) { + return get_var($e->{PROPERTIES}->{size_is}); + } + + return undef; +} + +sub find_metadata_args($) +{ + my ($fn) = @_; + my $metadata_args = { in => {}, out => {} }; + + # Determine arguments that are metadata for other arguments (size_is/length_is) + foreach my $e (@{$fn->{ELEMENTS}}) { + foreach my $dir (@{$e->{DIRECTION}}) { + my $main = get_metadata_var($e); + if ($main) { + $metadata_args->{$dir}->{$main} = $e->{NAME}; + } + } + } + + return $metadata_args; +} + +sub PythonFunctionUnpackOut($$$) +{ + my ($self, $fn, $fnname) = @_; + + my $outfnname = "unpack_$fnname\_args_out"; + my $signature = ""; + + my $metadata_args = find_metadata_args($fn); + + my $env = GenerateFunctionOutEnv($fn, "r->"); + my $result_size = 0; + + $self->pidl("static PyObject *$outfnname(struct $fn->{NAME} *r)"); + $self->pidl("{"); + $self->indent; + $self->pidl("PyObject *result = Py_None;"); + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless (grep(/out/,@{$e->{DIRECTION}})); + next if (($metadata_args->{in}->{$e->{NAME}} and grep(/in/, @{$e->{DIRECTION}})) or + ($metadata_args->{out}->{$e->{NAME}}) and grep(/out/, @{$e->{DIRECTION}})); + $self->pidl("PyObject *py_$e->{NAME};"); + $result_size++; + } + + if ($fn->{RETURN_TYPE}) { + $result_size++ unless ($fn->{RETURN_TYPE} eq "WERROR" or $fn->{RETURN_TYPE} eq "NTSTATUS"); + } + + my $i = 0; + + if ($result_size > 1) { + $self->pidl("result = PyTuple_New($result_size);"); + $signature .= "("; + } elsif ($result_size == 0) { + $signature .= "None"; + } + + foreach my $e (@{$fn->{ELEMENTS}}) { + next if ($metadata_args->{out}->{$e->{NAME}}); + my $py_name = "py_$e->{NAME}"; + if (grep(/out/,@{$e->{DIRECTION}})) { + $self->ConvertObjectToPython("r", $env, $e, "r->out.$e->{NAME}", $py_name, "return NULL;"); + if ($result_size > 1) { + $self->pidl("PyTuple_SetItem(result, $i, $py_name);"); + $i++; + $signature .= "$e->{NAME}, "; + } else { + $self->pidl("result = $py_name;"); + $signature .= $e->{NAME}; + } + } + } + + if (defined($fn->{RETURN_TYPE}) and $fn->{RETURN_TYPE} eq "NTSTATUS") { + $self->handle_ntstatus("r->out.result", "NULL", undef); + } elsif (defined($fn->{RETURN_TYPE}) and $fn->{RETURN_TYPE} eq "WERROR") { + $self->handle_werror("r->out.result", "NULL", undef); + } elsif (defined($fn->{RETURN_TYPE})) { + my $conv = $self->ConvertObjectToPythonData("r", $fn->{RETURN_TYPE}, "r->out.result"); + if ($result_size > 1) { + $self->pidl("PyTuple_SetItem(result, $i, $conv);"); + } else { + $self->pidl("result = $conv;"); + } + $signature .= "result"; + } + + if (substr($signature, -2) eq ", ") { + $signature = substr($signature, 0, -2); + } + if ($result_size > 1) { + $signature .= ")"; + } + + $self->pidl("return result;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + return ($outfnname, $signature); +} + +sub PythonFunctionPackIn($$$) +{ + my ($self, $fn, $fnname) = @_; + my $metadata_args = find_metadata_args($fn); + + my $infnname = "pack_$fnname\_args_in"; + + $self->pidl("static bool $infnname(PyObject *args, PyObject *kwargs, struct $fn->{NAME} *r)"); + $self->pidl("{"); + $self->indent; + my $args_format = ""; + my $args_string = ""; + my $args_names = ""; + my $signature = ""; + + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless (grep(/in/,@{$e->{DIRECTION}})); + next if (($metadata_args->{in}->{$e->{NAME}} and grep(/in/, @{$e->{DIRECTION}})) or + ($metadata_args->{out}->{$e->{NAME}}) and grep(/out/, @{$e->{DIRECTION}})); + $self->pidl("PyObject *py_$e->{NAME};"); + $args_format .= "O"; + $args_string .= ", &py_$e->{NAME}"; + $args_names .= "\"$e->{NAME}\", "; + $signature .= "$e->{NAME}, "; + } + if (substr($signature, -2) eq ", ") { + $signature = substr($signature, 0, -2); + } + $self->pidl("const char *kwnames[] = {"); + $self->indent; + $self->pidl($args_names . "NULL"); + $self->deindent; + $self->pidl("};"); + + $self->pidl(""); + $self->pidl("if (!PyArg_ParseTupleAndKeywords(args, kwargs, \"$args_format:$fn->{NAME}\", discard_const_p(char *, kwnames)$args_string)) {"); + $self->indent; + $self->pidl("return false;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + my $env = GenerateFunctionInEnv($fn, "r->"); + + my $fail = "return false;"; + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless (grep(/in/,@{$e->{DIRECTION}})); + if ($metadata_args->{in}->{$e->{NAME}}) { + my $py_var = "py_".$metadata_args->{in}->{$e->{NAME}}; + $self->pidl("PY_CHECK_TYPE(PyList, $py_var, $fail);"); + my $val = "PyList_Size($py_var)"; + if ($e->{LEVELS}[0]->{TYPE} eq "POINTER") { + $self->pidl("r->in.$e->{NAME} = talloc_ptrtype(r, r->in.$e->{NAME});"); + $self->pidl("*r->in.$e->{NAME} = $val;"); + } else { + $self->pidl("r->in.$e->{NAME} = $val;"); + } + } else { + $self->ConvertObjectFromPython($env, "r", $e, "py_$e->{NAME}", "r->in.$e->{NAME}", $fail); + } + } + $self->pidl("return true;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + return ($infnname, $signature); +} + +sub PythonFunction($$$) +{ + my ($self, $fn, $iface, $prettyname) = @_; + + my $fnname = "py_$fn->{NAME}"; + my $docstring = $self->DocString($fn, $fn->{NAME}); + + my ($insignature, $outsignature); + my ($infn, $outfn); + + if (has_property($fn, "todo")) { + unless ($docstring) { $docstring = "NULL"; } + $infn = "NULL"; + $outfn = "NULL"; + } else { + ($infn, $insignature) = $self->PythonFunctionPackIn($fn, $fnname); + ($outfn, $outsignature) = $self->PythonFunctionUnpackOut($fn, $fnname); + my $signature = "S.$prettyname($insignature) -> $outsignature"; + if ($docstring) { + $docstring = "\"$signature\\n\\n\"$docstring"; + } else { + $docstring = "\"$signature\""; + } + } + + return ($infn, $outfn, $docstring); +} + +sub handle_werror($$$$) +{ + my ($self, $var, $retval, $mem_ctx) = @_; + + $self->pidl("if (!W_ERROR_IS_OK($var)) {"); + $self->indent; + $self->pidl("PyErr_SetWERROR($var);"); + $self->pidl("talloc_free($mem_ctx);") if ($mem_ctx); + $self->pidl("return $retval;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +sub handle_ntstatus($$$$) +{ + my ($self, $var, $retval, $mem_ctx) = @_; + + $self->pidl("if (NT_STATUS_IS_ERR($var)) {"); + $self->indent; + $self->pidl("PyErr_SetNTSTATUS($var);"); + $self->pidl("talloc_free($mem_ctx);") if ($mem_ctx); + $self->pidl("return $retval;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +sub PythonType($$$$) +{ + my ($self, $modulename, $d, $interface, $basename) = @_; + + my $actual_ctype = $d; + if ($actual_ctype->{TYPE} eq "TYPEDEF") { + $actual_ctype = $actual_ctype->{DATA}; + } + + if ($actual_ctype->{TYPE} eq "STRUCT") { + my $typeobject; + my $fn_name = $d->{NAME}; + + $fn_name =~ s/^$interface->{NAME}_//; + $fn_name =~ s/^$basename\_//; + + + if ($d->{TYPE} eq "STRUCT") { + $typeobject = $self->PythonStruct($modulename, $fn_name, $d->{NAME}, mapTypeName($d), $d); + } else { + $typeobject = $self->PythonStruct($modulename, $fn_name, $d->{NAME}, mapTypeName($d), $d->{DATA}); + } + + $self->register_module_typeobject($fn_name, $typeobject); + } + + if ($d->{TYPE} eq "ENUM" or $d->{TYPE} eq "BITMAP") { + $self->EnumAndBitmapConsts($d->{NAME}, $d); + } + + if ($d->{TYPE} eq "TYPEDEF" and ($d->{DATA}->{TYPE} eq "ENUM" or $d->{DATA}->{TYPE} eq "BITMAP")) { + $self->EnumAndBitmapConsts($d->{NAME}, $d->{DATA}); + } + + if ($actual_ctype->{TYPE} eq "UNION" and defined($actual_ctype->{ELEMENTS})) { + $self->pidl("PyObject *py_import_$d->{NAME}(TALLOC_CTX *mem_ctx, int level, " .mapTypeName($d) . " *in)"); + $self->pidl("{"); + $self->indent; + $self->FromUnionToPythonFunction("mem_ctx", $actual_ctype, "level", "in") if ($actual_ctype->{TYPE} eq "UNION"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $self->pidl(mapTypeName($d) . " *py_export_$d->{NAME}(TALLOC_CTX *mem_ctx, int level, PyObject *in)"); + $self->pidl("{"); + $self->indent; + $self->FromPythonToUnionFunction($actual_ctype, mapTypeName($d), "level", "mem_ctx", "in") if ($actual_ctype->{TYPE} eq "UNION"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + } +} + +sub DocString($$$) +{ + my ($self, $d, $name) = @_; + if (has_property($d, "helpstring")) { + my $docstring = uc("py_doc_$name"); + $self->pidl("#define $docstring ".has_property($d, "helpstring")); + return $docstring; + } + + return undef; +} + +sub Interface($$$) +{ + my($self,$interface,$basename) = @_; + + $self->pidl_hdr("#ifndef _HEADER_PYTHON_$interface->{NAME}\n"); + $self->pidl_hdr("#define _HEADER_PYTHON_$interface->{NAME}\n\n"); + + $self->pidl_hdr("\n"); + + $self->Const($_) foreach (@{$interface->{CONSTS}}); + + foreach my $d (@{$interface->{TYPES}}) { + next if has_property($d, "nopython"); + + $self->PythonType($basename, $d, $interface, $basename); + } + + if (defined $interface->{PROPERTIES}->{uuid}) { + $self->pidl_hdr("PyAPI_DATA(PyTypeObject) $interface->{NAME}_InterfaceType;\n"); + $self->pidl(""); + + my @fns = (); + + foreach my $d (@{$interface->{FUNCTIONS}}) { + next if not defined($d->{OPNUM}); + next if has_property($d, "nopython"); + + my $prettyname = $d->{NAME}; + + $prettyname =~ s/^$interface->{NAME}_//; + $prettyname =~ s/^$basename\_//; + + my ($infn, $outfn, $fndocstring) = $self->PythonFunction($d, $interface->{NAME}, $prettyname); + + push (@fns, [$infn, $outfn, "dcerpc_$d->{NAME}", $prettyname, $fndocstring, $d->{OPNUM}]); + } + + $self->pidl("const struct PyNdrRpcMethodDef py_ndr_$interface->{NAME}\_methods[] = {"); + $self->pidl_hdr("extern const struct PyNdrRpcMethodDef py_ndr_$interface->{NAME}\_methods[];"); + $self->indent; + foreach my $d (@fns) { + my ($infn, $outfn, $callfn, $prettyname, $docstring, $opnum) = @$d; + $self->pidl("{ \"$prettyname\", $docstring, (dcerpc_call_fn)$callfn, (py_data_pack_fn)$infn, (py_data_unpack_fn)$outfn, $opnum, &ndr_table_$interface->{NAME} },"); + } + $self->pidl("{ NULL }"); + $self->deindent; + $self->pidl("};"); + $self->pidl(""); + + $self->pidl("static PyObject *interface_$interface->{NAME}_new(PyTypeObject *self, PyObject *args, PyObject *kwargs)"); + $self->pidl("{"); + $self->indent; + $self->pidl("dcerpc_InterfaceObject *ret;"); + $self->pidl("const char *binding_string;"); + $self->pidl("struct cli_credentials *credentials;"); + $self->pidl("struct loadparm_context *lp_ctx = NULL;"); + $self->pidl("PyObject *py_lp_ctx = Py_None, *py_credentials = Py_None, *py_basis = Py_None;"); + $self->pidl("TALLOC_CTX *mem_ctx = NULL;"); + $self->pidl("struct event_context *event_ctx;"); + $self->pidl("NTSTATUS status;"); + $self->pidl(""); + $self->pidl("const char *kwnames[] = {"); + $self->indent; + $self->pidl("\"binding\", \"lp_ctx\", \"credentials\", \"basis_connection\", NULL"); + $self->deindent; + $self->pidl("};"); + $self->pidl("extern struct loadparm_context *lp_from_py_object(PyObject *py_obj);"); + $self->pidl("extern struct cli_credentials *cli_credentials_from_py_object(PyObject *py_obj);"); + $self->pidl(""); + $self->pidl("if (!PyArg_ParseTupleAndKeywords(args, kwargs, \"s|OOO:$interface->{NAME}\", discard_const_p(char *, kwnames), &binding_string, &py_lp_ctx, &py_credentials, &py_basis)) {"); + $self->indent; + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $self->pidl("lp_ctx = lp_from_py_object(py_lp_ctx);"); + $self->pidl("if (lp_ctx == NULL) {"); + $self->indent; + $self->pidl("PyErr_SetString(PyExc_TypeError, \"Expected loadparm context\");"); + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("credentials = cli_credentials_from_py_object(py_credentials);"); + $self->pidl("if (credentials == NULL) {"); + $self->indent; + $self->pidl("PyErr_SetString(PyExc_TypeError, \"Expected credentials\");"); + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + + $self->pidl("ret = PyObject_New(dcerpc_InterfaceObject, &$interface->{NAME}_InterfaceType);"); + $self->pidl(""); + $self->pidl("event_ctx = event_context_init(mem_ctx);"); + $self->pidl(""); + + $self->pidl("if (py_basis != Py_None) {"); + $self->indent; + $self->pidl("struct dcerpc_pipe *base_pipe;"); + $self->pidl(""); + $self->pidl("if (!PyObject_TypeCheck(py_basis, &dcerpc_InterfaceType)) {"); + $self->indent; + $self->pidl("PyErr_SetString(PyExc_ValueError, \"basis_connection must be a DCE/RPC connection\");"); + $self->pidl("talloc_free(mem_ctx);"); + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $self->pidl("base_pipe = ((dcerpc_InterfaceObject *)py_basis)->pipe;"); + $self->pidl(""); + $self->pidl("status = dcerpc_secondary_context(base_pipe, &ret->pipe, &ndr_table_$interface->{NAME});"); + $self->deindent; + $self->pidl("} else {"); + $self->indent; + $self->pidl("status = dcerpc_pipe_connect(NULL, &ret->pipe, binding_string, "); + $self->pidl(" &ndr_table_$interface->{NAME}, credentials, event_ctx, lp_ctx);"); + $self->deindent; + $self->pidl("}"); + $self->handle_ntstatus("status", "NULL", "mem_ctx"); + + $self->pidl("ret->pipe->conn->flags |= DCERPC_NDR_REF_ALLOC;"); + + $self->pidl("return (PyObject *)ret;"); + $self->deindent; + $self->pidl("}"); + + $self->pidl(""); + + my $signature = +"\"$interface->{NAME}(binding, lp_ctx=None, credentials=None) -> connection\\n\" +\"\\n\" +\"binding should be a DCE/RPC binding string (for example: ncacn_ip_tcp:127.0.0.1)\\n\" +\"lp_ctx should be a path to a smb.conf file or a param.LoadParm object\\n\" +\"credentials should be a credentials.Credentials object.\\n\\n\""; + + my $docstring = $self->DocString($interface, $interface->{NAME}); + + if ($docstring) { + $docstring = "$signature$docstring"; + } else { + $docstring = $signature; + } + + $self->pidl("PyTypeObject $interface->{NAME}_InterfaceType = {"); + $self->indent; + $self->pidl("PyObject_HEAD_INIT(NULL) 0,"); + $self->pidl(".tp_name = \"$basename.$interface->{NAME}\","); + $self->pidl(".tp_basicsize = sizeof(dcerpc_InterfaceObject),"); + $self->pidl(".tp_base = &dcerpc_InterfaceType,"); + $self->pidl(".tp_doc = $docstring,"); + $self->pidl(".tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,"); + $self->pidl(".tp_new = interface_$interface->{NAME}_new,"); + $self->deindent; + $self->pidl("};"); + + $self->pidl(""); + + $self->register_module_typeobject($interface->{NAME}, "&$interface->{NAME}_InterfaceType"); + $self->register_module_readycode(["if (!PyInterface_AddNdrRpcMethods(&$interface->{NAME}_InterfaceType, py_ndr_$interface->{NAME}\_methods))", "\treturn;", ""]); + } + + $self->pidl_hdr("\n"); + $self->pidl_hdr("#endif /* _HEADER_NDR_$interface->{NAME} */\n"); +} + +sub register_module_method($$$$$) +{ + my ($self, $fn_name, $pyfn_name, $flags, $doc) = @_; + + push (@{$self->{module_methods}}, [$fn_name, $pyfn_name, $flags, $doc]) +} + +sub register_module_typeobject($$$) +{ + my ($self, $name, $py_name) = @_; + + $self->register_module_object($name, "(PyObject *)$py_name"); + + $self->check_ready_type($py_name); +} + +sub check_ready_type($$) +{ + my ($self, $py_name) = @_; + push (@{$self->{ready_types}}, $py_name) unless (grep(/^$py_name$/,@{$self->{ready_types}})); +} + +sub register_module_readycode($$) +{ + my ($self, $code) = @_; + + push (@{$self->{readycode}}, @$code); +} + +sub register_module_object($$$) +{ + my ($self, $name, $py_name) = @_; + + push (@{$self->{module_objects}}, [$name, $py_name]) +} + +sub assign($$$) +{ + my ($self, $dest, $src) = @_; + if ($dest =~ /^\&/) { + $self->pidl("memcpy($dest, $src, sizeof(" . get_value_of($dest) . "));"); + } else { + $self->pidl("$dest = $src;"); + } +} + +sub ConvertObjectFromPythonData($$$$$$) +{ + my ($self, $mem_ctx, $cvar, $ctype, $target, $fail) = @_; + + die("undef type for $cvar") unless(defined($ctype)); + + $ctype = resolveType($ctype); + + my $actual_ctype = $ctype; + if ($ctype->{TYPE} eq "TYPEDEF") { + $actual_ctype = $ctype->{DATA}; + } + + if ($actual_ctype->{TYPE} eq "ENUM" or $actual_ctype->{TYPE} eq "BITMAP" or + $actual_ctype->{TYPE} eq "SCALAR" and ( + expandAlias($actual_ctype->{NAME}) =~ /^(u?int[0-9]*|hyper|NTTIME|time_t|NTTIME_hyper|NTTIME_1sec|dlong|udlong|udlongr)$/)) { + $self->pidl("PY_CHECK_TYPE(PyInt, $cvar, $fail);"); + $self->pidl("$target = PyInt_AsLong($cvar);"); + return; + } + + if ($actual_ctype->{TYPE} eq "STRUCT") { + $self->pidl("PY_CHECK_TYPE($ctype->{NAME}, $cvar, $fail);"); + $self->assign($target, "py_talloc_get_ptr($cvar)"); + return; + } + + if ($actual_ctype->{TYPE} eq "SCALAR" and $actual_ctype->{NAME} eq "DATA_BLOB") { + $self->pidl("$target = data_blob_talloc($mem_ctx, PyString_AsString($cvar), PyString_Size($cvar));"); + return; + } + + if ($actual_ctype->{TYPE} eq "SCALAR" and + ($actual_ctype->{NAME} eq "string" or $actual_ctype->{NAME} eq "nbt_string" or $actual_ctype->{NAME} eq "nbt_name" or $actual_ctype->{NAME} eq "wrepl_nbt_name")) { + $self->pidl("$target = talloc_strdup($mem_ctx, PyString_AsString($cvar));"); + return; + } + + if ($actual_ctype->{TYPE} eq "SCALAR" and $actual_ctype->{NAME} eq "ipv4address") { + $self->pidl("$target = PyString_AsString($cvar);"); + return; + } + + + if ($actual_ctype->{TYPE} eq "SCALAR" and $actual_ctype->{NAME} eq "NTSTATUS") { + $self->pidl("$target = NT_STATUS(PyInt_AsLong($cvar));"); + return; + } + + if ($actual_ctype->{TYPE} eq "SCALAR" and $actual_ctype->{NAME} eq "WERROR") { + $self->pidl("$target = W_ERROR(PyInt_AsLong($cvar));"); + return; + } + + if ($actual_ctype->{TYPE} eq "SCALAR" and $actual_ctype->{NAME} eq "string_array") { + $self->pidl("$target = PyCObject_AsVoidPtr($cvar);"); + return; + } + + if ($actual_ctype->{TYPE} eq "SCALAR" and $actual_ctype->{NAME} eq "pointer") { + $self->assign($target, "PyCObject_AsVoidPtr($cvar)"); + return; + } + + die("unknown type ".mapTypeName($ctype) . ": $cvar"); + +} + +sub ConvertObjectFromPythonLevel($$$$$$$$) +{ + my ($self, $env, $mem_ctx, $py_var, $e, $l, $var_name, $fail) = @_; + my $nl = GetNextLevel($e, $l); + + if ($l->{TYPE} eq "POINTER") { + if ($nl->{TYPE} eq "DATA" and Parse::Pidl::Typelist::scalar_is_reference($nl->{DATA_TYPE})) { + $self->ConvertObjectFromPythonLevel($env, $mem_ctx, $py_var, $e, $nl, $var_name, $fail); + return; + } + if ($l->{POINTER_TYPE} ne "ref") { + $self->pidl("if ($py_var == Py_None) {"); + $self->indent; + $self->pidl("$var_name = NULL;"); + $self->deindent; + $self->pidl("} else {"); + $self->indent; + } + $self->pidl("$var_name = talloc_ptrtype($mem_ctx, $var_name);"); + $self->ConvertObjectFromPythonLevel($env, $mem_ctx, $py_var, $e, $nl, get_value_of($var_name), $fail); + if ($l->{POINTER_TYPE} ne "ref") { + $self->deindent; + $self->pidl("}"); + } + } elsif ($l->{TYPE} eq "ARRAY") { + my $pl = GetPrevLevel($e, $l); + if ($pl && $pl->{TYPE} eq "POINTER") { + $var_name = get_pointer_to($var_name); + } + + if (is_charset_array($e, $l)) { + $self->pidl("PY_CHECK_TYPE(PyUnicode, $py_var, $fail);"); + # FIXME: Use Unix charset setting rather than utf-8 + $self->pidl($var_name . " = PyString_AsString(PyUnicode_AsEncodedString($py_var, \"utf-8\", \"ignore\"));"); + } else { + my $counter = "$e->{NAME}_cntr_$l->{LEVEL_INDEX}"; + $self->pidl("PY_CHECK_TYPE(PyList, $py_var, $fail);"); + $self->pidl("{"); + $self->indent; + $self->pidl("int $counter;"); + if (!$l->{IS_FIXED}) { + $self->pidl("$var_name = talloc_array_ptrtype($mem_ctx, $var_name, PyList_Size($py_var));"); + } + $self->pidl("for ($counter = 0; $counter < PyList_Size($py_var); $counter++) {"); + $self->indent; + $self->ConvertObjectFromPythonLevel($env, $var_name, "PyList_GetItem($py_var, $counter)", $e, GetNextLevel($e, $l), $var_name."[$counter]", $fail); + $self->deindent; + $self->pidl("}"); + $self->deindent; + $self->pidl("}"); + } + } elsif ($l->{TYPE} eq "DATA") { + + if (not Parse::Pidl::Typelist::is_scalar($l->{DATA_TYPE})) { + $var_name = get_pointer_to($var_name); + } + $self->ConvertObjectFromPythonData($mem_ctx, $py_var, $l->{DATA_TYPE}, $var_name, $fail); + } elsif ($l->{TYPE} eq "SWITCH") { + $var_name = get_pointer_to($var_name); + my $switch = ParseExpr($l->{SWITCH_IS}, $env, $e); + $self->assign($var_name, "py_export_" . GetNextLevel($e, $l)->{DATA_TYPE} . "($mem_ctx, $switch, $py_var)"); + } elsif ($l->{TYPE} eq "SUBCONTEXT") { + $self->ConvertObjectFromPythonLevel($env, $mem_ctx, $py_var, $e, GetNextLevel($e, $l), $var_name, $fail); + } else { + die("unknown level type $l->{TYPE}"); + } +} + +sub ConvertObjectFromPython($$$$$$$) +{ + my ($self, $env, $mem_ctx, $ctype, $cvar, $target, $fail) = @_; + + $self->ConvertObjectFromPythonLevel($env, $mem_ctx, $cvar, $ctype, $ctype->{LEVELS}[0], $target, $fail); +} + +sub ConvertScalarToPython($$$) +{ + my ($self, $ctypename, $cvar) = @_; + + die("expected string for $cvar, not $ctypename") if (ref($ctypename) eq "HASH"); + + $ctypename = expandAlias($ctypename); + + if ($ctypename =~ /^(char|u?int[0-9]*|hyper|dlong|udlong|udlongr|time_t|NTTIME_hyper|NTTIME|NTTIME_1sec)$/) { + return "PyInt_FromLong($cvar)"; + } + + if ($ctypename eq "DATA_BLOB") { + return "PyString_FromStringAndSize((char *)($cvar).data, ($cvar).length)"; + } + + if ($ctypename eq "NTSTATUS") { + return "PyErr_FromNTSTATUS($cvar)"; + } + + if ($ctypename eq "WERROR") { + return "PyErr_FromWERROR($cvar)"; + } + + if (($ctypename eq "string" or $ctypename eq "nbt_string" or $ctypename eq "nbt_name" or $ctypename eq "wrepl_nbt_name")) { + return "PyString_FromString($cvar)"; + } + + # Not yet supported + if ($ctypename eq "string_array") { return "PyCObject_FromVoidPtr($cvar)"; } + if ($ctypename eq "ipv4address") { return "PyString_FromString($cvar)"; } + if ($ctypename eq "pointer") { + return "PyCObject_FromVoidPtr($cvar, talloc_free)"; + } + + die("Unknown scalar type $ctypename"); +} + +sub ConvertObjectToPythonData($$$$$) +{ + my ($self, $mem_ctx, $ctype, $cvar) = @_; + + die("undef type for $cvar") unless(defined($ctype)); + + $ctype = resolveType($ctype); + + my $actual_ctype = $ctype; + if ($ctype->{TYPE} eq "TYPEDEF") { + $actual_ctype = $ctype->{DATA}; + } + + if ($actual_ctype->{TYPE} eq "ENUM") { + return $self->ConvertScalarToPython(Parse::Pidl::Typelist::enum_type_fn($actual_ctype), $cvar); + } elsif ($actual_ctype->{TYPE} eq "BITMAP") { + return $self->ConvertScalarToPython(Parse::Pidl::Typelist::bitmap_type_fn($actual_ctype), $cvar); + } elsif ($actual_ctype->{TYPE} eq "SCALAR") { + return $self->ConvertScalarToPython($actual_ctype->{NAME}, $cvar); + } elsif ($actual_ctype->{TYPE} eq "STRUCT") { + return "py_talloc_import_ex(&$ctype->{NAME}_Type, $mem_ctx, $cvar)"; + } + + die("unknown type ".mapTypeName($ctype) . ": $cvar"); +} + +sub fail_on_null($$$) +{ + my ($self, $var, $fail) = @_; + $self->pidl("if ($var == NULL) {"); + $self->indent; + $self->pidl($fail); + $self->deindent; + $self->pidl("}"); +} + +sub ConvertObjectToPythonLevel($$$$$$) +{ + my ($self, $mem_ctx, $env, $e, $l, $var_name, $py_var, $fail) = @_; + my $nl = GetNextLevel($e, $l); + + if ($l->{TYPE} eq "POINTER") { + if ($nl->{TYPE} eq "DATA" and Parse::Pidl::Typelist::scalar_is_reference($nl->{DATA_TYPE})) { + $self->ConvertObjectToPythonLevel($var_name, $env, $e, $nl, $var_name, $py_var, $fail); + return; + } + if ($l->{POINTER_TYPE} ne "ref") { + $self->pidl("if ($var_name == NULL) {"); + $self->indent; + $self->pidl("$py_var = Py_None;"); + $self->deindent; + $self->pidl("} else {"); + $self->indent; + } + $self->ConvertObjectToPythonLevel($var_name, $env, $e, $nl, get_value_of($var_name), $py_var, $fail); + if ($l->{POINTER_TYPE} ne "ref") { + $self->deindent; + $self->pidl("}"); + } + } elsif ($l->{TYPE} eq "ARRAY") { + my $pl = GetPrevLevel($e, $l); + if ($pl && $pl->{TYPE} eq "POINTER") { + $var_name = get_pointer_to($var_name); + } + + if (is_charset_array($e, $l)) { + # FIXME: Use Unix charset setting rather than utf-8 + $self->pidl("$py_var = PyUnicode_Decode($var_name, strlen($var_name), \"utf-8\", \"ignore\");"); + } else { + die("No SIZE_IS for array $var_name") unless (defined($l->{SIZE_IS})); + my $length = $l->{SIZE_IS}; + if (defined($l->{LENGTH_IS})) { + $length = $l->{LENGTH_IS}; + } + + $length = ParseExpr($length, $env, $e); + $self->pidl("$py_var = PyList_New($length);"); + $self->fail_on_null($py_var, $fail); + $self->pidl("{"); + $self->indent; + my $counter = "$e->{NAME}_cntr_$l->{LEVEL_INDEX}"; + $self->pidl("int $counter;"); + $self->pidl("for ($counter = 0; $counter < $length; $counter++) {"); + $self->indent; + my $member_var = "py_$e->{NAME}_$l->{LEVEL_INDEX}"; + $self->pidl("PyObject *$member_var;"); + $self->ConvertObjectToPythonLevel($var_name, $env, $e, GetNextLevel($e, $l), $var_name."[$counter]", $member_var, $fail); + $self->pidl("PyList_SetItem($py_var, $counter, $member_var);"); + $self->deindent; + $self->pidl("}"); + $self->deindent; + $self->pidl("}"); + } + } elsif ($l->{TYPE} eq "SWITCH") { + $var_name = get_pointer_to($var_name); + my $switch = ParseExpr($l->{SWITCH_IS}, $env, $e); + $self->pidl("$py_var = py_import_" . GetNextLevel($e, $l)->{DATA_TYPE} . "($mem_ctx, $switch, $var_name);"); + $self->fail_on_null($py_var, $fail); + + } elsif ($l->{TYPE} eq "DATA") { + if (not Parse::Pidl::Typelist::is_scalar($l->{DATA_TYPE})) { + $var_name = get_pointer_to($var_name); + } + my $conv = $self->ConvertObjectToPythonData($mem_ctx, $l->{DATA_TYPE}, $var_name); + $self->pidl("$py_var = $conv;"); + } elsif ($l->{TYPE} eq "SUBCONTEXT") { + $self->ConvertObjectToPythonLevel($mem_ctx, $env, $e, GetNextLevel($e, $l), $var_name, $py_var, $fail); + } else { + die("Unknown level type $l->{TYPE} $var_name"); + } +} + +sub ConvertObjectToPython($$$$$$) +{ + my ($self, $mem_ctx, $env, $ctype, $cvar, $py_var, $fail) = @_; + + $self->ConvertObjectToPythonLevel($mem_ctx, $env, $ctype, $ctype->{LEVELS}[0], $cvar, $py_var, $fail); +} + +sub Parse($$$$$) +{ + my($self,$basename,$ndr,$ndr_hdr,$hdr) = @_; + + my $py_hdr = $hdr; + $py_hdr =~ s/ndr_([^\/]+)$/py_$1/g; + + $self->pidl_hdr("/* header auto-generated by pidl */\n\n"); + + $self->pidl(" +/* Python wrapper functions auto-generated by pidl */ +#include \"includes.h\" +#include <Python.h> +#include \"librpc/rpc/dcerpc.h\" +#include \"scripting/python/pytalloc.h\" +#include \"librpc/rpc/pyrpc.h\" +#include \"lib/events/events.h\" +#include \"$hdr\" +#include \"$ndr_hdr\" +#include \"$py_hdr\" + +"); + + foreach my $x (@$ndr) { + ($x->{TYPE} eq "IMPORT") && $self->Import(@{$x->{PATHS}}); + ($x->{TYPE} eq "INTERFACE") && $self->Interface($x, $basename); + } + + $self->pidl("static PyMethodDef $basename\_methods[] = {"); + $self->indent; + foreach (@{$self->{module_methods}}) { + my ($fn_name, $pyfn_name, $flags, $doc) = @$_; + $self->pidl("{ \"$fn_name\", (PyCFunction)$pyfn_name, $flags, $doc },"); + } + + $self->pidl("{ NULL, NULL, 0, NULL }"); + $self->deindent; + $self->pidl("};"); + + $self->pidl(""); + + $self->pidl("void init$basename(void)"); + $self->pidl("{"); + $self->indent; + $self->pidl("PyObject *m;"); + $self->pidl(""); + + foreach (@{$self->{ready_types}}) { + $self->pidl("if (PyType_Ready($_) < 0)"); + $self->pidl("\treturn;"); + } + + $self->pidl($_) foreach (@{$self->{readycode}}); + + $self->pidl(""); + + $self->pidl("m = Py_InitModule3(\"$basename\", $basename\_methods, \"$basename DCE/RPC\");"); + $self->pidl("if (m == NULL)"); + $self->pidl("\treturn;"); + $self->pidl(""); + foreach my $name (keys %{$self->{constants}}) { + my $py_obj; + my ($ctype, $cvar) = @{$self->{constants}->{$name}}; + if ($cvar =~ /^[0-9]+$/ or $cvar =~ /^0x[0-9a-fA-F]+$/) { + $py_obj = "PyInt_FromLong($cvar)"; + } elsif ($cvar =~ /^".*"$/) { + $py_obj = "PyString_FromString($cvar)"; + } else { + $py_obj = $self->ConvertObjectToPythonData("NULL", expandAlias($ctype), $cvar); + } + + $self->pidl("PyModule_AddObject(m, \"$name\", $py_obj);"); + } + + foreach (@{$self->{module_objects}}) { + my ($object_name, $c_name) = @$_; + $self->pidl("Py_INCREF($c_name);"); + $self->pidl("PyModule_AddObject(m, \"$object_name\", $c_name);"); + } + + $self->deindent; + $self->pidl("}"); + return ($self->{res_hdr}, $self->{res}); +} + +1; diff --git a/source/pidl/lib/Parse/Pidl/Samba4/SWIG.pm b/source/pidl/lib/Parse/Pidl/Samba4/SWIG.pm new file mode 100644 index 00000000000..62a0e6e7725 --- /dev/null +++ b/source/pidl/lib/Parse/Pidl/Samba4/SWIG.pm @@ -0,0 +1,177 @@ +################################################### +# Samba4 parser generator for swig wrappers +# Copyright tpot@samba.org 2004,2005 +# Copyright jelmer@samba.org 2006 +# released under the GNU GPL + +package Parse::Pidl::Samba4::SWIG; + +use vars qw($VERSION); +use Parse::Pidl::Samba4 qw(DeclLong); +use Parse::Pidl::Typelist qw(mapTypeName); +use Parse::Pidl::Util qw(has_property); +$VERSION = '0.01'; + +use strict; + +my $ret = ""; +my $tabs = ""; + +sub pidl($) +{ + my $p = shift; + $ret .= $tabs. $p . "\n"; +} + +sub indent() { $tabs.=" "; } +sub deindent() { $tabs = substr($tabs,0,-2); } + +sub IgnoreInterface($$) +{ + my ($basename,$if) = @_; + + foreach (@{$if->{TYPES}}) { + next unless (has_property($_, "public")); + pidl "\%types($_->{NAME});"; + } +} + +sub GenerateResultTypemap($) +{ + my $name = shift; + pidl "%typemap(in,numinputs=0) $name*result ($name tmp) {"; + indent; + pidl "\$1 = &tmp;"; + deindent; + pidl "}"; + pidl ""; + pidl "%typemap(argout) $name*result {"; + indent; + pidl "\$result = SWIG_NewPointerObj(*\$1, \$1_descriptor, 0);"; + deindent; + pidl "}"; +} + +sub ParseInterface($$) +{ + my ($basename,$if) = @_; + + pidl "\%inline {"; + pidl "typedef struct $if->{NAME} { struct dcerpc_pipe *pipe; } $if->{NAME};"; + pidl "}"; + pidl ""; + pidl "%talloctype($if->{NAME});"; + pidl ""; + pidl "\%extend $if->{NAME} {"; + indent(); + pidl "$if->{NAME} () {"; + indent; + pidl "return talloc(NULL, struct $if->{NAME});"; + deindent; + pidl "}"; + pidl ""; + pidl "NTSTATUS connect (const char *binding, struct cli_credentials *cred, struct event_context *event)"; + pidl "{"; + indent; + pidl "return dcerpc_pipe_connect(\$self, &\$self->pipe, binding, &ndr_table_$if->{NAME}, cred, event);"; + deindent; + pidl "}"; + pidl ""; + + foreach my $fn (@{$if->{FUNCTIONS}}) { + pidl "/* $fn->{NAME} */"; + my $args = ""; + foreach (@{$fn->{ELEMENTS}}) { + $args .= DeclLong($_) . ", "; + } + my $name = $fn->{NAME}; + $name =~ s/^$if->{NAME}_//g; + $name =~ s/^$basename\_//g; + $args .= "TALLOC_CTX *mem_ctx, " . mapTypeName($fn->{RETURN_TYPE}) . " *result"; + pidl "NTSTATUS $name($args)"; + pidl "{"; + indent; + pidl "struct $fn->{NAME} r;"; + pidl "NTSTATUS status;"; + pidl ""; + pidl "/* Fill r structure */"; + + foreach (@{$fn->{ELEMENTS}}) { + if (grep(/in/, @{$_->{DIRECTION}})) { + pidl "r.in.$_->{NAME} = $_->{NAME};"; + } + } + + pidl ""; + pidl "status = dcerpc_$fn->{NAME}(\$self->pipe, mem_ctx, &r);"; + pidl "if (NT_STATUS_IS_ERR(status)) {"; + indent; pidl "return status;"; deindent; + pidl "}"; + pidl ""; + pidl "/* Set out arguments */"; + foreach (@{$fn->{ELEMENTS}}) { + next unless (grep(/out/, @{$_->{DIRECTION}})); + + pidl ("/* FIXME: $_->{NAME} [out] argument is not a pointer */") if ($_->{LEVELS}[0]->{TYPE} ne "POINTER"); + + pidl "*$_->{NAME} = *r.out.$_->{NAME};"; + } + + if (defined($fn->{RETURN_TYPE})) { + pidl "*result = r.out.result;"; + } + pidl "return NT_STATUS_OK;"; + deindent; + pidl "}"; + pidl ""; + } + + deindent(); + pidl "};"; + pidl ""; + + foreach (@{$if->{TYPES}}) { + pidl "/* $_->{NAME} */"; + } + + pidl ""; +} + +sub Parse($$$$) +{ + my($ndr,$basename,$header,$gen_header) = @_; + + $ret = ""; + + pidl "/* This file is autogenerated by pidl. DO NOT EDIT */"; + + pidl "\%module $basename"; + + pidl ""; + + pidl "\%{"; + pidl "#include \"includes.h\""; + pidl "#include \"$header\""; + pidl "#include \"$gen_header\""; + pidl "%}"; + pidl "\%import \"../rpc/dcerpc.i\""; + pidl "\%import \"../../libcli/util/errors.i\""; + pidl "\%import \"../../lib/talloc/talloc.i\""; + pidl ""; + foreach (@$ndr) { + IgnoreInterface($basename, $_) if ($_->{TYPE} eq "INTERFACE"); + } + pidl ""; + + pidl ""; + + foreach (@$ndr) { + ParseInterface($basename, $_) if ($_->{TYPE} eq "INTERFACE"); + } + #FIXME: Foreach ref pointer, set NONNULL + #FIXME: Foreach unique/full pointer, set MAYBENULL + #FIXME: Foreach [out] parameter, set OUTPARAM + return $ret; +} + +1; diff --git a/source/pidl/lib/Parse/Pidl/Samba4/TDR.pm b/source/pidl/lib/Parse/Pidl/Samba4/TDR.pm new file mode 100644 index 00000000000..9b57fa5552a --- /dev/null +++ b/source/pidl/lib/Parse/Pidl/Samba4/TDR.pm @@ -0,0 +1,281 @@ +################################################### +# Trivial Parser Generator +# Copyright jelmer@samba.org 2005-2007 +# released under the GNU GPL + +package Parse::Pidl::Samba4::TDR; +use Parse::Pidl qw(fatal); +use Parse::Pidl::Util qw(has_property ParseExpr is_constant); +use Parse::Pidl::Samba4 qw(is_intree choose_header); + +use Exporter; +@ISA = qw(Exporter); +@EXPORT_OK = qw(ParserType $ret $ret_hdr); + +use vars qw($VERSION); +$VERSION = '0.01'; + +use strict; + +sub new($) { + my ($class) = shift; + my $self = { ret => "", ret_hdr => "", tabs => "" }; + bless($self, $class); +} + +sub indent($) { my $self = shift; $self->{tabs}.="\t"; } +sub deindent($) { my $self = shift; $self->{tabs} = substr($self->{tabs}, 1); } +sub pidl($$) { my $self = shift; $self->{ret} .= $self->{tabs}.(shift)."\n"; } +sub pidl_hdr($$) { my $self = shift; $self->{ret_hdr} .= (shift)."\n"; } +sub typearg($) { + my $t = shift; + return(", const char *name") if ($t eq "print"); + return(", TALLOC_CTX *mem_ctx") if ($t eq "pull"); + return(""); +} + +sub fn_declare($$$) +{ + my ($self, $p, $d) = @_; + if ($p) { + $self->pidl($d); $self->pidl_hdr("$d;"); + } else { + $self->pidl("static $d"); + } +} + +sub ContainsArray($) +{ + my $e = shift; + foreach (@{$e->{ELEMENTS}}) { + next if (has_property($_, "charset") and + scalar(@{$_->{ARRAY_LEN}}) == 1); + return 1 if (defined($_->{ARRAY_LEN}) and + scalar(@{$_->{ARRAY_LEN}}) > 0); + } + return 0; +} + +sub ParserElement($$$$) +{ + my ($self, $e,$t,$env) = @_; + my $switch = ""; + my $array = ""; + my $name = ""; + my $mem_ctx = "mem_ctx"; + + fatal($e,"Pointers not supported in TDR") if ($e->{POINTERS} > 0); + fatal($e,"size_is() not supported in TDR") if (has_property($e, "size_is")); + fatal($e,"length_is() not supported in TDR") if (has_property($e, "length_is")); + + if ($t eq "print") { + $name = ", \"$e->{NAME}\"$array"; + } + + if (has_property($e, "flag")) { + $self->pidl("{"); + $self->indent; + $self->pidl("uint32_t saved_flags = tdr->flags;"); + $self->pidl("tdr->flags |= $e->{PROPERTIES}->{flag};"); + } + + if (has_property($e, "charset")) { + fatal($e,"charset() on non-array element") unless (defined($e->{ARRAY_LEN}) and scalar(@{$e->{ARRAY_LEN}}) > 0); + + my $len = ParseExpr(@{$e->{ARRAY_LEN}}[0], $env, $e); + if ($len eq "*") { $len = "-1"; } + $name = ", mem_ctx" if ($t eq "pull"); + $self->pidl("TDR_CHECK(tdr_$t\_charset(tdr$name, &v->$e->{NAME}, $len, sizeof($e->{TYPE}_t), CH_$e->{PROPERTIES}->{charset}));"); + return; + } + + if (has_property($e, "switch_is")) { + $switch = ", " . ParseExpr($e->{PROPERTIES}->{switch_is}, $env, $e); + } + + if (defined($e->{ARRAY_LEN}) and scalar(@{$e->{ARRAY_LEN}}) > 0) { + my $len = ParseExpr($e->{ARRAY_LEN}[0], $env, $e); + + if ($t eq "pull" and not is_constant($len)) { + $self->pidl("TDR_ALLOC(mem_ctx, v->$e->{NAME}, $len);"); + $mem_ctx = "v->$e->{NAME}"; + } + + $self->pidl("for (i = 0; i < $len; i++) {"); + $self->indent; + $array = "[i]"; + } + + if ($t eq "pull") { + $name = ", $mem_ctx"; + } + + if (has_property($e, "value") && $t eq "push") { + $self->pidl("v->$e->{NAME} = ".ParseExpr($e->{PROPERTIES}->{value}, $env, $e).";"); + } + + $self->pidl("TDR_CHECK(tdr_$t\_$e->{TYPE}(tdr$name$switch, &v->$e->{NAME}$array));"); + + if ($array) { $self->deindent; $self->pidl("}"); } + + if (has_property($e, "flag")) { + $self->pidl("tdr->flags = saved_flags;"); + $self->deindent; + $self->pidl("}"); + } +} + +sub ParserStruct($$$$$) +{ + my ($self, $e,$t,$p) = @_; + + $self->fn_declare($p,"NTSTATUS tdr_$t\_$e->{NAME} (struct tdr_$t *tdr".typearg($t).", struct $e->{NAME} *v)"); + $self->pidl("{"); $self->indent; + $self->pidl("int i;") if (ContainsArray($e)); + + if ($t eq "print") { + $self->pidl("tdr->print(tdr, \"\%-25s: struct $e->{NAME}\", name);"); + $self->pidl("tdr->level++;"); + } + + my %env = map { $_->{NAME} => "v->$_->{NAME}" } @{$e->{ELEMENTS}}; + $env{"this"} = "v"; + $self->ParserElement($_, $t, \%env) foreach (@{$e->{ELEMENTS}}); + + if ($t eq "print") { + $self->pidl("tdr->level--;"); + } + + $self->pidl("return NT_STATUS_OK;"); + + $self->deindent; $self->pidl("}"); +} + +sub ParserUnion($$$$) +{ + my ($self, $e,$t,$p) = @_; + + $self->fn_declare($p,"NTSTATUS tdr_$t\_$e->{NAME}(struct tdr_$t *tdr".typearg($t).", int level, union $e->{NAME} *v)"); + $self->pidl("{"); $self->indent; + $self->pidl("int i;") if (ContainsArray($e)); + + if ($t eq "print") { + $self->pidl("tdr->print(tdr, \"\%-25s: union $e->{NAME}\", name);"); + $self->pidl("tdr->level++;"); + } + + $self->pidl("switch (level) {"); $self->indent; + foreach (@{$e->{ELEMENTS}}) { + if (has_property($_, "case")) { + $self->pidl("case " . $_->{PROPERTIES}->{case} . ":"); + } elsif (has_property($_, "default")) { + $self->pidl("default:"); + } + $self->indent; $self->ParserElement($_, $t, {}); $self->deindent; + $self->pidl("break;"); + } + $self->deindent; $self->pidl("}"); + + if ($t eq "print") { + $self->pidl("tdr->level--;"); + } + + $self->pidl("return NT_STATUS_OK;\n"); + $self->deindent; $self->pidl("}"); +} + +sub ParserBitmap($$$$) +{ + my ($self,$e,$t,$p) = @_; + return if ($p); + $self->pidl("#define tdr_$t\_$e->{NAME} tdr_$t\_" . Parse::Pidl::Typelist::bitmap_type_fn($e)); +} + +sub ParserEnum($$$$) +{ + my ($self,$e,$t,$p) = @_; + my $bt = Parse::Pidl::Typelist::enum_type_fn($e); + + $self->fn_declare($p, "NTSTATUS tdr_$t\_$e->{NAME} (struct tdr_$t *tdr".typearg($t).", enum $e->{NAME} *v)"); + $self->pidl("{"); + if ($t eq "pull") { + $self->pidl("\t$bt\_t r;"); + $self->pidl("\tTDR_CHECK(tdr_$t\_$bt(tdr, mem_ctx, \&r));"); + $self->pidl("\t*v = r;"); + } elsif ($t eq "push") { + $self->pidl("\tTDR_CHECK(tdr_$t\_$bt(tdr, ($bt\_t *)v));"); + } elsif ($t eq "print") { + $self->pidl("\t/* FIXME */"); + } + $self->pidl("\treturn NT_STATUS_OK;"); + $self->pidl("}"); +} + +sub ParserTypedef($$$$) +{ + my ($self, $e,$t,$p) = @_; + + $self->ParserType($e->{DATA},$t); +} + +sub ParserType($$$) +{ + my ($self, $e,$t) = @_; + + return if (has_property($e, "no$t")); + + my $handlers = { + STRUCT => \&ParserStruct, UNION => \&ParserUnion, + ENUM => \&ParserEnum, BITMAP => \&ParserBitmap, + TYPEDEF => \&ParserTypedef + }; + + $handlers->{$e->{TYPE}}->($self, $e, $t, has_property($e, "public")) + if (defined($handlers->{$e->{TYPE}})); + + $self->pidl(""); +} + +sub ParserInterface($$) +{ + my ($self,$x) = @_; + + $self->pidl_hdr("#ifndef __TDR_$x->{NAME}_HEADER__"); + $self->pidl_hdr("#define __TDR_$x->{NAME}_HEADER__"); + + foreach (@{$x->{DATA}}) { + $self->ParserType($_, "pull"); + $self->ParserType($_, "push"); + $self->ParserType($_, "print"); + } + + $self->pidl_hdr("#endif /* __TDR_$x->{NAME}_HEADER__ */"); +} + +sub Parser($$$$) +{ + my ($self,$idl,$hdrname,$baseheader) = @_; + $self->pidl("/* autogenerated by pidl */"); + if (is_intree()) { + $self->pidl("#include \"includes.h\""); + } else { + $self->pidl("#include <stdio.h>"); + $self->pidl("#include <stdbool.h>"); + $self->pidl("#include <stdlib.h>"); + $self->pidl("#include <stdint.h>"); + $self->pidl("#include <stdarg.h>"); + $self->pidl("#include <string.h>"); + $self->pidl("#include <core/ntstatus.h>"); + } + $self->pidl("#include \"$hdrname\""); + $self->pidl(""); + $self->pidl_hdr("/* autogenerated by pidl */"); + $self->pidl_hdr("#include \"$baseheader\""); + $self->pidl_hdr(choose_header("tdr/tdr.h", "tdr.h")); + $self->pidl_hdr(""); + + foreach (@$idl) { $self->ParserInterface($_) if ($_->{TYPE} eq "INTERFACE"); } + return ($self->{ret_hdr}, $self->{ret}); +} + +1; diff --git a/source/pidl/lib/Parse/Pidl/Samba4/Template.pm b/source/pidl/lib/Parse/Pidl/Samba4/Template.pm new file mode 100644 index 00000000000..bfdb76b6c26 --- /dev/null +++ b/source/pidl/lib/Parse/Pidl/Samba4/Template.pm @@ -0,0 +1,98 @@ +################################################### +# server template function generator +# Copyright tridge@samba.org 2003 +# released under the GNU GPL + +package Parse::Pidl::Samba4::Template; + +use vars qw($VERSION); +$VERSION = '0.01'; + +use strict; + +my($res); + +##################################################################### +# produce boilerplate code for a interface +sub Template($) +{ + my($interface) = shift; + my($data) = $interface->{DATA}; + my $name = $interface->{NAME}; + + $res .= +"/* + Unix SMB/CIFS implementation. + + endpoint server for the $name pipe + + Copyright (C) YOUR NAME HERE YEAR + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include \"includes.h\" +#include \"rpc_server/dcerpc_server.h\" +#include \"librpc/gen_ndr/ndr_$name.h\" +#include \"rpc_server/common/common.h\" + +"; + + foreach my $d (@{$data}) { + if ($d->{TYPE} eq "FUNCTION") { + my $fname = $d->{NAME}; + $res .= +" +/* + $fname +*/ +static $d->{RETURN_TYPE} dcesrv_$fname(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, + struct $fname *r) +{ +"; + + if ($d->{RETURN_TYPE} eq "void") { + $res .= "\tDCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);\n"; + } else { + $res .= "\tDCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);\n"; + } + + $res .= "} + +"; + } + } + + $res .= +" +/* include the generated boilerplate */ +#include \"librpc/gen_ndr/ndr_$name\_s.c\" +" +} + + +##################################################################### +# parse a parsed IDL structure back into an IDL file +sub Parse($) +{ + my($idl) = shift; + $res = ""; + foreach my $x (@{$idl}) { + ($x->{TYPE} eq "INTERFACE") && + Template($x); + } + return $res; +} + +1; diff --git a/source/pidl/lib/Parse/Pidl/Typelist.pm b/source/pidl/lib/Parse/Pidl/Typelist.pm new file mode 100644 index 00000000000..2c1f44b64b3 --- /dev/null +++ b/source/pidl/lib/Parse/Pidl/Typelist.pm @@ -0,0 +1,294 @@ +################################################### +# Samba4 parser generator for IDL structures +# Copyright jelmer@samba.org 2005 +# released under the GNU GPL + +package Parse::Pidl::Typelist; + +require Exporter; +@ISA = qw(Exporter); +@EXPORT_OK = qw(hasType getType resolveType mapTypeName scalar_is_reference expandAlias + mapScalarType addType typeIs is_scalar enum_type_fn + bitmap_type_fn mapType typeHasBody +); +use vars qw($VERSION); +$VERSION = '0.01'; + +use Parse::Pidl::Util qw(has_property); +use strict; + +my %types = (); + +my @reference_scalars = ( + "string", "string_array", "nbt_string", + "wrepl_nbt_name", "ipv4address" +); + +# a list of known scalar types +my %scalars = ( + "void" => "void", + "char" => "char", + "int8" => "int8_t", + "uint8" => "uint8_t", + "int16" => "int16_t", + "uint16" => "uint16_t", + "int32" => "int32_t", + "uint32" => "uint32_t", + "hyper" => "uint64_t", + "dlong" => "int64_t", + "udlong" => "uint64_t", + "udlongr" => "uint64_t", + "pointer" => "void*", + "DATA_BLOB" => "DATA_BLOB", + "string" => "const char *", + "string_array" => "const char **", + "time_t" => "time_t", + "NTTIME" => "NTTIME", + "NTTIME_1sec" => "NTTIME", + "NTTIME_hyper" => "NTTIME", + "WERROR" => "WERROR", + "NTSTATUS" => "NTSTATUS", + "COMRESULT" => "COMRESULT", + "nbt_string" => "const char *", + "wrepl_nbt_name"=> "struct nbt_name *", + "ipv4address" => "const char *", +); + +my %aliases = ( + "error_status_t" => "uint32", + "boolean8" => "uint8", + "boolean32" => "uint32", + "DWORD" => "uint32", + "uint" => "uint32", + "int" => "int32", + "WORD" => "uint16", + "char" => "uint8", + "long" => "int32", + "short" => "int16", + "HYPER_T" => "hyper", + "HRESULT" => "COMRESULT", +); + +sub expandAlias($) +{ + my $name = shift; + + return $aliases{$name} if defined($aliases{$name}); + + return $name; +} + +# map from a IDL type to a C header type +sub mapScalarType($) +{ + my $name = shift; + + # it's a bug when a type is not in the list + # of known scalars or has no mapping + return $scalars{$name} if defined($scalars{$name}); + + die("Unknown scalar type $name"); +} + +sub addType($) +{ + my $t = shift; + $types{$t->{NAME}} = $t; +} + +sub resolveType($) +{ + my ($ctype) = @_; + + if (not hasType($ctype)) { + # assume struct typedef + return { TYPE => "TYPEDEF", NAME => $ctype, DATA => { TYPE => "STRUCT" } }; + } else { + return getType($ctype); + } + + return $ctype; +} + +sub getType($) +{ + my $t = shift; + return ($t) if (ref($t) eq "HASH" and not defined($t->{NAME})); + return undef if not hasType($t); + return $types{$t->{NAME}} if (ref($t) eq "HASH"); + return $types{$t}; +} + +sub typeIs($$) +{ + my ($t,$tt) = @_; + + if (ref($t) eq "HASH") { + return 1 if ($t->{TYPE} eq $tt); + return 0; + } + return 1 if (hasType($t) and getType($t)->{TYPE} eq "TYPEDEF" and + getType($t)->{DATA}->{TYPE} eq $tt); + return 0; +} + +sub hasType($) +{ + my $t = shift; + if (ref($t) eq "HASH") { + return 1 if (not defined($t->{NAME})); + return 1 if (defined($types{$t->{NAME}}) and + $types{$t->{NAME}}->{TYPE} eq $t->{TYPE}); + return 0; + } + return 1 if defined($types{$t}); + return 0; +} + +sub is_scalar($) +{ + sub is_scalar($); + my $type = shift; + + return 1 if (ref($type) eq "HASH" and + ($type->{TYPE} eq "SCALAR" or $type->{TYPE} eq "ENUM" or + $type->{TYPE} eq "BITMAP")); + + if (my $dt = getType($type)) { + return is_scalar($dt->{DATA}) if ($dt->{TYPE} eq "TYPEDEF"); + return 1 if ($dt->{TYPE} eq "SCALAR" or $dt->{TYPE} eq "ENUM" or + $dt->{TYPE} eq "BITMAP"); + } + + return 0; +} + +sub scalar_is_reference($) +{ + my $name = shift; + + return 1 if (grep(/^$name$/, @reference_scalars)); + return 0; +} + +sub RegisterScalars() +{ + foreach (keys %scalars) { + addType({ + NAME => $_, + TYPE => "TYPEDEF", + DATA => { + TYPE => "SCALAR", + NAME => $_ + } + } + ); + } +} + +sub enum_type_fn($) +{ + my $enum = shift; + $enum->{TYPE} eq "ENUM" or die("not an enum"); + + # for typedef enum { } we need to check $enum->{PARENT} + if (has_property($enum, "enum8bit")) { + return "uint8"; + } elsif (has_property($enum, "enum16bit")) { + return "uint16"; + } elsif (has_property($enum, "v1_enum")) { + return "uint32"; + } elsif (has_property($enum->{PARENT}, "enum8bit")) { + return "uint8"; + } elsif (has_property($enum->{PARENT}, "enum16bit")) { + return "uint16"; + } elsif (has_property($enum->{PARENT}, "v1_enum")) { + return "uint32"; + } + return "uint16"; +} + +sub bitmap_type_fn($) +{ + my $bitmap = shift; + + $bitmap->{TYPE} eq "BITMAP" or die("not a bitmap"); + + if (has_property($bitmap, "bitmap8bit")) { + return "uint8"; + } elsif (has_property($bitmap, "bitmap16bit")) { + return "uint16"; + } elsif (has_property($bitmap, "bitmap64bit")) { + return "hyper"; + } + return "uint32"; +} + +sub typeHasBody($) +{ + sub typeHasBody($); + my ($e) = @_; + + if ($e->{TYPE} eq "TYPEDEF") { + return 0 unless(defined($e->{DATA})); + return typeHasBody($e->{DATA}); + } + + return defined($e->{ELEMENTS}); +} + +sub mapType($$) +{ + sub mapType($$); + my ($t, $n) = @_; + + return mapType($t->{DATA}, $n) if ($t->{TYPE} eq "TYPEDEF"); + return mapScalarType($n) if ($t->{TYPE} eq "SCALAR"); + return "enum $n" if ($t->{TYPE} eq "ENUM"); + return "struct $n" if ($t->{TYPE} eq "STRUCT"); + return "union $n" if ($t->{TYPE} eq "UNION"); + return mapScalarType(bitmap_type_fn($t)) if ($t->{TYPE} eq "BITMAP"); + die("Unknown type $t->{TYPE}"); +} + +sub mapTypeName($) +{ + my $t = shift; + return "void" unless defined($t); + my $dt; + $t = expandAlias($t); + + unless ($dt or ($dt = getType($t))) { + # Best guess + return "struct $t"; + } + + return mapType($dt, $dt->{NAME}); +} + +sub LoadIdl($) +{ + my ($idl) = @_; + + foreach my $x (@{$idl}) { + next if $x->{TYPE} ne "INTERFACE"; + + foreach my $y (@{$x->{DATA}}) { + addType($y) if ( + $y->{TYPE} eq "TYPEDEF" + or $y->{TYPE} eq "UNION" + or $y->{TYPE} eq "STRUCT" + or $y->{TYPE} eq "ENUM" + or $y->{TYPE} eq "BITMAP"); + } + } +} + +sub GenerateTypeLib() +{ + return Parse::Pidl::Util::MyDumper(\%types); +} + +RegisterScalars(); + +1; diff --git a/source/pidl/lib/Parse/Pidl/Util.pm b/source/pidl/lib/Parse/Pidl/Util.pm new file mode 100644 index 00000000000..54a227f9253 --- /dev/null +++ b/source/pidl/lib/Parse/Pidl/Util.pm @@ -0,0 +1,182 @@ +################################################### +# utility functions to support pidl +# Copyright tridge@samba.org 2000 +# released under the GNU GPL +package Parse::Pidl::Util; + +require Exporter; +@ISA = qw(Exporter); +@EXPORT = qw(has_property property_matches ParseExpr ParseExprExt is_constant make_str unmake_str print_uuid MyDumper); +use vars qw($VERSION); +$VERSION = '0.01'; + +use strict; + +use Parse::Pidl::Expr; +use Parse::Pidl qw(error); + +=head1 NAME + +Parse::Pidl::Util - Generic utility functions for pidl + +=head1 SYNOPSIS + +use Parse::Pidl::Util; + +=head1 DESCRIPTION + +Simple module that contains a couple of trivial helper functions +used throughout the various pidl modules. + +=head1 FUNCTIONS + +=over 4 + +=cut + +=item B<MyDumper> +a dumper wrapper to prevent dependence on the Data::Dumper module +unless we actually need it + +=cut + +sub MyDumper($) +{ + require Data::Dumper; + my $s = shift; + return Data::Dumper::Dumper($s); +} + +=item B<has_property> +see if a pidl property list contains a given property + +=cut +sub has_property($$) +{ + my($e, $p) = @_; + + return undef if (not defined($e->{PROPERTIES})); + + return $e->{PROPERTIES}->{$p}; +} + +=item B<property_matches> +see if a pidl property matches a value + +=cut +sub property_matches($$$) +{ + my($e,$p,$v) = @_; + + if (!defined has_property($e, $p)) { + return undef; + } + + if ($e->{PROPERTIES}->{$p} =~ /$v/) { + return 1; + } + + return undef; +} + +=item B<is_constant> +return 1 if the string is a C constant + +=cut +sub is_constant($) +{ + my $s = shift; + return 1 if ($s =~ /^\d+$/); + return 1 if ($s =~ /^0x[0-9A-Fa-f]+$/); + return 0; +} + +=item B<make_str> +return a "" quoted string, unless already quoted + +=cut +sub make_str($) +{ + my $str = shift; + if (substr($str, 0, 1) eq "\"") { + return $str; + } + return "\"$str\""; +} + +=item B<unmake_str> +unquote a "" quoted string + +=cut +sub unmake_str($) +{ + my $str = shift; + + $str =~ s/^\"(.*)\"$/$1/; + + return $str; +} + +=item B<print_uuid> +Print C representation of a UUID. + +=cut +sub print_uuid($) +{ + my ($uuid) = @_; + $uuid =~ s/"//g; + my ($time_low,$time_mid,$time_hi,$clock_seq,$node) = split /-/, $uuid; + return undef if not defined($node); + + my @clock_seq = $clock_seq =~ /(..)/g; + my @node = $node =~ /(..)/g; + + return "{0x$time_low,0x$time_mid,0x$time_hi," . + "{".join(',', map {"0x$_"} @clock_seq)."}," . + "{".join(',', map {"0x$_"} @node)."}}"; +} + +=item B<ParseExpr> +Interpret an IDL expression, substituting particular variables. + +=cut +sub ParseExpr($$$) +{ + my($expr, $varlist, $e) = @_; + + my $x = new Parse::Pidl::Expr(); + + return $x->Run($expr, sub { my $x = shift; error($e, $x); }, + # Lookup fn + sub { my $x = shift; + return($varlist->{$x}) if (defined($varlist->{$x})); + return $x; + }, + undef, undef); +} + +=item B<ParseExprExt> +Interpret an IDL expression, substituting particular variables. Can call +callbacks when pointers are being dereferenced or variables are being used. + +=cut +sub ParseExprExt($$$$$) +{ + my($expr, $varlist, $e, $deref, $use) = @_; + + my $x = new Parse::Pidl::Expr(); + + return $x->Run($expr, sub { my $x = shift; error($e, $x); }, + # Lookup fn + sub { my $x = shift; + return($varlist->{$x}) if (defined($varlist->{$x})); + return $x; + }, + $deref, $use); +} + +=back + +=cut + +1; diff --git a/source/pidl/lib/Parse/Pidl/Wireshark/Conformance.pm b/source/pidl/lib/Parse/Pidl/Wireshark/Conformance.pm new file mode 100644 index 00000000000..d003081ffdf --- /dev/null +++ b/source/pidl/lib/Parse/Pidl/Wireshark/Conformance.pm @@ -0,0 +1,439 @@ +################################################### +# parse an Wireshark conformance file +# Copyright jelmer@samba.org 2005 +# released under the GNU GPL + +=pod + +=head1 NAME + +Parse::Pidl::Wireshark::Conformance - Conformance file parser for Wireshark + +=head1 DESCRIPTION + +This module supports parsing Wireshark conformance files (*.cnf). + +=head1 FILE FORMAT + +Pidl needs additional data for Wireshark output. This data is read from +so-called conformance files. This section describes the format of these +files. + +Conformance files are simple text files with a single command on each line. +Empty lines and lines starting with a '#' character are ignored. +Arguments to commands are seperated by spaces. + +The following commands are currently supported: + +=over 4 + +=item I<TYPE> name dissector ft_type base_type mask valsstring alignment + +Register new data type with specified name, what dissector function to call +and what properties to give header fields for elements of this type. + +=item I<NOEMIT> type + +Suppress emitting a dissect_type function for the specified type + +=item I<PARAM_VALUE> type param + +Set parameter to specify to dissector function for given type. + +=item I<HF_FIELD> hf title filter ft_type base_type valsstring mask description + +Generate a custom header field with specified properties. + +=item I<HF_RENAME> old_hf_name new_hf_name + +Force the use of new_hf_name when the parser generator was going to +use old_hf_name. + +This can be used in conjunction with HF_FIELD in order to make more than +one element use the same filter name. + +=item I<ETT_FIELD> ett + +Register a custom ett field + +=item I<STRIP_PREFIX> prefix + +Remove the specified prefix from all function names (if present). + +=item I<PROTOCOL> longname shortname filtername + +Change the short-, long- and filter-name for the current interface in +Wireshark. + +=item I<FIELD_DESCRIPTION> field desc + +Change description for the specified header field. `field' is the hf name of the field. + +=item I<IMPORT> dissector code... + +Code to insert when generating the specified dissector. @HF@ and +@PARAM@ will be substituted. + +=item I<INCLUDE> filename + +Include conformance data from the specified filename in the dissector. + +=item I<TFS> hf_name "true string" "false string" + +Override the text shown when a bitmap boolean value is enabled or disabled. + +=item I<MANUAL> fn_name + +Force pidl to not generate a particular function but allow the user +to write a function manually. This can be used to remove the function +for only one level for a particular element rather than all the functions and +ett/hf variables for a particular element as the NOEMIT command does. + +=back + +=head1 EXAMPLE + + INFO_KEY OpenKey.Ke + +=cut + +package Parse::Pidl::Wireshark::Conformance; + +require Exporter; +use vars qw($VERSION); +$VERSION = '0.01'; + +@ISA = qw(Exporter); +@EXPORT_OK = qw(ReadConformance ReadConformanceFH valid_ft_type valid_base_type); + +use strict; + +use Parse::Pidl qw(fatal warning error); +use Parse::Pidl::Util qw(has_property); + +sub handle_type($$$$$$$$$$) +{ + my ($pos,$data,$name,$dissectorname,$ft_type,$base_type,$mask,$valsstring,$alignment) = @_; + + unless(defined($alignment)) { + error($pos, "incomplete TYPE command"); + return; + } + + unless ($dissectorname =~ /.*dissect_.*/) { + warning($pos, "dissector name does not contain `dissect'"); + } + + unless(valid_ft_type($ft_type)) { + warning($pos, "invalid FT_TYPE `$ft_type'"); + } + + unless (valid_base_type($base_type)) { + warning($pos, "invalid BASE_TYPE `$base_type'"); + } + + $dissectorname =~ s/^\"(.*)\"$/$1/g; + + if (not ($dissectorname =~ /;$/)) { + warning($pos, "missing semicolon"); + } + + $data->{types}->{$name} = { + NAME => $name, + POS => $pos, + USED => 0, + DISSECTOR_NAME => $dissectorname, + FT_TYPE => $ft_type, + BASE_TYPE => $base_type, + MASK => $mask, + VALSSTRING => $valsstring, + ALIGNMENT => $alignment + }; +} + +sub handle_tfs($$$$$) +{ + my ($pos,$data,$hf,$trues,$falses) = @_; + + unless(defined($falses)) { + error($pos, "incomplete TFS command"); + return; + } + + $data->{tfs}->{$hf} = { + TRUE_STRING => $trues, + FALSE_STRING => $falses + }; +} + +sub handle_hf_rename($$$$) +{ + my ($pos,$data,$old,$new) = @_; + + unless(defined($new)) { + warning($pos, "incomplete HF_RENAME command"); + return; + } + + $data->{hf_renames}->{$old} = { + OLDNAME => $old, + NEWNAME => $new, + POS => $pos, + USED => 0 + }; +} + +sub handle_param_value($$$$) +{ + my ($pos,$data,$dissector_name,$value) = @_; + + unless(defined($value)) { + error($pos, "incomplete PARAM_VALUE command"); + return; + } + + $data->{dissectorparams}->{$dissector_name} = { + DISSECTOR => $dissector_name, + PARAM => $value, + POS => $pos, + USED => 0 + }; +} + +sub valid_base_type($) +{ + my $t = shift; + return 0 unless($t =~ /^BASE_.*/); + return 1; +} + +sub valid_ft_type($) +{ + my $t = shift; + return 0 unless($t =~ /^FT_.*/); + return 1; +} + +sub handle_hf_field($$$$$$$$$$) +{ + my ($pos,$data,$index,$name,$filter,$ft_type,$base_type,$valsstring,$mask,$blurb) = @_; + + unless(defined($blurb)) { + error($pos, "incomplete HF_FIELD command"); + return; + } + + unless(valid_ft_type($ft_type)) { + warning($pos, "invalid FT_TYPE `$ft_type'"); + } + + unless(valid_base_type($base_type)) { + warning($pos, "invalid BASE_TYPE `$base_type'"); + } + + $data->{header_fields}->{$index} = { + INDEX => $index, + POS => $pos, + USED => 0, + NAME => $name, + FILTER => $filter, + FT_TYPE => $ft_type, + BASE_TYPE => $base_type, + VALSSTRING => $valsstring, + MASK => $mask, + BLURB => $blurb + }; +} + +sub handle_strip_prefix($$$) +{ + my ($pos,$data,$x) = @_; + + push (@{$data->{strip_prefixes}}, $x); +} + +sub handle_noemit($$$) +{ + my ($pos,$data,$type) = @_; + + if (defined($type)) { + $data->{noemit}->{$type} = 1; + } else { + $data->{noemit_dissector} = 1; + } +} + +sub handle_manual($$$) +{ + my ($pos,$data,$fn) = @_; + + unless(defined($fn)) { + warning($pos, "incomplete MANUAL command"); + return; + } + + $data->{manual}->{$fn} = 1; +} + +sub handle_protocol($$$$$$) +{ + my ($pos, $data, $name, $longname, $shortname, $filtername) = @_; + + $data->{protocols}->{$name} = { + LONGNAME => $longname, + SHORTNAME => $shortname, + FILTERNAME => $filtername + }; +} + +sub handle_fielddescription($$$$) +{ + my ($pos,$data,$field,$desc) = @_; + + unless(defined($desc)) { + warning($pos, "incomplete FIELD_DESCRIPTION command"); + return; + } + + $data->{fielddescription}->{$field} = { + DESCRIPTION => $desc, + POS => $pos, + USED => 0 + }; +} + +sub handle_import +{ + my $pos = shift @_; + my $data = shift @_; + my $dissectorname = shift @_; + + unless(defined($dissectorname)) { + error($pos, "no dissectorname specified"); + return; + } + + $data->{imports}->{$dissectorname} = { + NAME => $dissectorname, + DATA => join(' ', @_), + USED => 0, + POS => $pos + }; +} + +sub handle_ett_field +{ + my $pos = shift @_; + my $data = shift @_; + my $ett = shift @_; + + unless(defined($ett)) { + error($pos, "incomplete ETT_FIELD command"); + return; + } + + push (@{$data->{ett}}, $ett); +} + +sub handle_include +{ + my $pos = shift @_; + my $data = shift @_; + my $fn = shift @_; + + unless(defined($fn)) { + error($pos, "incomplete INCLUDE command"); + return; + } + + ReadConformance($fn, $data); +} + +my %field_handlers = ( + TYPE => \&handle_type, + NOEMIT => \&handle_noemit, + MANUAL => \&handle_manual, + PARAM_VALUE => \&handle_param_value, + HF_FIELD => \&handle_hf_field, + HF_RENAME => \&handle_hf_rename, + ETT_FIELD => \&handle_ett_field, + TFS => \&handle_tfs, + STRIP_PREFIX => \&handle_strip_prefix, + PROTOCOL => \&handle_protocol, + FIELD_DESCRIPTION => \&handle_fielddescription, + IMPORT => \&handle_import, + INCLUDE => \&handle_include +); + +sub ReadConformance($$) +{ + my ($f,$data) = @_; + my $ret; + + open(IN,"<$f") or return undef; + + $ret = ReadConformanceFH(*IN, $data, $f); + + close(IN); + + return $ret; +} + +sub ReadConformanceFH($$$) +{ + my ($fh,$data,$f) = @_; + + my $incodeblock = 0; + + my $ln = 0; + + foreach (<$fh>) { + $ln++; + next if (/^#.*$/); + next if (/^$/); + + s/[\r\n]//g; + + if ($_ eq "CODE START") { + $incodeblock = 1; + next; + } elsif ($incodeblock and $_ eq "CODE END") { + $incodeblock = 0; + next; + } elsif ($incodeblock) { + if (exists $data->{override}) { + $data->{override}.="$_\n"; + } else { + $data->{override} = "$_\n"; + } + next; + } + + my @fields = /([^ "]+|"[^"]+")/g; + + my $cmd = $fields[0]; + + shift @fields; + + my $pos = { FILE => $f, LINE => $ln }; + + next unless(defined($cmd)); + + if (not defined($field_handlers{$cmd})) { + warning($pos, "Unknown command `$cmd'"); + next; + } + + $field_handlers{$cmd}($pos, $data, @fields); + } + + if ($incodeblock) { + warning({ FILE => $f, LINE => $ln }, + "Expecting CODE END"); + return undef; + } + + return 1; +} + +1; diff --git a/source/pidl/lib/Parse/Pidl/Wireshark/NDR.pm b/source/pidl/lib/Parse/Pidl/Wireshark/NDR.pm new file mode 100644 index 00000000000..f1e3f734961 --- /dev/null +++ b/source/pidl/lib/Parse/Pidl/Wireshark/NDR.pm @@ -0,0 +1,1141 @@ +################################################## +# Samba4 NDR parser generator for IDL structures +# Copyright tridge@samba.org 2000-2003 +# Copyright tpot@samba.org 2001,2005 +# Copyright jelmer@samba.org 2004-2007 +# Portions based on idl2eth.c by Ronnie Sahlberg +# released under the GNU GPL + +=pod + +=head1 NAME + +Parse::Pidl::Wireshark::NDR - Parser generator for Wireshark + +=cut + +package Parse::Pidl::Wireshark::NDR; + +use Exporter; +@ISA = qw(Exporter); +@EXPORT_OK = qw(field2name %res PrintIdl StripPrefixes RegisterInterfaceHandoff register_hf_field CheckUsed ProcessImport ProcessInclude find_type DumpEttList DumpEttDeclaration DumpHfList DumpHfDeclaration DumpFunctionTable register_type register_ett); + +use strict; +use Parse::Pidl qw(error warning); +use Parse::Pidl::Typelist qw(getType); +use Parse::Pidl::Util qw(has_property property_matches make_str); +use Parse::Pidl::NDR qw(ContainsString GetNextLevel); +use Parse::Pidl::Dump qw(DumpType DumpFunction); +use Parse::Pidl::Wireshark::Conformance qw(ReadConformance); +use File::Basename; + +use vars qw($VERSION); +$VERSION = '0.01'; + +my %return_types = (); +my %dissector_used = (); + +my %ptrtype_mappings = ( + "unique" => "NDR_POINTER_UNIQUE", + "ref" => "NDR_POINTER_REF", + "ptr" => "NDR_POINTER_PTR" +); + +sub StripPrefixes($$) +{ + my ($s, $prefixes) = @_; + + foreach (@$prefixes) { + $s =~ s/^$_\_//g; + } + + return $s; +} + +# Convert a IDL structure field name (e.g access_mask) to a prettier +# string like 'Access Mask'. + +sub field2name($) +{ + my($field) = shift; + + $field =~ s/_/ /g; # Replace underscores with spaces + $field =~ s/(\w+)/\u\L$1/g; # Capitalise each word + + return $field; +} + +sub new($) +{ + my ($class) = @_; + my $self = {res => {hdr => "", def => "", code => ""}, tabs => "", cur_fn => undef, + hf_used => {}, ett => [], conformance => undef + + }; + bless($self, $class); +} + +sub pidl_fn_start($$) +{ + my ($self, $fn) = @_; + $self->{cur_fn} = $fn; +} +sub pidl_fn_end($$) +{ + my ($self, $fn) = @_; + die("Inconsistent state: $fn != $self->{cur_fn}") if ($fn ne $self->{cur_fn}); + $self->{cur_fn} = undef; +} + +sub pidl_code($$) +{ + my ($self, $d) = @_; + return if (defined($self->{cur_fn}) and defined($self->{conformance}->{manual}->{$self->{cur_fn}})); + + if ($d) { + $self->{res}->{code} .= $self->{tabs}; + $self->{res}->{code} .= $d; + } + $self->{res}->{code} .="\n"; +} + +sub pidl_hdr($$) { my ($self,$x) = @_; $self->{res}->{hdr} .= "$x\n"; } +sub pidl_def($$) { my ($self,$x) = @_; $self->{res}->{def} .= "$x\n"; } + +sub indent($) +{ + my ($self) = @_; + $self->{tabs} .= "\t"; +} + +sub deindent($) +{ + my ($self) = @_; + $self->{tabs} = substr($self->{tabs}, 0, -1); +} + +sub PrintIdl($$) +{ + my ($self, $idl) = @_; + + foreach (split /\n/, $idl) { + $self->pidl_code("/* IDL: $_ */"); + } + + $self->pidl_code(""); +} + +##################################################################### +# parse the interface definitions +sub Interface($$) +{ + my($self, $interface) = @_; + $self->Const($_,$interface->{NAME}) foreach (@{$interface->{CONSTS}}); + $self->Type($_, $_->{NAME}, $interface->{NAME}) foreach (@{$interface->{TYPES}}); + $self->Function($_,$interface->{NAME}) foreach (@{$interface->{FUNCTIONS}}); +} + +sub Enum($$$$) +{ + my ($self, $e,$name,$ifname) = @_; + my $valsstring = "$ifname\_$name\_vals"; + my $dissectorname = "$ifname\_dissect\_enum\_".StripPrefixes($name, $self->{conformance}->{strip_prefixes}); + + return if (defined($self->{conformance}->{noemit}->{StripPrefixes($name, $self->{conformance}->{strip_prefixes})})); + + foreach (@{$e->{ELEMENTS}}) { + if (/([^=]*)=(.*)/) { + $self->pidl_hdr("#define $1 ($2)"); + } + } + + $self->pidl_hdr("extern const value_string $valsstring\[];"); + $self->pidl_hdr("int $dissectorname(tvbuff_t *tvb _U_, int offset _U_, packet_info *pinfo _U_, proto_tree *tree _U_, guint8 *drep _U_, int hf_index _U_, guint32 *param _U_);"); + + $self->pidl_def("const value_string ".$valsstring."[] = {"); + foreach (@{$e->{ELEMENTS}}) { + next unless (/([^=]*)=(.*)/); + $self->pidl_def("\t{ $1, \"$1\" },"); + } + + $self->pidl_def("{ 0, NULL }"); + $self->pidl_def("};"); + + $self->pidl_fn_start($dissectorname); + $self->pidl_code("int"); + $self->pidl_code("$dissectorname(tvbuff_t *tvb _U_, int offset _U_, packet_info *pinfo _U_, proto_tree *tree _U_, guint8 *drep _U_, int hf_index _U_, guint32 *param _U_)"); + $self->pidl_code("{"); + $self->indent; + $self->pidl_code("g$e->{BASE_TYPE} parameter=0;"); + $self->pidl_code("if(param){"); + $self->indent; + $self->pidl_code("parameter=(g$e->{BASE_TYPE})*param;"); + $self->deindent; + $self->pidl_code("}"); + $self->pidl_code("offset = dissect_ndr_$e->{BASE_TYPE}(tvb, offset, pinfo, tree, drep, hf_index, ¶meter);"); + $self->pidl_code("if(param){"); + $self->indent; + $self->pidl_code("*param=(guint32)parameter;"); + $self->deindent; + $self->pidl_code("}"); + $self->pidl_code("return offset;"); + $self->deindent; + $self->pidl_code("}\n"); + $self->pidl_fn_end($dissectorname); + + my $enum_size = $e->{BASE_TYPE}; + $enum_size =~ s/uint//g; + $self->register_type($name, "offset = $dissectorname(tvb, offset, pinfo, tree, drep, \@HF\@, \@PARAM\@);", "FT_UINT$enum_size", "BASE_DEC", "0", "VALS($valsstring)", $enum_size / 8); +} + +sub Bitmap($$$$) +{ + my ($self,$e,$name,$ifname) = @_; + my $dissectorname = "$ifname\_dissect\_bitmap\_".StripPrefixes($name, $self->{conformance}->{strip_prefixes}); + + $self->register_ett("ett_$ifname\_$name"); + + $self->pidl_hdr("int $dissectorname(tvbuff_t *tvb _U_, int offset _U_, packet_info *pinfo _U_, proto_tree *tree _U_, guint8 *drep _U_, int hf_index _U_, guint32 param _U_);"); + + $self->pidl_fn_start($dissectorname); + $self->pidl_code("int"); + $self->pidl_code("$dissectorname(tvbuff_t *tvb _U_, int offset _U_, packet_info *pinfo _U_, proto_tree *parent_tree _U_, guint8 *drep _U_, int hf_index _U_, guint32 param _U_)"); + $self->pidl_code("{"); + $self->indent; + $self->pidl_code("proto_item *item = NULL;"); + $self->pidl_code("proto_tree *tree = NULL;"); + $self->pidl_code(""); + + $self->pidl_code("g$e->{BASE_TYPE} flags;"); + if ($e->{ALIGN} > 1) { + $self->pidl_code("ALIGN_TO_$e->{ALIGN}_BYTES;"); + } + + $self->pidl_code(""); + + $self->pidl_code("if (parent_tree) {"); + $self->indent; + $self->pidl_code("item = proto_tree_add_item(parent_tree, hf_index, tvb, offset, $e->{ALIGN}, TRUE);"); + $self->pidl_code("tree = proto_item_add_subtree(item,ett_$ifname\_$name);"); + $self->deindent; + $self->pidl_code("}\n"); + + $self->pidl_code("offset = dissect_ndr_$e->{BASE_TYPE}(tvb, offset, pinfo, NULL, drep, -1, &flags);"); + + $self->pidl_code("proto_item_append_text(item, \": \");\n"); + $self->pidl_code("if (!flags)"); + $self->pidl_code("\tproto_item_append_text(item, \"(No values set)\");\n"); + + foreach (@{$e->{ELEMENTS}}) { + next unless (/([^ ]*) (.*)/); + my ($en,$ev) = ($1,$2); + my $hf_bitname = "hf_$ifname\_$name\_$en"; + my $filtername = "$ifname\.$name\.$en"; + + $self->{hf_used}->{$hf_bitname} = 1; + + $self->register_hf_field($hf_bitname, field2name($en), $filtername, "FT_BOOLEAN", $e->{ALIGN} * 8, "TFS(&$name\_$en\_tfs)", $ev, ""); + + $self->pidl_def("static const true_false_string $name\_$en\_tfs = {"); + if (defined($self->{conformance}->{tfs}->{$hf_bitname})) { + $self->pidl_def(" $self->{conformance}->{tfs}->{$hf_bitname}->{TRUE_STRING},"); + $self->pidl_def(" $self->{conformance}->{tfs}->{$hf_bitname}->{FALSE_STRING},"); + $self->{conformance}->{tfs}->{$hf_bitname}->{USED} = 1; + } else { + $self->pidl_def(" \"$en is SET\","); + $self->pidl_def(" \"$en is NOT SET\","); + } + $self->pidl_def("};"); + + $self->pidl_code("proto_tree_add_boolean(tree, $hf_bitname, tvb, offset-$e->{ALIGN}, $e->{ALIGN}, flags);"); + $self->pidl_code("if (flags&$ev){"); + $self->pidl_code("\tproto_item_append_text(item, \"$en\");"); + $self->pidl_code("\tif (flags & (~$ev))"); + $self->pidl_code("\t\tproto_item_append_text(item, \", \");"); + $self->pidl_code("}"); + $self->pidl_code("flags&=(~$ev);"); + $self->pidl_code(""); + } + + $self->pidl_code("if (flags) {"); + $self->pidl_code("\tproto_item_append_text(item, \"Unknown bitmap value 0x%x\", flags);"); + $self->pidl_code("}\n"); + $self->pidl_code("return offset;"); + $self->deindent; + $self->pidl_code("}\n"); + $self->pidl_fn_end($dissectorname); + + my $size = $e->{BASE_TYPE}; + $size =~ s/uint//g; + $self->register_type($name, "offset = $dissectorname(tvb, offset, pinfo, tree, drep, \@HF\@, \@PARAM\@);", "FT_UINT$size", "BASE_HEX", "0", "NULL", $size/8); +} + +sub ElementLevel($$$$$$$) +{ + my ($self,$e,$l,$hf,$myname,$pn,$ifname) = @_; + + my $param = 0; + + if (defined($self->{conformance}->{dissectorparams}->{$myname})) { + $param = $self->{conformance}->{dissectorparams}->{$myname}->{PARAM}; + } + + if ($l->{TYPE} eq "POINTER") { + my $type; + if ($l->{LEVEL} eq "TOP") { + $type = "toplevel"; + } elsif ($l->{LEVEL} eq "EMBEDDED") { + $type = "embedded"; + } + $self->pidl_code("offset = dissect_ndr_$type\_pointer(tvb, offset, pinfo, tree, drep, $myname\_, $ptrtype_mappings{$l->{POINTER_TYPE}}, \"Pointer to ".field2name(StripPrefixes($e->{NAME}, $self->{conformance}->{strip_prefixes})) . " ($e->{TYPE})\",$hf);"); + } elsif ($l->{TYPE} eq "ARRAY") { + if ($l->{IS_INLINE}) { + error($e->{ORIGINAL}, "Inline arrays not supported"); + } elsif ($l->{IS_FIXED}) { + $self->pidl_code("int i;"); + $self->pidl_code("for (i = 0; i < $l->{SIZE_IS}; i++)"); + $self->pidl_code("\toffset = $myname\_(tvb, offset, pinfo, tree, drep);"); + } else { + my $type = ""; + $type .= "c" if ($l->{IS_CONFORMANT}); + $type .= "v" if ($l->{IS_VARYING}); + + unless ($l->{IS_ZERO_TERMINATED}) { + $self->pidl_code("offset = dissect_ndr_u" . $type . "array(tvb, offset, pinfo, tree, drep, $myname\_);"); + } else { + my $nl = GetNextLevel($e,$l); + $self->pidl_code("char *data;"); + $self->pidl_code(""); + $self->pidl_code("offset = dissect_ndr_$type" . "string(tvb, offset, pinfo, tree, drep, sizeof(g$nl->{DATA_TYPE}), $hf, FALSE, &data);"); + $self->pidl_code("proto_item_append_text(tree, \": %s\", data);"); + } + } + } elsif ($l->{TYPE} eq "DATA") { + if ($l->{DATA_TYPE} eq "string") { + my $bs = 2; # Byte size defaults to that of UCS2 + + + ($bs = 1) if (property_matches($e, "flag", ".*LIBNDR_FLAG_STR_ASCII.*")); + + if (property_matches($e, "flag", ".*LIBNDR_FLAG_STR_SIZE4.*") and property_matches($e, "flag", ".*LIBNDR_FLAG_STR_LEN4.*")) { + $self->pidl_code("char *data;\n"); + $self->pidl_code("offset = dissect_ndr_cvstring(tvb, offset, pinfo, tree, drep, $bs, $hf, FALSE, &data);"); + $self->pidl_code("proto_item_append_text(tree, \": %s\", data);"); + } elsif (property_matches($e, "flag", ".*LIBNDR_FLAG_STR_SIZE4.*")) { + $self->pidl_code("offset = dissect_ndr_vstring(tvb, offset, pinfo, tree, drep, $bs, $hf, FALSE, NULL);"); + } else { + warn("Unable to handle string with flags $e->{PROPERTIES}->{flag}"); + } + } else { + my $call; + + if ($self->{conformance}->{imports}->{$l->{DATA_TYPE}}) { + $call = $self->{conformance}->{imports}->{$l->{DATA_TYPE}}->{DATA}; + $self->{conformance}->{imports}->{$l->{DATA_TYPE}}->{USED} = 1; + } elsif (defined($self->{conformance}->{imports}->{"$pn.$e->{NAME}"})) { + $call = $self->{conformance}->{imports}->{"$pn.$e->{NAME}"}->{DATA}; + $self->{conformance}->{imports}->{"$pn.$e->{NAME}"}->{USED} = 1; + + } elsif (defined($self->{conformance}->{types}->{$l->{DATA_TYPE}})) { + $call= $self->{conformance}->{types}->{$l->{DATA_TYPE}}->{DISSECTOR_NAME}; + $self->{conformance}->{types}->{$l->{DATA_TYPE}}->{USED} = 1; + } else { + $self->pidl_code("offset = $ifname\_dissect_struct_" . $l->{DATA_TYPE} . "(tvb,offset,pinfo,tree,drep,$hf,$param);"); + + return; + } + + $call =~ s/\@HF\@/$hf/g; + $call =~ s/\@PARAM\@/$param/g; + $self->pidl_code($call); + } + } elsif ($_->{TYPE} eq "SUBCONTEXT") { + my $num_bits = ($l->{HEADER_SIZE}*8); + $self->pidl_code("guint$num_bits size;"); + $self->pidl_code("int start_offset = offset;"); + $self->pidl_code("tvbuff_t *subtvb;"); + $self->pidl_code("offset = dissect_ndr_uint$num_bits(tvb, offset, pinfo, tree, drep, $hf, &size);"); + $self->pidl_code("proto_tree_add_text(tree, tvb, start_offset, offset - start_offset + size, \"Subcontext size\");"); + + $self->pidl_code("subtvb = tvb_new_subset(tvb, offset, size, -1);"); + $self->pidl_code("$myname\_(subtvb, 0, pinfo, tree, drep);"); + } else { + die("Unknown type `$_->{TYPE}'"); + } +} + +sub Element($$$) +{ + my ($self,$e,$pn,$ifname) = @_; + + my $dissectorname = "$ifname\_dissect\_element\_".StripPrefixes($pn, $self->{conformance}->{strip_prefixes})."\_".StripPrefixes($e->{NAME}, $self->{conformance}->{strip_prefixes}); + + my $call_code = "offset = $dissectorname(tvb, offset, pinfo, tree, drep);"; + + my $type = $self->find_type($e->{TYPE}); + + if (not defined($type)) { + # default settings + $type = { + MASK => 0, + VALSSTRING => "NULL", + FT_TYPE => "FT_NONE", + BASE_TYPE => "BASE_HEX" + }; + } + + if (ContainsString($e)) { + $type = { + MASK => 0, + VALSSTRING => "NULL", + FT_TYPE => "FT_STRING", + BASE_TYPE => "BASE_DEC" + }; + } + + my $hf = $self->register_hf_field("hf_$ifname\_$pn\_$e->{NAME}", field2name($e->{NAME}), "$ifname.$pn.$e->{NAME}", $type->{FT_TYPE}, $type->{BASE_TYPE}, $type->{VALSSTRING}, $type->{MASK}, ""); + $self->{hf_used}->{$hf} = 1; + + my $eltname = StripPrefixes($pn, $self->{conformance}->{strip_prefixes}) . ".$e->{NAME}"; + if (defined($self->{conformance}->{noemit}->{$eltname})) { + return $call_code; + } + + my $add = ""; + + foreach (@{$e->{LEVELS}}) { + next if ($_->{TYPE} eq "SWITCH"); + $self->pidl_def("static int $dissectorname$add(tvbuff_t *tvb _U_, int offset _U_, packet_info *pinfo _U_, proto_tree *tree _U_, guint8 *drep _U_);"); + $self->pidl_fn_start("$dissectorname$add"); + $self->pidl_code("static int"); + $self->pidl_code("$dissectorname$add(tvbuff_t *tvb _U_, int offset _U_, packet_info *pinfo _U_, proto_tree *tree _U_, guint8 *drep _U_)"); + $self->pidl_code("{"); + $self->indent; + + $self->ElementLevel($e,$_,$hf,$dissectorname.$add,$pn,$ifname); + + $self->pidl_code(""); + $self->pidl_code("return offset;"); + $self->deindent; + $self->pidl_code("}\n"); + $self->pidl_fn_end("$dissectorname$add"); + $add.="_"; + last if ($_->{TYPE} eq "ARRAY" and $_->{IS_ZERO_TERMINATED}); + } + + return $call_code; +} + +sub Function($$$) +{ + my ($self, $fn,$ifname) = @_; + + my %dissectornames; + + foreach (@{$fn->{ELEMENTS}}) { + $dissectornames{$_->{NAME}} = $self->Element($_, $fn->{NAME}, $ifname) if not defined($dissectornames{$_->{NAME}}); + } + + my $fn_name = $_->{NAME}; + $fn_name =~ s/^${ifname}_//; + + $self->PrintIdl(DumpFunction($fn->{ORIGINAL})); + $self->pidl_fn_start("$ifname\_dissect\_$fn_name\_response"); + $self->pidl_code("static int"); + $self->pidl_code("$ifname\_dissect\_${fn_name}_response(tvbuff_t *tvb _U_, int offset _U_, packet_info *pinfo _U_, proto_tree *tree _U_, guint8 *drep _U_)"); + $self->pidl_code("{"); + $self->indent; + if ( not defined($fn->{RETURN_TYPE})) { + } elsif ($fn->{RETURN_TYPE} eq "NTSTATUS" or $fn->{RETURN_TYPE} eq "WERROR") + { + $self->pidl_code("guint32 status;\n"); + } elsif (my $type = getType($fn->{RETURN_TYPE})) { + if ($type->{DATA}->{TYPE} eq "ENUM") { + $self->pidl_code("g".Parse::Pidl::Typelist::enum_type_fn($type->{DATA}) . " status;\n"); + } elsif ($type->{DATA}->{TYPE} eq "SCALAR") { + $self->pidl_code("g$fn->{RETURN_TYPE} status;\n"); + } else { + error($fn, "return type `$fn->{RETURN_TYPE}' not yet supported"); + } + } else { + error($fn, "unknown return type `$fn->{RETURN_TYPE}'"); + } + + $self->pidl_code("pinfo->dcerpc_procedure_name=\"${fn_name}\";"); + foreach (@{$fn->{ELEMENTS}}) { + if (grep(/out/,@{$_->{DIRECTION}})) { + $self->pidl_code("$dissectornames{$_->{NAME}}"); + $self->pidl_code("offset = dissect_deferred_pointers(pinfo, tvb, offset, drep);"); + $self->pidl_code(""); + } + } + + if (not defined($fn->{RETURN_TYPE})) { + } elsif ($fn->{RETURN_TYPE} eq "NTSTATUS") { + $self->pidl_code("offset = dissect_ntstatus(tvb, offset, pinfo, tree, drep, hf\_$ifname\_status, &status);\n"); + $self->pidl_code("if (status != 0 && check_col(pinfo->cinfo, COL_INFO))"); + $self->pidl_code("\tcol_append_fstr(pinfo->cinfo, COL_INFO, \", Error: %s\", val_to_str(status, NT_errors, \"Unknown NT status 0x%08x\"));\n"); + $return_types{$ifname}->{"status"} = ["NTSTATUS", "NT Error"]; + } elsif ($fn->{RETURN_TYPE} eq "WERROR") { + $self->pidl_code("offset = dissect_ndr_uint32(tvb, offset, pinfo, tree, drep, hf\_$ifname\_werror, &status);\n"); + $self->pidl_code("if (status != 0 && check_col(pinfo->cinfo, COL_INFO))"); + $self->pidl_code("\tcol_append_fstr(pinfo->cinfo, COL_INFO, \", Error: %s\", val_to_str(status, WERR_errors, \"Unknown DOS error 0x%08x\"));\n"); + + $return_types{$ifname}->{"werror"} = ["WERROR", "Windows Error"]; + } elsif (my $type = getType($fn->{RETURN_TYPE})) { + if ($type->{DATA}->{TYPE} eq "ENUM") { + my $return_type = "g".Parse::Pidl::Typelist::enum_type_fn($type->{DATA}); + my $return_dissect = "dissect_ndr_" .Parse::Pidl::Typelist::enum_type_fn($type->{DATA}); + + $self->pidl_code("offset = $return_dissect(tvb, offset, pinfo, tree, drep, hf\_$ifname\_$fn->{RETURN_TYPE}_status, &status);"); + $self->pidl_code("if (status != 0 && check_col(pinfo->cinfo, COL_INFO))"); + $self->pidl_code("\tcol_append_fstr(pinfo->cinfo, COL_INFO, \", Status: %s\", val_to_str(status, $ifname\_$fn->{RETURN_TYPE}\_vals, \"Unknown " . $fn->{RETURN_TYPE} . " error 0x%08x\"));\n"); + $return_types{$ifname}->{$fn->{RETURN_TYPE}."_status"} = [$fn->{RETURN_TYPE}, $fn->{RETURN_TYPE}]; + } elsif ($type->{DATA}->{TYPE} eq "SCALAR") { + $self->pidl_code("offset = dissect_ndr_$fn->{RETURN_TYPE}(tvb, offset, pinfo, tree, drep, hf\_$ifname\_$fn->{RETURN_TYPE}_status, &status);"); + $self->pidl_code("if (status != 0 && check_col(pinfo->cinfo, COL_INFO))"); + $self->pidl_code("\tcol_append_fstr(pinfo->cinfo, COL_INFO, \", Status: %d\", status);\n"); + $return_types{$ifname}->{$fn->{RETURN_TYPE}."_status"} = [$fn->{RETURN_TYPE}, $fn->{RETURN_TYPE}]; + } + } + + $self->pidl_code("return offset;"); + $self->deindent; + $self->pidl_code("}\n"); + $self->pidl_fn_end("$ifname\_dissect\_$fn_name\_response"); + + $self->pidl_fn_start("$ifname\_dissect\_$fn_name\_request"); + $self->pidl_code("static int"); + $self->pidl_code("$ifname\_dissect\_${fn_name}_request(tvbuff_t *tvb _U_, int offset _U_, packet_info *pinfo _U_, proto_tree *tree _U_, guint8 *drep _U_)"); + $self->pidl_code("{"); + $self->indent; + $self->pidl_code("pinfo->dcerpc_procedure_name=\"${fn_name}\";"); + foreach (@{$fn->{ELEMENTS}}) { + if (grep(/in/,@{$_->{DIRECTION}})) { + $self->pidl_code("$dissectornames{$_->{NAME}}"); + $self->pidl_code("offset = dissect_deferred_pointers(pinfo, tvb, offset, drep);"); + } + + } + + $self->pidl_code("return offset;"); + $self->deindent; + $self->pidl_code("}\n"); + $self->pidl_fn_end("$ifname\_dissect\_$fn_name\_request"); +} + +sub Struct($$$$) +{ + my ($self,$e,$name,$ifname) = @_; + my $dissectorname = "$ifname\_dissect\_struct\_".StripPrefixes($name, $self->{conformance}->{strip_prefixes}); + + return if (defined($self->{conformance}->{noemit}->{StripPrefixes($name, $self->{conformance}->{strip_prefixes})})); + + $self->register_ett("ett_$ifname\_$name"); + + my $res = ""; + ($res.="\t".$self->Element($_, $name, $ifname)."\n\n") foreach (@{$e->{ELEMENTS}}); + + $self->pidl_hdr("int $dissectorname(tvbuff_t *tvb _U_, int offset _U_, packet_info *pinfo _U_, proto_tree *parent_tree _U_, guint8 *drep _U_, int hf_index _U_, guint32 param _U_);"); + + $self->pidl_fn_start($dissectorname); + $self->pidl_code("int"); + $self->pidl_code("$dissectorname(tvbuff_t *tvb _U_, int offset _U_, packet_info *pinfo _U_, proto_tree *parent_tree _U_, guint8 *drep _U_, int hf_index _U_, guint32 param _U_)"); + $self->pidl_code("{"); + $self->indent; + $self->pidl_code("proto_item *item = NULL;"); + $self->pidl_code("proto_tree *tree = NULL;"); + $self->pidl_code("int old_offset;"); + $self->pidl_code(""); + + if ($e->{ALIGN} > 1) { + $self->pidl_code("ALIGN_TO_$e->{ALIGN}_BYTES;"); + } + $self->pidl_code(""); + + $self->pidl_code("old_offset = offset;"); + $self->pidl_code(""); + $self->pidl_code("if (parent_tree) {"); + $self->indent; + $self->pidl_code("item = proto_tree_add_item(parent_tree, hf_index, tvb, offset, -1, TRUE);"); + $self->pidl_code("tree = proto_item_add_subtree(item, ett_$ifname\_$name);"); + $self->deindent; + $self->pidl_code("}"); + + $self->pidl_code("\n$res"); + + $self->pidl_code("proto_item_set_len(item, offset-old_offset);\n"); + $self->pidl_code("return offset;"); + $self->deindent; + $self->pidl_code("}\n"); + $self->pidl_fn_end($dissectorname); + + $self->register_type($name, "offset = $dissectorname(tvb,offset,pinfo,tree,drep,\@HF\@,\@PARAM\@);", "FT_NONE", "BASE_NONE", 0, "NULL", 0); +} + +sub Union($$$$) +{ + my ($self,$e,$name,$ifname) = @_; + + my $dissectorname = "$ifname\_dissect_".StripPrefixes($name, $self->{conformance}->{strip_prefixes}); + + return if (defined($self->{conformance}->{noemit}->{StripPrefixes($name, $self->{conformance}->{strip_prefixes})})); + + $self->register_ett("ett_$ifname\_$name"); + + my $res = ""; + foreach (@{$e->{ELEMENTS}}) { + $res.="\n\t\t$_->{CASE}:\n"; + if ($_->{TYPE} ne "EMPTY") { + $res.="\t\t\t".$self->Element($_, $name, $ifname)."\n"; + } + $res.="\t\tbreak;\n"; + } + + my $switch_type; + my $switch_dissect; + my $switch_dt = getType($e->{SWITCH_TYPE}); + if ($switch_dt->{DATA}->{TYPE} eq "ENUM") { + $switch_type = "g".Parse::Pidl::Typelist::enum_type_fn($switch_dt->{DATA}); + $switch_dissect = "dissect_ndr_" .Parse::Pidl::Typelist::enum_type_fn($switch_dt->{DATA}); + } elsif ($switch_dt->{DATA}->{TYPE} eq "SCALAR") { + $switch_type = "g$e->{SWITCH_TYPE}"; + $switch_dissect = "dissect_ndr_$e->{SWITCH_TYPE}"; + } + + $self->pidl_fn_start($dissectorname); + $self->pidl_code("static int"); + $self->pidl_code("$dissectorname(tvbuff_t *tvb _U_, int offset _U_, packet_info *pinfo _U_, proto_tree *parent_tree _U_, guint8 *drep _U_, int hf_index _U_, guint32 param _U_)"); + $self->pidl_code("{"); + $self->indent; + $self->pidl_code("proto_item *item = NULL;"); + $self->pidl_code("proto_tree *tree = NULL;"); + $self->pidl_code("int old_offset;"); + $self->pidl_code("$switch_type level;"); + $self->pidl_code(""); + + $self->pidl_code("old_offset = offset;"); + $self->pidl_code("if (parent_tree) {"); + $self->indent; + $self->pidl_code("item = proto_tree_add_text(parent_tree, tvb, offset, -1, \"$name\");"); + $self->pidl_code("tree = proto_item_add_subtree(item, ett_$ifname\_$name);"); + $self->deindent; + $self->pidl_code("}"); + + $self->pidl_code(""); + + $self->pidl_code("offset = $switch_dissect(tvb, offset, pinfo, tree, drep, hf_index, &level);"); + + if ($e->{ALIGN} > 1) { + $self->pidl_code("ALIGN_TO_$e->{ALIGN}_BYTES;"); + $self->pidl_code(""); + } + + + $self->pidl_code("switch(level) {$res\t}"); + $self->pidl_code("proto_item_set_len(item, offset-old_offset);\n"); + $self->pidl_code("return offset;"); + $self->deindent; + $self->pidl_code("}"); + $self->pidl_fn_end($dissectorname); + + $self->register_type($name, "offset = $dissectorname(tvb, offset, pinfo, tree, drep, \@HF\@, \@PARAM\@);", "FT_NONE", "BASE_NONE", 0, "NULL", 0); +} + +sub Const($$$) +{ + my ($self,$const,$ifname) = @_; + + if (!defined($const->{ARRAY_LEN}[0])) { + $self->pidl_hdr("#define $const->{NAME}\t( $const->{VALUE} )\n"); + } else { + $self->pidl_hdr("#define $const->{NAME}\t $const->{VALUE}\n"); + } +} + +sub Typedef($$$$) +{ + my ($self,$e,$name,$ifname) = @_; + + $self->Type($e->{DATA}, $name, $ifname); +} + +sub Type($$$$) +{ + my ($self, $e, $name, $ifname) = @_; + + $self->PrintIdl(DumpType($e->{ORIGINAL})); + + { + ENUM => \&Enum, + STRUCT => \&Struct, + UNION => \&Union, + BITMAP => \&Bitmap, + TYPEDEF => \&Typedef + }->{$e->{TYPE}}->($self, $e, $name, $ifname); +} + +sub RegisterInterface($$) +{ + my ($self, $x) = @_; + + $self->pidl_fn_start("proto_register_dcerpc_$x->{NAME}"); + $self->pidl_code("void proto_register_dcerpc_$x->{NAME}(void)"); + $self->pidl_code("{"); + $self->indent; + + $self->{res}->{code}.=$self->DumpHfList()."\n"; + $self->{res}->{code}.="\n".DumpEttList($self->{ett})."\n"; + + if (defined($x->{UUID})) { + # These can be changed to non-pidl_code names if the old dissectors + # in epan/dissctors are deleted. + + my $name = uc($x->{NAME}) . " (pidl)"; + my $short_name = uc($x->{NAME}); + my $filter_name = $x->{NAME}; + + if (has_property($x, "helpstring")) { + $name = $x->{PROPERTIES}->{helpstring}; + } + + if (defined($self->{conformance}->{protocols}->{$x->{NAME}})) { + $short_name = $self->{conformance}->{protocols}->{$x->{NAME}}->{SHORTNAME}; + $name = $self->{conformance}->{protocols}->{$x->{NAME}}->{LONGNAME}; + $filter_name = $self->{conformance}->{protocols}->{$x->{NAME}}->{FILTERNAME}; + } + + $self->pidl_code("proto_dcerpc_$x->{NAME} = proto_register_protocol(".make_str($name).", ".make_str($short_name).", ".make_str($filter_name).");"); + + $self->pidl_code("proto_register_field_array(proto_dcerpc_$x->{NAME}, hf, array_length (hf));"); + $self->pidl_code("proto_register_subtree_array(ett, array_length(ett));"); + } else { + $self->pidl_code("proto_dcerpc = proto_get_id_by_filter_name(\"dcerpc\");"); + $self->pidl_code("proto_register_field_array(proto_dcerpc, hf, array_length(hf));"); + $self->pidl_code("proto_register_subtree_array(ett, array_length(ett));"); + } + + $self->deindent; + $self->pidl_code("}\n"); + $self->pidl_fn_end("proto_register_dcerpc_$x->{NAME}"); +} + +sub RegisterInterfaceHandoff($$) +{ + my ($self,$x) = @_; + + if (defined($x->{UUID})) { + $self->pidl_fn_start("proto_reg_handoff_dcerpc_$x->{NAME}"); + $self->pidl_code("void proto_reg_handoff_dcerpc_$x->{NAME}(void)"); + $self->pidl_code("{"); + $self->indent; + $self->pidl_code("dcerpc_init_uuid(proto_dcerpc_$x->{NAME}, ett_dcerpc_$x->{NAME},"); + $self->pidl_code("\t&uuid_dcerpc_$x->{NAME}, ver_dcerpc_$x->{NAME},"); + $self->pidl_code("\t$x->{NAME}_dissectors, hf_$x->{NAME}_opnum);"); + $self->deindent; + $self->pidl_code("}"); + $self->pidl_fn_end("proto_reg_handoff_dcerpc_$x->{NAME}"); + + $self->{hf_used}->{"hf_$x->{NAME}_opnum"} = 1; + } +} + +sub ProcessInclude +{ + my $self = shift; + my @includes = @_; + foreach (@includes) { + $self->pidl_hdr("#include \"$_\""); + } + $self->pidl_hdr(""); +} + +sub ProcessImport +{ + my $self = shift; + my @imports = @_; + foreach (@imports) { + next if($_ eq "security"); + s/\.idl\"$//; + s/^\"//; + $self->pidl_hdr("#include \"packet-dcerpc-$_\.h\""); + } + $self->pidl_hdr(""); +} + +sub ProcessInterface($$) +{ + my ($self, $x) = @_; + + push(@{$self->{conformance}->{strip_prefixes}}, $x->{NAME}); + + my $define = "__PACKET_DCERPC_" . uc($_->{NAME}) . "_H"; + $self->pidl_hdr("#ifndef $define"); + $self->pidl_hdr("#define $define"); + $self->pidl_hdr(""); + + $self->pidl_def("static gint proto_dcerpc_$x->{NAME} = -1;"); + $self->register_ett("ett_dcerpc_$x->{NAME}"); + $self->register_hf_field("hf_$x->{NAME}_opnum", "Operation", "$x->{NAME}.opnum", "FT_UINT16", "BASE_DEC", "NULL", 0, ""); + + if (defined($x->{UUID})) { + my $if_uuid = $x->{UUID}; + + $self->pidl_def("/* Version information */\n\n"); + + $self->pidl_def("static e_uuid_t uuid_dcerpc_$x->{NAME} = {"); + $self->pidl_def("\t0x" . substr($if_uuid, 1, 8) + . ", 0x" . substr($if_uuid, 10, 4) + . ", 0x" . substr($if_uuid, 15, 4) . ","); + $self->pidl_def("\t{ 0x" . substr($if_uuid, 20, 2) + . ", 0x" . substr($if_uuid, 22, 2) + . ", 0x" . substr($if_uuid, 25, 2) + . ", 0x" . substr($if_uuid, 27, 2) + . ", 0x" . substr($if_uuid, 29, 2) + . ", 0x" . substr($if_uuid, 31, 2) + . ", 0x" . substr($if_uuid, 33, 2) + . ", 0x" . substr($if_uuid, 35, 2) . " }"); + $self->pidl_def("};"); + + my $maj = $x->{VERSION}; + $maj =~ s/\.(.*)$//g; + $self->pidl_def("static guint16 ver_dcerpc_$x->{NAME} = $maj;"); + $self->pidl_def(""); + } + + $return_types{$x->{NAME}} = {}; + + $self->Interface($x); + + $self->pidl_code("\n".DumpFunctionTable($x)); + + foreach (keys %{$return_types{$x->{NAME}}}) { + my ($type, $desc) = @{$return_types{$x->{NAME}}->{$_}}; + my $dt = $self->find_type($type); + $dt or die("Unable to find information about return type `$type'"); + $self->register_hf_field("hf_$x->{NAME}_$_", $desc, "$x->{NAME}.$_", $dt->{FT_TYPE}, "BASE_HEX", $dt->{VALSSTRING}, 0, ""); + $self->{hf_used}->{"hf_$x->{NAME}_$_"} = 1; + } + + $self->RegisterInterface($x); + $self->RegisterInterfaceHandoff($x); + + $self->pidl_hdr("#endif /* $define */"); +} + +sub find_type($$) +{ + my ($self, $n) = @_; + + return $self->{conformance}->{types}->{$n}; +} + +sub register_type($$$$$$$$) +{ + my ($self, $type,$call,$ft,$base,$mask,$vals,$length) = @_; + + return if (defined($self->{conformance}->{types}->{$type})); + + $self->{conformance}->{types}->{$type} = { + NAME => $type, + DISSECTOR_NAME => $call, + FT_TYPE => $ft, + BASE_TYPE => $base, + MASK => $mask, + VALSSTRING => $vals, + ALIGNMENT => $length + }; +} + +# Loads the default types +sub Initialize($$) +{ + my ($self, $cnf_file) = @_; + + $self->{conformance} = { + imports => {}, + header_fields=> {} + }; + + ReadConformance($cnf_file, $self->{conformance}) or print STDERR "warning: No conformance file `$cnf_file'\n"; + + foreach my $bytes (qw(1 2 4 8)) { + my $bits = $bytes * 8; + $self->register_type("uint$bits", "offset = PIDL_dissect_uint$bits(tvb, offset, pinfo, tree, drep, \@HF\@, \@PARAM\@);", "FT_UINT$bits", "BASE_DEC", 0, "NULL", $bytes); + $self->register_type("int$bits", "offset = PIDL_dissect_uint$bits(tvb, offset, pinfo, tree, drep, \@HF\@, \@PARAM\@);", "FT_INT$bits", "BASE_DEC", 0, "NULL", $bytes); + } + + $self->register_type("udlong", "offset = dissect_ndr_duint32(tvb, offset, pinfo, tree, drep, \@HF\@, NULL);", "FT_UINT64", "BASE_DEC", 0, "NULL", 4); + $self->register_type("bool8", "offset = PIDL_dissect_uint8(tvb, offset, pinfo, tree, drep, \@HF\@, \@PARAM\@);","FT_INT8", "BASE_DEC", 0, "NULL", 1); + $self->register_type("char", "offset = PIDL_dissect_uint8(tvb, offset, pinfo, tree, drep, \@HF\@, \@PARAM\@);","FT_INT8", "BASE_DEC", 0, "NULL", 1); + $self->register_type("long", "offset = PIDL_dissect_uint32(tvb, offset, pinfo, tree, drep, \@HF\@, \@PARAM\@);","FT_INT32", "BASE_DEC", 0, "NULL", 4); + $self->register_type("dlong", "offset = dissect_ndr_duint32(tvb, offset, pinfo, tree, drep, \@HF\@, NULL);","FT_INT64", "BASE_DEC", 0, "NULL", 8); + $self->register_type("GUID", "offset = dissect_ndr_uuid_t(tvb, offset, pinfo, tree, drep, \@HF\@, NULL);","FT_GUID", "BASE_NONE", 0, "NULL", 4); + $self->register_type("policy_handle", "offset = PIDL_dissect_policy_hnd(tvb, offset, pinfo, tree, drep, \@HF\@, \@PARAM\@);","FT_BYTES", "BASE_NONE", 0, "NULL", 4); + $self->register_type("NTTIME", "offset = dissect_ndr_nt_NTTIME(tvb, offset, pinfo, tree, drep, \@HF\@);","FT_ABSOLUTE_TIME", "BASE_NONE", 0, "NULL", 4); + $self->register_type("NTTIME_hyper", "offset = dissect_ndr_nt_NTTIME(tvb, offset, pinfo, tree, drep, \@HF\@);","FT_ABSOLUTE_TIME", "BASE_NONE", 0, "NULL", 4); + $self->register_type("time_t", "offset = dissect_ndr_time_t(tvb, offset, pinfo,tree, drep, \@HF\@, NULL);","FT_ABSOLUTE_TIME", "BASE_DEC", 0, "NULL", 4); + $self->register_type("NTTIME_1sec", "offset = dissect_ndr_nt_NTTIME(tvb, offset, pinfo, tree, drep, \@HF\@);", "FT_ABSOLUTE_TIME", "BASE_NONE", 0, "NULL", 4); + $self->register_type("SID", " + dcerpc_info *di = (dcerpc_info *)pinfo->private_data; + + di->hf_index = \@HF\@; + + offset = dissect_ndr_nt_SID_with_options(tvb, offset, pinfo, tree, drep, param); + ","FT_STRING", "BASE_DEC", 0, "NULL", 4); + $self->register_type("WERROR", + "offset = PIDL_dissect_uint32(tvb, offset, pinfo, tree, drep, \@HF\@, \@PARAM\@);","FT_UINT32", "BASE_DEC", 0, "VALS(WERR_errors)", 4); + $self->register_type("NTSTATUS", + "offset = PIDL_dissect_uint32(tvb, offset, pinfo, tree, drep, \@HF\@, \@PARAM\@);","FT_UINT32", "BASE_DEC", 0, "VALS(NT_errors)", 4); + +} + +##################################################################### +# Generate Wireshark parser and header code +sub Parse($$$$$) +{ + my($self,$ndr,$idl_file,$h_filename,$cnf_file) = @_; + + $self->Initialize($cnf_file); + + return (undef, undef) if defined($self->{conformance}->{noemit_dissector}); + + my $notice = +"/* DO NOT EDIT + This filter was automatically generated + from $idl_file and $cnf_file. + + Pidl is a perl based IDL compiler for DCE/RPC idl files. + It is maintained by the Samba team, not the Wireshark team. + Instructions on how to download and install Pidl can be + found at http://wiki.wireshark.org/Pidl +*/ + +"; + + $self->pidl_hdr($notice); + + $self->{res}->{headers} = "\n"; + $self->{res}->{headers} .= "#ifdef HAVE_CONFIG_H\n"; + $self->{res}->{headers} .= "#include \"config.h\"\n"; + $self->{res}->{headers} .= "#endif\n\n"; + + $self->{res}->{headers} .= "#ifdef _MSC_VER\n"; + $self->{res}->{headers} .= "#pragma warning(disable:4005)\n"; + $self->{res}->{headers} .= "#pragma warning(disable:4013)\n"; + $self->{res}->{headers} .= "#pragma warning(disable:4018)\n"; + $self->{res}->{headers} .= "#pragma warning(disable:4101)\n"; + $self->{res}->{headers} .= "#endif\n\n"; + + $self->{res}->{headers} .= "#include <glib.h>\n"; + $self->{res}->{headers} .= "#include <string.h>\n"; + $self->{res}->{headers} .= "#include <epan/packet.h>\n\n"; + + $self->{res}->{headers} .= "#include \"packet-dcerpc.h\"\n"; + $self->{res}->{headers} .= "#include \"packet-dcerpc-nt.h\"\n"; + $self->{res}->{headers} .= "#include \"packet-windows-common.h\"\n"; + + my $h_basename = basename($h_filename); + + $self->{res}->{headers} .= "#include \"$h_basename\"\n"; + $self->pidl_code(""); + + if (defined($self->{conformance}->{ett})) { + register_ett($self,$_) foreach(@{$self->{conformance}->{ett}}) + } + + # Wireshark protocol registration + + foreach (@$ndr) { + $self->ProcessInterface($_) if ($_->{TYPE} eq "INTERFACE"); + $self->ProcessImport(@{$_->{PATHS}}) if ($_->{TYPE} eq "IMPORT"); + $self->ProcessInclude(@{$_->{PATHS}}) if ($_->{TYPE} eq "INCLUDE"); + } + + $self->{res}->{ett} = DumpEttDeclaration($self->{ett}); + $self->{res}->{hf} = $self->DumpHfDeclaration(); + + my $parser = $notice; + $parser.= $self->{res}->{headers}; + $parser.=$self->{res}->{ett}; + $parser.=$self->{res}->{hf}; + $parser.=$self->{res}->{def}; + if (exists ($self->{conformance}->{override})) { + $parser.=$self->{conformance}->{override}; + } + $parser.=$self->{res}->{code}; + + my $header = "/* autogenerated by pidl */\n\n"; + $header.=$self->{res}->{hdr}; + + $self->CheckUsed($self->{conformance}); + + return ($parser,$header); +} + +############################################################################### +# ETT +############################################################################### + +sub register_ett($$) +{ + my ($self, $name) = @_; + + push (@{$self->{ett}}, $name); +} + +sub DumpEttList +{ + my ($ett) = @_; + my $res = "\tstatic gint *ett[] = {\n"; + foreach (@$ett) { + $res .= "\t\t&$_,\n"; + } + + return "$res\t};\n"; +} + +sub DumpEttDeclaration +{ + my ($ett) = @_; + my $res = "\n/* Ett declarations */\n"; + foreach (@$ett) { + $res .= "static gint $_ = -1;\n"; + } + + return "$res\n"; +} + +############################################################################### +# HF +############################################################################### + +sub register_hf_field($$$$$$$$$) +{ + my ($self,$index,$name,$filter_name,$ft_type,$base_type,$valsstring,$mask,$blurb) = @_; + + if (defined ($self->{conformance}->{hf_renames}->{$index})) { + $self->{conformance}->{hf_renames}->{$index}->{USED} = 1; + return $self->{conformance}->{hf_renames}->{$index}->{NEWNAME}; + } + + $self->{conformance}->{header_fields}->{$index} = { + INDEX => $index, + NAME => $name, + FILTER => $filter_name, + FT_TYPE => $ft_type, + BASE_TYPE => $base_type, + VALSSTRING => $valsstring, + MASK => $mask, + BLURB => $blurb + }; + + if ((not defined($blurb) or $blurb eq "") and + defined($self->{conformance}->{fielddescription}->{$index})) { + $self->{conformance}->{header_fields}->{$index}->{BLURB} = + $self->{conformance}->{fielddescription}->{$index}->{DESCRIPTION}; + $self->{conformance}->{fielddescription}->{$index}->{USED} = 1; + } + + return $index; +} + +sub DumpHfDeclaration($) +{ + my ($self) = @_; + my $res = ""; + + $res = "\n/* Header field declarations */\n"; + + foreach (keys %{$self->{conformance}->{header_fields}}) + { + $res .= "static gint $_ = -1;\n"; + } + + return "$res\n"; +} + +sub DumpHfList($) +{ + my ($self) = @_; + my $res = "\tstatic hf_register_info hf[] = {\n"; + + foreach (values %{$self->{conformance}->{header_fields}}) + { + $res .= "\t{ &$_->{INDEX}, + { ".make_str($_->{NAME}).", ".make_str($_->{FILTER}).", $_->{FT_TYPE}, $_->{BASE_TYPE}, $_->{VALSSTRING}, $_->{MASK}, ".make_str($_->{BLURB}).", HFILL }}, +"; + } + + return $res."\t};\n"; +} + + +############################################################################### +# Function table +############################################################################### + +sub DumpFunctionTable($) +{ + my $if = shift; + + my $res = "static dcerpc_sub_dissector $if->{NAME}\_dissectors[] = {\n"; + foreach (@{$if->{FUNCTIONS}}) { + my $fn_name = $_->{NAME}; + $fn_name =~ s/^$if->{NAME}_//; + $res.= "\t{ $_->{OPNUM}, \"$fn_name\",\n"; + $res.= "\t $if->{NAME}_dissect_${fn_name}_request, $if->{NAME}_dissect_${fn_name}_response},\n"; + } + + $res .= "\t{ 0, NULL, NULL, NULL }\n"; + + return "$res};\n"; +} + +sub CheckUsed($$) +{ + my ($self, $conformance) = @_; + foreach (values %{$conformance->{header_fields}}) { + if (not defined($self->{hf_used}->{$_->{INDEX}})) { + warning($_->{POS}, "hf field `$_->{INDEX}' not used"); + } + } + + foreach (values %{$conformance->{hf_renames}}) { + if (not $_->{USED}) { + warning($_->{POS}, "hf field `$_->{OLDNAME}' not used"); + } + } + + foreach (values %{$conformance->{dissectorparams}}) { + if (not $_->{USED}) { + warning($_->{POS}, "dissector param never used"); + } + } + + foreach (values %{$conformance->{imports}}) { + if (not $_->{USED}) { + warning($_->{POS}, "import never used"); + } + } + + foreach (values %{$conformance->{types}}) { + if (not $_->{USED} and defined($_->{POS})) { + warning($_->{POS}, "type never used"); + } + } + + foreach (values %{$conformance->{fielddescription}}) { + if (not $_->{USED}) { + warning($_->{POS}, "description never used"); + } + } + + foreach (values %{$conformance->{tfs}}) { + if (not $_->{USED}) { + warning($_->{POS}, "True/False description never used"); + } + } +} + +1; diff --git a/source/pidl/lib/Parse/Yapp/Driver.pm b/source/pidl/lib/Parse/Yapp/Driver.pm new file mode 100644 index 00000000000..f7a88d47739 --- /dev/null +++ b/source/pidl/lib/Parse/Yapp/Driver.pm @@ -0,0 +1,471 @@ +# +# Module Parse::Yapp::Driver +# +# This module is part of the Parse::Yapp package available on your +# nearest CPAN +# +# Any use of this module in a standalone parser make the included +# text under the same copyright as the Parse::Yapp module itself. +# +# This notice should remain unchanged. +# +# (c) Copyright 1998-2001 Francois Desarmenien, all rights reserved. +# (see the pod text in Parse::Yapp module for use and distribution rights) +# + +package Parse::Yapp::Driver; + +require 5.004; + +use strict; + +use vars qw ( $VERSION $COMPATIBLE $FILENAME ); + +$VERSION = '1.05'; +$COMPATIBLE = '0.07'; +$FILENAME=__FILE__; + +use Carp; + +#Known parameters, all starting with YY (leading YY will be discarded) +my(%params)=(YYLEX => 'CODE', 'YYERROR' => 'CODE', YYVERSION => '', + YYRULES => 'ARRAY', YYSTATES => 'ARRAY', YYDEBUG => ''); +#Mandatory parameters +my(@params)=('LEX','RULES','STATES'); + +sub new { + my($class)=shift; + my($errst,$nberr,$token,$value,$check,$dotpos); + my($self)={ ERROR => \&_Error, + ERRST => \$errst, + NBERR => \$nberr, + TOKEN => \$token, + VALUE => \$value, + DOTPOS => \$dotpos, + STACK => [], + DEBUG => 0, + CHECK => \$check }; + + _CheckParams( [], \%params, \@_, $self ); + + exists($$self{VERSION}) + and $$self{VERSION} < $COMPATIBLE + and croak "Yapp driver version $VERSION ". + "incompatible with version $$self{VERSION}:\n". + "Please recompile parser module."; + + ref($class) + and $class=ref($class); + + bless($self,$class); +} + +sub YYParse { + my($self)=shift; + my($retval); + + _CheckParams( \@params, \%params, \@_, $self ); + + if($$self{DEBUG}) { + _DBLoad(); + $retval = eval '$self->_DBParse()';#Do not create stab entry on compile + $@ and die $@; + } + else { + $retval = $self->_Parse(); + } + $retval +} + +sub YYData { + my($self)=shift; + + exists($$self{USER}) + or $$self{USER}={}; + + $$self{USER}; + +} + +sub YYErrok { + my($self)=shift; + + ${$$self{ERRST}}=0; + undef; +} + +sub YYNberr { + my($self)=shift; + + ${$$self{NBERR}}; +} + +sub YYRecovering { + my($self)=shift; + + ${$$self{ERRST}} != 0; +} + +sub YYAbort { + my($self)=shift; + + ${$$self{CHECK}}='ABORT'; + undef; +} + +sub YYAccept { + my($self)=shift; + + ${$$self{CHECK}}='ACCEPT'; + undef; +} + +sub YYError { + my($self)=shift; + + ${$$self{CHECK}}='ERROR'; + undef; +} + +sub YYSemval { + my($self)=shift; + my($index)= $_[0] - ${$$self{DOTPOS}} - 1; + + $index < 0 + and -$index <= @{$$self{STACK}} + and return $$self{STACK}[$index][1]; + + undef; #Invalid index +} + +sub YYCurtok { + my($self)=shift; + + @_ + and ${$$self{TOKEN}}=$_[0]; + ${$$self{TOKEN}}; +} + +sub YYCurval { + my($self)=shift; + + @_ + and ${$$self{VALUE}}=$_[0]; + ${$$self{VALUE}}; +} + +sub YYExpect { + my($self)=shift; + + keys %{$self->{STATES}[$self->{STACK}[-1][0]]{ACTIONS}} +} + +sub YYLexer { + my($self)=shift; + + $$self{LEX}; +} + + +################# +# Private stuff # +################# + + +sub _CheckParams { + my($mandatory,$checklist,$inarray,$outhash)=@_; + my($prm,$value); + my($prmlst)={}; + + while(($prm,$value)=splice(@$inarray,0,2)) { + $prm=uc($prm); + exists($$checklist{$prm}) + or croak("Unknow parameter '$prm'"); + ref($value) eq $$checklist{$prm} + or croak("Invalid value for parameter '$prm'"); + $prm=unpack('@2A*',$prm); + $$outhash{$prm}=$value; + } + for (@$mandatory) { + exists($$outhash{$_}) + or croak("Missing mandatory parameter '".lc($_)."'"); + } +} + +sub _Error { + print "Parse error.\n"; +} + +sub _DBLoad { + { + no strict 'refs'; + + exists(${__PACKAGE__.'::'}{_DBParse})#Already loaded ? + and return; + } + my($fname)=__FILE__; + my(@drv); + open(DRV,"<$fname") or die "Report this as a BUG: Cannot open $fname"; + while(<DRV>) { + /^\s*sub\s+_Parse\s*{\s*$/ .. /^\s*}\s*#\s*_Parse\s*$/ + and do { + s/^#DBG>//; + push(@drv,$_); + } + } + close(DRV); + + $drv[0]=~s/_P/_DBP/; + eval join('',@drv); +} + +#Note that for loading debugging version of the driver, +#this file will be parsed from 'sub _Parse' up to '}#_Parse' inclusive. +#So, DO NOT remove comment at end of sub !!! +sub _Parse { + my($self)=shift; + + my($rules,$states,$lex,$error) + = @$self{ 'RULES', 'STATES', 'LEX', 'ERROR' }; + my($errstatus,$nberror,$token,$value,$stack,$check,$dotpos) + = @$self{ 'ERRST', 'NBERR', 'TOKEN', 'VALUE', 'STACK', 'CHECK', 'DOTPOS' }; + +#DBG> my($debug)=$$self{DEBUG}; +#DBG> my($dbgerror)=0; + +#DBG> my($ShowCurToken) = sub { +#DBG> my($tok)='>'; +#DBG> for (split('',$$token)) { +#DBG> $tok.= (ord($_) < 32 or ord($_) > 126) +#DBG> ? sprintf('<%02X>',ord($_)) +#DBG> : $_; +#DBG> } +#DBG> $tok.='<'; +#DBG> }; + + $$errstatus=0; + $$nberror=0; + ($$token,$$value)=(undef,undef); + @$stack=( [ 0, undef ] ); + $$check=''; + + while(1) { + my($actions,$act,$stateno); + + $stateno=$$stack[-1][0]; + $actions=$$states[$stateno]; + +#DBG> print STDERR ('-' x 40),"\n"; +#DBG> $debug & 0x2 +#DBG> and print STDERR "In state $stateno:\n"; +#DBG> $debug & 0x08 +#DBG> and print STDERR "Stack:[". +#DBG> join(',',map { $$_[0] } @$stack). +#DBG> "]\n"; + + + if (exists($$actions{ACTIONS})) { + + defined($$token) + or do { + ($$token,$$value)=&$lex($self); +#DBG> $debug & 0x01 +#DBG> and print STDERR "Need token. Got ".&$ShowCurToken."\n"; + }; + + $act= exists($$actions{ACTIONS}{$$token}) + ? $$actions{ACTIONS}{$$token} + : exists($$actions{DEFAULT}) + ? $$actions{DEFAULT} + : undef; + } + else { + $act=$$actions{DEFAULT}; +#DBG> $debug & 0x01 +#DBG> and print STDERR "Don't need token.\n"; + } + + defined($act) + and do { + + $act > 0 + and do { #shift + +#DBG> $debug & 0x04 +#DBG> and print STDERR "Shift and go to state $act.\n"; + + $$errstatus + and do { + --$$errstatus; + +#DBG> $debug & 0x10 +#DBG> and $dbgerror +#DBG> and $$errstatus == 0 +#DBG> and do { +#DBG> print STDERR "**End of Error recovery.\n"; +#DBG> $dbgerror=0; +#DBG> }; + }; + + + push(@$stack,[ $act, $$value ]); + + $$token ne '' #Don't eat the eof + and $$token=$$value=undef; + next; + }; + + #reduce + my($lhs,$len,$code,@sempar,$semval); + ($lhs,$len,$code)=@{$$rules[-$act]}; + +#DBG> $debug & 0x04 +#DBG> and $act +#DBG> and print STDERR "Reduce using rule ".-$act." ($lhs,$len): "; + + $act + or $self->YYAccept(); + + $$dotpos=$len; + + unpack('A1',$lhs) eq '@' #In line rule + and do { + $lhs =~ /^\@[0-9]+\-([0-9]+)$/ + or die "In line rule name '$lhs' ill formed: ". + "report it as a BUG.\n"; + $$dotpos = $1; + }; + + @sempar = $$dotpos + ? map { $$_[1] } @$stack[ -$$dotpos .. -1 ] + : (); + + $semval = $code ? &$code( $self, @sempar ) + : @sempar ? $sempar[0] : undef; + + splice(@$stack,-$len,$len); + + $$check eq 'ACCEPT' + and do { + +#DBG> $debug & 0x04 +#DBG> and print STDERR "Accept.\n"; + + return($semval); + }; + + $$check eq 'ABORT' + and do { + +#DBG> $debug & 0x04 +#DBG> and print STDERR "Abort.\n"; + + return(undef); + + }; + +#DBG> $debug & 0x04 +#DBG> and print STDERR "Back to state $$stack[-1][0], then "; + + $$check eq 'ERROR' + or do { +#DBG> $debug & 0x04 +#DBG> and print STDERR +#DBG> "go to state $$states[$$stack[-1][0]]{GOTOS}{$lhs}.\n"; + +#DBG> $debug & 0x10 +#DBG> and $dbgerror +#DBG> and $$errstatus == 0 +#DBG> and do { +#DBG> print STDERR "**End of Error recovery.\n"; +#DBG> $dbgerror=0; +#DBG> }; + + push(@$stack, + [ $$states[$$stack[-1][0]]{GOTOS}{$lhs}, $semval ]); + $$check=''; + next; + }; + +#DBG> $debug & 0x04 +#DBG> and print STDERR "Forced Error recovery.\n"; + + $$check=''; + + }; + + #Error + $$errstatus + or do { + + $$errstatus = 1; + &$error($self); + $$errstatus # if 0, then YYErrok has been called + or next; # so continue parsing + +#DBG> $debug & 0x10 +#DBG> and do { +#DBG> print STDERR "**Entering Error recovery.\n"; +#DBG> ++$dbgerror; +#DBG> }; + + ++$$nberror; + + }; + + $$errstatus == 3 #The next token is not valid: discard it + and do { + $$token eq '' # End of input: no hope + and do { +#DBG> $debug & 0x10 +#DBG> and print STDERR "**At eof: aborting.\n"; + return(undef); + }; + +#DBG> $debug & 0x10 +#DBG> and print STDERR "**Dicard invalid token ".&$ShowCurToken.".\n"; + + $$token=$$value=undef; + }; + + $$errstatus=3; + + while( @$stack + and ( not exists($$states[$$stack[-1][0]]{ACTIONS}) + or not exists($$states[$$stack[-1][0]]{ACTIONS}{error}) + or $$states[$$stack[-1][0]]{ACTIONS}{error} <= 0)) { + +#DBG> $debug & 0x10 +#DBG> and print STDERR "**Pop state $$stack[-1][0].\n"; + + pop(@$stack); + } + + @$stack + or do { + +#DBG> $debug & 0x10 +#DBG> and print STDERR "**No state left on stack: aborting.\n"; + + return(undef); + }; + + #shift the error token + +#DBG> $debug & 0x10 +#DBG> and print STDERR "**Shift \$error token and go to state ". +#DBG> $$states[$$stack[-1][0]]{ACTIONS}{error}. +#DBG> ".\n"; + + push(@$stack, [ $$states[$$stack[-1][0]]{ACTIONS}{error}, undef ]); + + } + + #never reached + croak("Error in driver logic. Please, report it as a BUG"); + +}#_Parse +#DO NOT remove comment + +1; + diff --git a/source/pidl/pidl b/source/pidl/pidl new file mode 100755 index 00000000000..a4bdcbf714b --- /dev/null +++ b/source/pidl/pidl @@ -0,0 +1,780 @@ +#!/usr/bin/perl -w + +################################################### +# package to parse IDL files and generate code for +# rpc functions in Samba +# Copyright tridge@samba.org 2000-2003 +# Copyright jelmer@samba.org 2005-2007 +# released under the GNU GPL + +=pod + +=head1 NAME + +pidl - An IDL compiler written in Perl + +=head1 SYNOPSIS + +pidl --help + +pidl [--outputdir[=OUTNAME]] [--includedir DIR...] [--parse-idl-tree] [--dump-idl-tree] [--dump-ndr-tree] [--header[=OUTPUT]] [--ejs[=OUTPUT]] [--python[=OUTPUT]] [--swig[=OUTPUT]] [--ndr-parser[=OUTPUT]] [--client] [--server] [--warn-compat] [--quiet] [--verbose] [--template] [--ws-parser[=OUTPUT]] [--diff] [--dump-idl] [--tdr-parser[=OUTPUT]] [--samba3-ndr-client[=OUTPUT]] [--samba3-ndr-server[=OUTPUT]] [--typelib=[OUTPUT]] [<idlfile>.idl]... + +=head1 DESCRIPTION + +pidl is an IDL compiler written in Perl that aims to be somewhat +compatible with the midl compiler. IDL is short for +"Interface Definition Language". + +pidl can generate stubs for DCE/RPC server code, DCE/RPC +client code and Wireshark dissectors for DCE/RPC traffic. + +IDL compilers like pidl take a description +of an interface as their input and use it to generate C +(though support for other languages may be added later) code that +can use these interfaces, pretty print data sent +using these interfaces, or even generate Wireshark +dissectors that can parse data sent over the +wire by these interfaces. + +pidl takes IDL files in the same format as is used by midl, +converts it to a .pidl file (which contains pidl's internal representation of the interface) and can then generate whatever output you need. +.pidl files should be used for debugging purposes only. Write your +interface definitions in .idl format. + +The goal of pidl is to implement a IDL compiler that can be used +while developing the RPC subsystem in Samba (for +both marshalling/unmarshalling and debugging purposes). + +=head1 OPTIONS + +=over 4 + +=item I<--help> + +Show list of available options. + +=item I<--version> + +Show pidl version + +=item I<--outputdir OUTNAME> + +Write output files to the specified directory. Defaults to the current +directory. + +=item I<--includedir DIR> + +Add DIR to the search path used by the preprocessor. This option can be +specified multiple times. + +=item I<--parse-idl-tree> + +Read internal tree structure from input files rather +than assuming they contain IDL. + +=item I<--dump-idl> + +Generate a new IDL file. File will be named OUTNAME.idl. + +=item I<--header> + +Generate a C header file for the specified interface. Filename defaults to OUTNAME.h. + +=item I<--ndr-parser> + +Generate a C file and C header containing NDR parsers. The filename for +the parser defaults to ndr_OUTNAME.c. The header filename will be the +parser filename with the extension changed from .c to .h. + +=item I<--tdr-parser> + +Generate a C file and C header containing TDR parsers. The filename for +the parser defaults to tdr_OUTNAME.c. The header filename will be the +parser filename with the extension changed from .c to .h. + +=item I<--typelib> + +Write type information to the specified file. + +=item I<--server> + +Generate boilerplate for the RPC server that implements +the interface. Filename defaults to ndr_OUTNAME_s.c. + +=item I<--template> + +Generate stubs for a RPC server that implements the interface. Output will +be written to stdout. + +=item I<--ws-parser> + +Generate an Wireshark dissector (in C) and header file. The dissector filename +defaults to packet-dcerpc-OUTNAME.c while the header filename defaults to +packet-dcerpc-OUTNAME.h. + +Pidl will read additional data from an Wireshark conformance file if present. +Such a file should have the same location as the IDL file but with the +extension I<cnf> rather than I<idl>. See L<Parse::Pidl::Wireshark::Conformance> +for details on the format of this file. + +=item I<--diff> + +Parse an IDL file, generate a new IDL file based on the internal data +structures and see if there are any differences with the original IDL file. +Useful for debugging pidl. + +=item I<--dump-idl-tree> + +Tell pidl to dump the internal tree representation of an IDL +file the to disk. Useful for debugging pidl. + +=item I<--dump-ndr-tree> + +Tell pidl to dump the internal NDR information tree it generated +from the IDL file to disk. Useful for debugging pidl. + +=item I<--samba3-ndr-client> + +Generate client calls for Samba3, to be placed in rpc_client/. Instead of +calling out to the code in Samba3's rpc_parse/, this will call out to +Samba4's NDR code instead. + +=item I<--samba3-ndr-server> + +Generate server calls for Samba3, to be placed in rpc_server/. Instead of +calling out to the code in Samba3's rpc_parse/, this will call out to +Samba4's NDR code instead. + +=back + +=head1 IDL SYNTAX + +IDL files are always preprocessed using the C preprocessor. + +Pretty much everything in an interface (the interface itself, functions, +parameters) can have attributes (or properties whatever name you give them). +Attributes always prepend the element they apply to and are surrounded +by square brackets ([]). Multiple attributes are separated by comma's; +arguments to attributes are specified between parentheses. + +See the section COMPATIBILITY for the list of attributes that +pidl supports. + +C-style comments can be used. + +=head2 CONFORMANT ARRAYS + +A conformant array is one with that ends in [*] or []. The strange +things about conformant arrays are that they can only appear as the last +element of a structure (unless there is a pointer to the conformant array, +of course) and the array size appears before the structure itself on the wire. + +So, in this example: + + typedef struct { + long abc; + long count; + long foo; + [size_is(count)] long s[*]; + } Struct1; + +it appears like this: + + [size_is] [abc] [count] [foo] [s...] + +the first [size_is] field is the allocation size of the array, and +occurs before the array elements and even before the structure +alignment. + +Note that size_is() can refer to a constant, but that doesn't change +the wire representation. It does not make the array a fixed array. + +midl.exe would write the above array as the following C header: + + typedef struct { + long abc; + long count; + long foo; + long s[1]; + } Struct1; + +pidl takes a different approach, and writes it like this: + + typedef struct { + long abc; + long count; + long foo; + long *s; + } Struct1; + +=head2 VARYING ARRAYS + +A varying array looks like this: + + typedef struct { + long abc; + long count; + long foo; + [size_is(count)] long *s; + } Struct1; + +This will look like this on the wire: + + [abc] [count] [foo] [PTR_s] [count] [s...] + +=head2 FIXED ARRAYS + +A fixed array looks like this: + + typedef struct { + long s[10]; + } Struct1; + +The NDR representation looks just like 10 separate long +declarations. The array size is not encoded on the wire. + +pidl also supports "inline" arrays, which are not part of the IDL/NDR +standard. These are declared like this: + + typedef struct { + uint32 foo; + uint32 count; + uint32 bar; + long s[count]; + } Struct1; + +This appears like this: + + [foo] [count] [bar] [s...] + +Fixed arrays are an extension added to support some of the strange +embedded structures in security descriptors and spoolss. + +This section is by no means complete. See the OpenGroup and MSDN + documentation for additional information. + +=head1 COMPATIBILITY WITH MIDL + +=head2 Missing features in pidl + +The following MIDL features are not (yet) implemented in pidl +or are implemented with an incompatible interface: + +=over + +=item * + +Asynchronous communication + +=item * + +Typelibs (.tlb files) + +=item * + +Datagram support (ncadg_*) + +=back + +=head2 Supported attributes and statements + +in, out, ref, length_is, switch_is, size_is, uuid, case, default, string, +unique, ptr, pointer_default, v1_enum, object, helpstring, range, local, +call_as, endpoint, switch_type, progid, coclass, iid_is, represent_as, +transmit_as, import, include, cpp_quote. + +=head2 PIDL Specific properties + +=over 4 + +=item public + +The [public] property on a structure or union is a pidl extension that +forces the generated pull/push functions to be non-static. This allows +you to declare types that can be used between modules. If you don't +specify [public] then pull/push functions for other than top-level +functions are declared static. + +=item noprint + +The [noprint] property is a pidl extension that allows you to specify +that pidl should not generate a ndr_print_*() function for that +structure or union. This is used when you wish to define your own +print function that prints a structure in a nicer manner. A good +example is the use of [noprint] on dom_sid, which allows the +pretty-printing of SIDs. + +=item value + +The [value(expression)] property is a pidl extension that allows you +to specify the value of a field when it is put on the wire. This +allows fields that always have a well-known value to be automatically +filled in, thus making the API more programmer friendly. The +expression can be any C expression. + +=item relative + +The [relative] property can be supplied on a pointer. When it is used +it declares the pointer as a spoolss style "relative" pointer, which +means it appears on the wire as an offset within the current +encapsulating structure. This is not part of normal IDL/NDR, but it is +a very useful extension as it avoids the manual encoding of many +complex structures. + +=item subcontext(length) + +Specifies that a size of I<length> +bytes should be read, followed by a blob of that size, +which will be parsed as NDR. + +subcontext() is deprecated now, and should not be used in new code. +Instead, use represent_as() or transmit_as(). + +=item flag + +Specify boolean options, mostly used for +low-level NDR options. Several options +can be specified using the | character. +Note that flags are inherited by substructures! + +=item nodiscriminant + +The [nodiscriminant] property on a union means that the usual uint16 +discriminent field at the start of the union on the wire is +omitted. This is not normally allowed in IDL/NDR, but is used for some +spoolss structures. + +=item charset(name) + +Specify that the array or string uses the specified +charset. If this attribute is specified, pidl will +take care of converting the character data from this format +to the host format. Commonly used values are UCS2, DOS and UTF8. + +=back + +=head2 Unsupported MIDL properties or statements + +aggregatable, appobject, async_uuid, bindable, control, +defaultbind, defaultcollelem, defaultvalue, defaultvtable, dispinterface, +displaybind, dual, entry, first_is, helpcontext, helpfile, helpstringcontext, +helpstringdll, hidden, idl_module, idl_quote, id, immediatebind, importlib, +includelib, last_is, lcid, licensed, max_is, module, +ms_union, no_injected_text, nonbrowsable, noncreatable, nonextensible, odl, +oleautomation, optional, pragma, propget, propputref, propput, readonly, +requestedit, restricted, retval, source, uidefault, +usesgetlasterror, vararg, vi_progid, wire_marshal. + +=head1 EXAMPLES + + # Generating an Wireshark parser + $ ./pidl --ws-parser -- atsvc.idl + + # Generating a TDR parser and header + $ ./pidl --tdr-parser --header -- regf.idl + + # Generating a Samba3 client and server + $ ./pidl --samba3-ndr-client --samba3-ndr-server -- dfs.idl + + # Generating a Samba4 NDR parser, client and server + $ ./pidl --ndr-parser --ndr-client --ndr-server -- samr.idl + +=head1 SEE ALSO + +L<http://msdn.microsoft.com/library/en-us/rpc/rpc/field_attributes.asp>, +L<http://wiki.wireshark.org/DCE/RPC>, +L<http://www.samba.org/>, +L<yapp(1)> + +=head1 LICENSE + +pidl is licensed under the GNU General Public License L<http://www.gnu.org/licenses/gpl.html>. + +=head1 AUTHOR + +pidl was written by Andrew Tridgell, Stefan Metzmacher, Tim Potter and Jelmer +Vernooij. The current maintainer is Jelmer Vernooij. + +This manpage was written by Jelmer Vernooij, partially based on the original +pidl README by Andrew Tridgell. + +=cut + + +use strict; +use FindBin qw($RealBin $Script); +use lib "$RealBin/lib"; +use lib "$RealBin/../share/perl5"; +use Getopt::Long; +use File::Basename; +use Parse::Pidl qw ( $VERSION ); +use Parse::Pidl::Util; + +##################################################################### +# save a data structure into a file +sub SaveStructure($$) +{ + my($filename,$v) = @_; + FileSave($filename, Parse::Pidl::Util::MyDumper($v)); +} + +##################################################################### +# load a data structure from a file (as saved with SaveStructure) +sub LoadStructure($) +{ + my $f = shift; + my $contents = FileLoad($f); + defined $contents || return undef; + return eval "$contents"; +} + +##################################################################### +# read a file into a string +sub FileLoad($) +{ + my($filename) = shift; + local(*INPUTFILE); + open(INPUTFILE, $filename) || return undef; + my($saved_delim) = $/; + undef $/; + my($data) = <INPUTFILE>; + close(INPUTFILE); + $/ = $saved_delim; + return $data; +} + +##################################################################### +# write a string into a file +sub FileSave($$) +{ + my($filename) = shift; + my($v) = shift; + local(*FILE); + open(FILE, ">$filename") || die "can't open $filename"; + print FILE $v; + close(FILE); +} + +my(@opt_incdirs) = (); +my($opt_help) = 0; +my($opt_version) = 0; +my($opt_parse_idl_tree) = 0; +my($opt_dump_idl_tree); +my($opt_dump_ndr_tree); +my($opt_dump_idl) = 0; +my($opt_diff) = 0; +my($opt_header); +my($opt_samba3_header); +my($opt_samba3_parser); +my($opt_samba3_server); +my($opt_samba3_ndr_client); +my($opt_samba3_ndr_server); +my($opt_template) = 0; +my($opt_client); +my($opt_typelib); +my($opt_server); +my($opt_ndr_parser); +my($opt_tdr_parser); +my($opt_ws_parser); +my($opt_swig); +my($opt_ejs); +my($opt_python); +my($opt_quiet) = 0; +my($opt_outputdir) = '.'; +my($opt_verbose) = 0; +my($opt_warn_compat) = 0; + +######################################### +# display help text +sub ShowHelp() +{ +print "perl IDL parser and code generator\n"; +ShowVersion(); +print" +Copyright (C) Andrew Tridgell <tridge\@samba.org> +Copyright (C) Jelmer Vernooij <jelmer\@samba.org> + +Usage: $Script [options] [--] <idlfile> [<idlfile>...] + +Generic Options: + --help this help page + --version show pidl version + --outputdir=OUTDIR put output in OUTDIR/ [.] + --warn-compat warn about incompatibility with other compilers + --quiet be quiet + --verbose be verbose + --includedir DIR search DIR for included files + +Debugging: + --dump-idl-tree[=FILE] dump internal representation to file [BASENAME.pidl] + --parse-idl-tree read internal representation instead of IDL + --dump-ndr-tree[=FILE] dump internal NDR data tree to file [BASENAME.ndr] + --dump-idl regenerate IDL file + --diff run diff on original IDL and dumped output + --typelib print type information + +Samba 4 output: + --header[=OUTFILE] create generic header file [BASENAME.h] + --ndr-parser[=OUTFILE] create a C NDR parser [ndr_BASENAME.c] + --client[=OUTFILE] create a C NDR client [ndr_BASENAME_c.c] + --tdr-parser[=OUTFILE] create a C TDR parser [tdr_BASENAME.c] + --ejs[=OUTFILE] create ejs wrapper file [BASENAME_ejs.c] + --python[=OUTFILE] create python wrapper file [py_BASENAME.c] + --swig[=OUTFILE] create swig wrapper file [BASENAME.i] + --server[=OUTFILE] create server boilerplate [ndr_BASENAME_s.c] + --template print a template for a pipe + +Samba 3 output: + --samba3-ndr-client[=OUTF] create client calls for Samba3 + using Samba4's NDR code [cli_BASENAME.c] + --samba3-ndr-server[=OUTF] create server call wrapper for Samba3 + using Samba4's NDR code [srv_BASENAME.c] + +Wireshark parsers: + --ws-parser[=OUTFILE] create Wireshark parser and header +\n"; + exit(0); +} + +######################################### +# Display version +sub ShowVersion() +{ + print "perl IDL version $VERSION\n"; +} + +# main program +my $result = GetOptions ( + 'help|h|?' => \$opt_help, + 'version' => \$opt_version, + 'outputdir=s' => \$opt_outputdir, + 'dump-idl' => \$opt_dump_idl, + 'dump-idl-tree:s' => \$opt_dump_idl_tree, + 'parse-idl-tree' => \$opt_parse_idl_tree, + 'dump-ndr-tree:s' => \$opt_dump_ndr_tree, + 'samba3-ndr-client:s' => \$opt_samba3_ndr_client, + 'samba3-ndr-server:s' => \$opt_samba3_ndr_server, + 'header:s' => \$opt_header, + 'server:s' => \$opt_server, + 'typelib:s' => \$opt_typelib, + 'tdr-parser:s' => \$opt_tdr_parser, + 'template' => \$opt_template, + 'ndr-parser:s' => \$opt_ndr_parser, + 'client:s' => \$opt_client, + 'ws-parser:s' => \$opt_ws_parser, + 'ejs' => \$opt_ejs, + 'python' => \$opt_python, + 'diff' => \$opt_diff, + 'swig:s' => \$opt_swig, + 'quiet' => \$opt_quiet, + 'verbose' => \$opt_verbose, + 'warn-compat' => \$opt_warn_compat, + 'includedir=s@' => \@opt_incdirs + ); + +if (not $result) { + exit(1); +} + +if ($opt_help) { + ShowHelp(); + exit(0); +} + +if ($opt_version) { + ShowVersion(); + exit(0); +} + +sub process_file($) +{ + my $idl_file = shift; + my $outputdir = $opt_outputdir; + my $pidl; + my $ndr; + + my $basename = basename($idl_file, ".idl"); + + unless ($opt_quiet) { print "Compiling $idl_file\n"; } + + if ($opt_parse_idl_tree) { + $pidl = LoadStructure($idl_file); + defined $pidl || die "Failed to load $idl_file"; + } else { + require Parse::Pidl::IDL; + + $pidl = Parse::Pidl::IDL::parse_file($idl_file, \@opt_incdirs); + defined @$pidl || die "Failed to parse $idl_file"; + require Parse::Pidl::Typelist; + Parse::Pidl::Typelist::LoadIdl($pidl); + } + + if (defined($opt_dump_idl_tree)) { + my($pidl_file) = ($opt_dump_idl_tree or "$outputdir/$basename.pidl"); + SaveStructure($pidl_file, $pidl) or die "Failed to save $pidl_file\n"; + } + + if ($opt_dump_idl) { + require Parse::Pidl::Dump; + print Parse::Pidl::Dump($pidl); + } + + if ($opt_diff) { + my($tempfile) = "$outputdir/$basename.tmp"; + FileSave($tempfile, IdlDump::Dump($pidl)); + system("diff -wu $idl_file $tempfile"); + unlink($tempfile); + } + + if ($opt_warn_compat) { + require Parse::Pidl::Compat; + Parse::Pidl::Compat::Check($pidl); + } + + if (defined($opt_ws_parser) or + defined($opt_client) or + defined($opt_server) or + defined($opt_header) or + defined($opt_ndr_parser) or + defined($opt_ejs) or + defined($opt_python) or + defined($opt_dump_ndr_tree) or + defined($opt_samba3_header) or + defined($opt_samba3_parser) or + defined($opt_samba3_server) or + defined($opt_swig) or + defined($opt_samba3_ndr_client) or + defined($opt_samba3_ndr_server)) { + require Parse::Pidl::NDR; + $ndr = Parse::Pidl::NDR::Parse($pidl); + } + + if (defined($opt_dump_ndr_tree)) { + my($ndr_file) = ($opt_dump_ndr_tree or "$outputdir/$basename.ndr"); + SaveStructure($ndr_file, $ndr) or die "Failed to save $ndr_file\n"; + } + + my $gen_header = ($opt_header or "$outputdir/$basename.h"); + if (defined($opt_header)) { + require Parse::Pidl::Samba4::Header; + FileSave($gen_header, Parse::Pidl::Samba4::Header::Parse($ndr)); + } + + my $h_filename = "$outputdir/ndr_$basename.h"; + if (defined($opt_client)) { + require Parse::Pidl::Samba4::NDR::Client; + my ($c_client) = ($opt_client or "$outputdir/ndr_$basename\_c.c"); + my ($c_header) = $c_client; + $c_header =~ s/\.c$/.h/; + + my ($srcd,$hdrd) = Parse::Pidl::Samba4::NDR::Client::Parse( + $ndr,$gen_header,$h_filename,$c_header); + + FileSave($c_client, $srcd); + FileSave($c_header, $hdrd); + } + + if (defined($opt_swig)) { + require Parse::Pidl::Samba4::SWIG; + my($filename) = ($opt_swig or "$outputdir/$basename.i"); + my $code = Parse::Pidl::Samba4::SWIG::Parse($ndr, $basename, "$outputdir/ndr_$basename\_c.h", $gen_header); + FileSave($filename, $code); + } + + if (defined($opt_ejs)) { + require Parse::Pidl::Samba4::EJS; + my $generator = new Parse::Pidl::Samba4::EJS(); + my ($hdr,$prsr) = $generator->Parse($ndr, $h_filename); + FileSave("$outputdir/ndr_$basename\_ejs.c", $prsr); + FileSave("$outputdir/ndr_$basename\_ejs.h", $hdr); + } + + if (defined($opt_python)) { + require Parse::Pidl::Samba4::Python; + my $generator = new Parse::Pidl::Samba4::Python(); + my ($hdr,$prsr) = $generator->Parse($basename, $ndr, + "$outputdir/ndr_$basename\_c.h", $h_filename); + FileSave("$outputdir/py_$basename.c", $prsr); + FileSave("$outputdir/py_$basename.h", $hdr); + } + + if (defined($opt_server)) { + require Parse::Pidl::Samba4::NDR::Server; + + FileSave(($opt_server or "$outputdir/ndr_$basename\_s.c"), Parse::Pidl::Samba4::NDR::Server::Parse($ndr,$h_filename)); + } + + if (defined($opt_ndr_parser)) { + my $parser_fname = ($opt_ndr_parser or "$outputdir/ndr_$basename.c"); + require Parse::Pidl::Samba4::NDR::Parser; + my $generator = new Parse::Pidl::Samba4::NDR::Parser(); + my ($header,$parser) = $generator->Parse($ndr, $gen_header, $h_filename); + + FileSave($parser_fname, $parser); + FileSave($h_filename, $header); + + } + + if (defined($opt_ws_parser)) { + require Parse::Pidl::Wireshark::NDR; + my($eparser) = ($opt_ws_parser or "$outputdir/packet-dcerpc-$basename.c"); + my $eheader = $eparser; + $eheader =~ s/\.c$/\.h/; + my $cnffile = $idl_file; + $cnffile =~ s/\.idl$/\.cnf/; + + my $generator = new Parse::Pidl::Wireshark::NDR(); + my ($dp, $dh) = $generator->Parse($ndr, $idl_file, $eheader, $cnffile); + FileSave($eparser, $dp) if defined($dp); + FileSave($eheader, $dh) if defined($dh); + } + + if (defined($opt_tdr_parser)) { + my $tdr_parser = ($opt_tdr_parser or "$outputdir/tdr_$basename.c"); + my $tdr_header = $tdr_parser; + $tdr_header =~ s/\.c$/\.h/; + require Parse::Pidl::Samba4::TDR; + my $generator = new Parse::Pidl::Samba4::TDR(); + my ($hdr,$prsr) = $generator->Parser($pidl, $tdr_header, $gen_header); + FileSave($tdr_parser, $prsr); + FileSave($tdr_header, $hdr); + } + + if (defined($opt_typelib)) { + my $typelib = ($opt_typelib or "$outputdir/$basename.tlb"); + require Parse::Pidl::Typelist; + FileSave($typelib, Parse::Pidl::Typelist::GenerateTypeLib()); + } + + if ($opt_template) { + require Parse::Pidl::Samba4::Template; + print Parse::Pidl::Samba4::Template::Parse($pidl); + } + + if (defined($opt_samba3_ndr_client)) { + my $client = ($opt_samba3_ndr_client or "$outputdir/cli_$basename.c"); + my $header = $client; $header =~ s/\.c$/\.h/; + require Parse::Pidl::Samba3::ClientNDR; + my $generator = new Parse::Pidl::Samba3::ClientNDR(); + my ($c_code,$h_code) = $generator->Parse($ndr, $header, $h_filename); + FileSave($client, $c_code); + FileSave($header, $h_code); + } + + if (defined($opt_samba3_ndr_server)) { + my $server = ($opt_samba3_ndr_server or "$outputdir/srv_$basename.c"); + my $header = $server; $header =~ s/\.c$/\.h/; + require Parse::Pidl::Samba3::ServerNDR; + my ($c_code,$h_code) = Parse::Pidl::Samba3::ServerNDR::Parse($ndr, $header, $h_filename); + FileSave($server, $c_code); + FileSave($header, $h_code); + } + +} + +if (scalar(@ARGV) == 0) { + print "$Script: no input files\n"; + exit(1); +} + +process_file($_) foreach (@ARGV); diff --git a/source/pidl/tests/Util.pm b/source/pidl/tests/Util.pm new file mode 100644 index 00000000000..062bbb1a32a --- /dev/null +++ b/source/pidl/tests/Util.pm @@ -0,0 +1,179 @@ +# Some simple utility functions for pidl tests +# Copyright (C) 2005-2006 Jelmer Vernooij +# Published under the GNU General Public License + +package Util; + +require Exporter; +@ISA = qw(Exporter); +@EXPORT = qw(test_samba4_ndr test_warnings test_errors); + +use strict; + +use FindBin qw($RealBin); +use lib "$RealBin/../lib"; + +use Parse::Pidl; +my $warnings = ""; +undef &Parse::Pidl::warning; +*Parse::Pidl::warning = sub { + my ($e, $l) = @_; + if (defined($e)) { + $warnings .= "$e->{FILE}:$e->{LINE}: $l\n"; + } else { + $warnings .= "$l\n"; + } +}; + +my $errors = ""; +undef &Parse::Pidl::error; +*Parse::Pidl::error = sub { + my ($e, $l) = @_; + if (defined($e)) { + $errors .= "$e->{FILE}:$e->{LINE}: $l\n"; + } else { + $errors .= "$l\n"; + } +}; + +use Test::More; +use Parse::Pidl::IDL; +use Parse::Pidl::NDR; +use Parse::Pidl::Samba4::NDR::Parser; +use Parse::Pidl::Samba4::Header; + +# Generate a Samba4 parser for an IDL fragment and run it with a specified +# piece of code to check whether the parser works as expected +sub test_samba4_ndr +{ + my ($name,$idl,$c,$extra) = @_; + + $extra = "" unless defined($extra); + + my $pidl = Parse::Pidl::IDL::parse_string("interface echo { $idl }; ", "<$name>"); + ok(defined($pidl), "($name) parse idl"); + + my $pndr = Parse::Pidl::NDR::Parse($pidl); + ok(defined($pndr), "($name) generate NDR tree"); + + my $header = Parse::Pidl::Samba4::Header::Parse($pndr); + ok(defined($header), "($name) generate generic header"); + + my $generator = new Parse::Pidl::Samba4::NDR::Parser(); + my ($ndrheader,$ndrparser) = $generator->Parse($pndr, undef, undef); + ok(defined($ndrparser), "($name) generate NDR parser"); + ok(defined($ndrheader), "($name) generate NDR header"); + +SKIP: { + + skip "no samba environment available, skipping compilation", 3 + if (system("pkg-config --exists ndr") != 0); + + my $main = " +#define uint_t unsigned int +#define _GNU_SOURCE +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdbool.h> +#include <stdarg.h> +#include <util/data_blob.h> + +/* header start */ +$header +/* header end */ + +/* ndrheader start */ +$ndrheader +/* ndrheader end */ + +/* extra start */ +$extra +/* extra end */ + +/* ndrparser start */ +$ndrparser +/* ndrparser end */ + +/* main start */ +int main(int argc, const char **argv) +{ + TALLOC_CTX *mem_ctx = talloc_init(NULL); + +$c + + talloc_free(mem_ctx); + + return 0; +} +/* main end */ +\n"; + + my $main_debug = "# ".join("\n# ", split("\n", $main)); + + my $test_data_prefix = $ENV{TEST_DATA_PREFIX}; + my $outfile; + if (defined($test_data_prefix)) { + $outfile = "$test_data_prefix/test-$name"; + } else { + $outfile = "./test-$name"; + } + + my $cflags = $ENV{CFLAGS}; + unless (defined($cflags)) { + $cflags = ""; + } + + my $ldflags = $ENV{LDFLAGS}; + unless (defined($ldflags)) { + $ldflags = ""; + } + + my $cc = $ENV{CC}; + unless (defined($cc)) { + $cc = "cc"; + } + + my $flags = `pkg-config --libs --cflags ndr`; + + my $cmd = "$cc $cflags -x c - -o $outfile $flags $ldflags"; + $cmd =~ s/\n//g; + open CC, "|$cmd"; + print CC $main; + close CC; + + ok(-f $outfile, "($name) compile"); + + my $ret = system($outfile, ()) >> 8; + print "# code:\n#\n$main_debug\n" if ($ret != 0); + print "# cmd: $cmd\n" if ($ret != 0); + print "# return code: $ret\n" if ($ret != 0); + + ok($ret == 0, "($name) run"); + + ok(unlink($outfile), "($name) remove"); + + } +} + +sub test_warnings($$) +{ + my ($exp, $code) = @_; + + $warnings = ""; + + $code->(); + + is($warnings, $exp); +} + +sub test_errors($$) +{ + my ($exp, $code) = @_; + $errors = ""; + $code->(); + + is($errors, $exp); +} + +1; diff --git a/source/pidl/tests/cutil.pl b/source/pidl/tests/cutil.pl new file mode 100755 index 00000000000..78c8bce45e1 --- /dev/null +++ b/source/pidl/tests/cutil.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +# (C) 2007 Jelmer Vernooij <jelmer@samba.org> +# Published under the GNU General Public License +use strict; +use warnings; + +use Test::More tests => 7; +use FindBin qw($RealBin); +use lib "$RealBin"; +use Util; +use Parse::Pidl::Util qw(MyDumper); +use Parse::Pidl::CUtil qw(get_pointer_to get_value_of); + +is("&foo", get_pointer_to("foo")); +is("&(&foo)", get_pointer_to(get_pointer_to("foo"))); +is("*foo", get_pointer_to("**foo")); +is("foo", get_pointer_to("*foo")); + +is("foo", get_value_of("&foo")); +is("*foo", get_value_of("foo")); +is("**foo", get_value_of("*foo")); diff --git a/source/pidl/tests/dump.pl b/source/pidl/tests/dump.pl new file mode 100755 index 00000000000..b67392e9098 --- /dev/null +++ b/source/pidl/tests/dump.pl @@ -0,0 +1,15 @@ +#!/usr/bin/perl +# (C) 2007 Jelmer Vernooij <jelmer@samba.org> +# Published under the GNU General Public License +use strict; +use warnings; + +use Test::More tests => 1; +use FindBin qw($RealBin); +use lib "$RealBin"; +use Util; +use Parse::Pidl::Dump qw(DumpStruct); + +is (DumpStruct({ NAME => "foo", ELEMENTS => []}), + "struct foo {\n}"); + diff --git a/source/pidl/tests/header.pl b/source/pidl/tests/header.pl new file mode 100755 index 00000000000..1ae6faab2bb --- /dev/null +++ b/source/pidl/tests/header.pl @@ -0,0 +1,108 @@ +#!/usr/bin/perl +# (C) 2007 Jelmer Vernooij <jelmer@samba.org> +# Published under the GNU General Public License +use strict; +use warnings; + +use Test::More tests => 27; +use FindBin qw($RealBin); +use lib "$RealBin"; +use Util; +use Parse::Pidl::Util qw(MyDumper); +use Parse::Pidl::Samba4::Header qw( + GenerateFunctionInEnv GenerateFunctionOutEnv GenerateStructEnv + EnvSubstituteValue); +use Parse::Pidl::IDL qw(parse_string); +use Parse::Pidl::NDR; + +sub parse_idl($) +{ + my $text = shift; + my $idl = Parse::Pidl::IDL::parse_string($text, "nofile"); + my $ndr = Parse::Pidl::NDR::Parse($idl); + return Parse::Pidl::Samba4::Header::Parse($ndr); +} + +like(parse_idl(""), qr/\/\* header auto-generated by pidl \*\/\n/sm, "includes work"); +like(parse_idl("interface x {}"), qr/\/\* header auto-generated by pidl \*\/\n/sm, "simple empty interface doesn't cause overhead"); +like(parse_idl("interface p { typedef struct { int y; } x; };"), + qr/.*#ifndef _HEADER_p\n#define _HEADER_p\n.+\n#endif \/\* _HEADER_p \*\/.*/ms, "ifdefs are created"); +like(parse_idl("interface p { typedef struct { int y; } x; };"), + qr/struct x.*{.*int32_t y;.*}.*;/sm, "interface member generated properly"); +like(parse_idl("interface x { void foo (void); };"), + qr/struct foo.*{\s+int _dummy_element;\s+};/sm, "void fn contains dummy element"); +like(parse_idl("interface x { void foo ([in] uint32 x); };"), + qr/struct foo.*{\s+struct\s+{\s+uint32_t x;\s+} in;\s+};/sm, "fn in arg works"); +like(parse_idl("interface x { void foo ([out] uint32 x); };"), + qr/struct foo.*{.*struct\s+{\s+uint32_t x;\s+} out;.*};/sm, "fn out arg works"); +like(parse_idl("interface x { void foo ([in,out] uint32 x); };"), + qr/struct foo.*{.*struct\s+{\s+uint32_t x;\s+} in;\s+struct\s+{\s+uint32_t x;\s+} out;.*};/sm, "fn in,out arg works"); +like(parse_idl("interface x { void foo (uint32 x); };"), qr/struct foo.*{.*struct\s+{\s+uint32_t x;\s+} in;\s+struct\s+{\s+uint32_t x;\s+} out;.*};/sm, "fn with no props implies in,out"); +like(parse_idl("interface p { struct x { int y; }; };"), + qr/struct x.*{.*int32_t y;.*}.*;/sm, "interface member generated properly"); + +like(parse_idl("interface p { struct x { struct y z; }; };"), + qr/struct x.*{.*struct y z;.*}.*;/sm, "tagged type struct member"); + +like(parse_idl("interface p { struct x { union y z; }; };"), + qr/struct x.*{.*union y z;.*}.*;/sm, "tagged type union member"); + +like(parse_idl("interface p { struct x { }; };"), + qr/struct x.*{.*char _empty_;.*}.*;/sm, "empty struct"); + +like(parse_idl("interface p { struct x; };"), + qr/struct x;/sm, "struct declaration"); + +like(parse_idl("interface p { typedef struct x { int p; } x; };"), + qr/struct x.*{.*int32_t p;.*};/sm, "double struct declaration"); + +like(parse_idl("cpp_quote(\"some-foo\")"), + qr/some-foo/sm, "cpp quote"); + +# Make sure GenerateFunctionInEnv and GenerateFunctionOutEnv work +my $fn = { ELEMENTS => [ { DIRECTION => ["in"], NAME => "foo" } ] }; +is_deeply({ "foo" => "r->in.foo" }, GenerateFunctionInEnv($fn)); + +$fn = { ELEMENTS => [ { DIRECTION => ["out"], NAME => "foo" } ] }; +is_deeply({ "foo" => "r->out.foo" }, GenerateFunctionOutEnv($fn)); + +$fn = { ELEMENTS => [ { DIRECTION => ["out", "in"], NAME => "foo" } ] }; +is_deeply({ "foo" => "r->in.foo" }, GenerateFunctionInEnv($fn)); + +$fn = { ELEMENTS => [ { DIRECTION => ["out", "in"], NAME => "foo" } ] }; +is_deeply({ "foo" => "r->out.foo" }, GenerateFunctionOutEnv($fn)); + +$fn = { ELEMENTS => [ { DIRECTION => ["in"], NAME => "foo" } ] }; +is_deeply({ "foo" => "r->in.foo" }, GenerateFunctionOutEnv($fn)); + +$fn = { ELEMENTS => [ { DIRECTION => ["out"], NAME => "foo" } ] }; +is_deeply({ }, GenerateFunctionInEnv($fn)); + +$fn = { ELEMENTS => [ { NAME => "foo" }, { NAME => "bar" } ] }; +is_deeply({ foo => "r->foo", bar => "r->bar", this => "r" }, + GenerateStructEnv($fn, "r")); + +$fn = { ELEMENTS => [ { NAME => "foo" }, { NAME => "bar" } ] }; +is_deeply({ foo => "some->complex.variable->foo", + bar => "some->complex.variable->bar", + this => "some->complex.variable" }, + GenerateStructEnv($fn, "some->complex.variable")); + +$fn = { ELEMENTS => [ { NAME => "foo", PROPERTIES => { value => 3 }} ] }; + +my $env = GenerateStructEnv($fn, "r"); +EnvSubstituteValue($env, $fn); +is_deeply($env, { foo => 3, this => "r" }); + +$fn = { ELEMENTS => [ { NAME => "foo" }, { NAME => "bar" } ] }; +$env = GenerateStructEnv($fn, "r"); +EnvSubstituteValue($env, $fn); +is_deeply($env, { foo => 'r->foo', bar => 'r->bar', this => "r" }); + +$fn = { ELEMENTS => [ { NAME => "foo", PROPERTIES => { value => 0 }} ] }; + +$env = GenerateStructEnv($fn, "r"); +EnvSubstituteValue($env, $fn); +is_deeply($env, { foo => 0, this => "r" }); + + diff --git a/source/pidl/tests/ndr.pl b/source/pidl/tests/ndr.pl new file mode 100755 index 00000000000..541a1786aad --- /dev/null +++ b/source/pidl/tests/ndr.pl @@ -0,0 +1,558 @@ +#!/usr/bin/perl +# (C) 2007 Jelmer Vernooij <jelmer@samba.org> +# Published under the GNU General Public License +use strict; +use warnings; + +use Test::More tests => 46; +use FindBin qw($RealBin); +use lib "$RealBin"; +use Util; +use Parse::Pidl::Util qw(MyDumper); +use Parse::Pidl::NDR qw(GetElementLevelTable ParseElement align_type mapToScalar ParseType can_contain_deferred); + +# Case 1 + +my $e = { + 'FILE' => 'foo.idl', + 'NAME' => 'v', + 'PROPERTIES' => {}, + 'POINTERS' => 0, + 'TYPE' => 'uint8', + 'PARENT' => { TYPE => 'STRUCT' }, + 'LINE' => 42 }; + +is_deeply(GetElementLevelTable($e, "unique"), [ + { + 'IS_DEFERRED' => 0, + 'LEVEL_INDEX' => 0, + 'DATA_TYPE' => 'uint8', + 'CONTAINS_DEFERRED' => 0, + 'TYPE' => 'DATA', + 'IS_SURROUNDING' => 0, + } +]); + +my $ne = ParseElement($e, "unique"); +is($ne->{ORIGINAL}, $e); +is($ne->{NAME}, "v"); +is($ne->{ALIGN}, 1); +is($ne->{TYPE}, "uint8"); +is_deeply($ne->{LEVELS}, [ + { + 'IS_DEFERRED' => 0, + 'LEVEL_INDEX' => 0, + 'DATA_TYPE' => 'uint8', + 'CONTAINS_DEFERRED' => 0, + 'TYPE' => 'DATA', + 'IS_SURROUNDING' => 0, + } +]); + +# Case 2 : pointers +# +$e = { + 'FILE' => 'foo.idl', + 'NAME' => 'v', + 'PROPERTIES' => {"unique" => 1}, + 'POINTERS' => 1, + 'PARENT' => { TYPE => 'STRUCT' }, + 'TYPE' => 'uint8', + 'LINE' => 42 }; + +is_deeply(GetElementLevelTable($e, "unique"), [ + { + LEVEL_INDEX => 0, + IS_DEFERRED => 0, + TYPE => 'POINTER', + POINTER_TYPE => "unique", + POINTER_INDEX => 0, + LEVEL => 'EMBEDDED' + }, + { + 'IS_DEFERRED' => 1, + 'LEVEL_INDEX' => 1, + 'DATA_TYPE' => 'uint8', + 'CONTAINS_DEFERRED' => 0, + 'TYPE' => 'DATA', + 'IS_SURROUNDING' => 0, + } +]); + +# Case 3 : double pointers +# +$e = { + 'FILE' => 'foo.idl', + 'NAME' => 'v', + 'PROPERTIES' => {"unique" => 1}, + 'POINTERS' => 2, + 'TYPE' => 'uint8', + 'PARENT' => { TYPE => 'STRUCT' }, + 'LINE' => 42 }; + +is_deeply(GetElementLevelTable($e, "unique"), [ + { + LEVEL_INDEX => 0, + IS_DEFERRED => 0, + TYPE => 'POINTER', + POINTER_TYPE => "unique", + POINTER_INDEX => 0, + LEVEL => 'EMBEDDED' + }, + { + LEVEL_INDEX => 1, + IS_DEFERRED => 1, + TYPE => 'POINTER', + POINTER_TYPE => "unique", + POINTER_INDEX => 1, + LEVEL => 'EMBEDDED' + }, + { + 'IS_DEFERRED' => 1, + 'LEVEL_INDEX' => 2, + 'DATA_TYPE' => 'uint8', + 'CONTAINS_DEFERRED' => 0, + 'TYPE' => 'DATA', + 'IS_SURROUNDING' => 0, + } +]); + +# Case 3 : ref pointers +# +$e = { + 'FILE' => 'foo.idl', + 'NAME' => 'v', + 'PROPERTIES' => {"ref" => 1}, + 'POINTERS' => 1, + 'TYPE' => 'uint8', + 'PARENT' => { TYPE => 'STRUCT' }, + 'LINE' => 42 }; + +is_deeply(GetElementLevelTable($e, "unique"), [ + { + LEVEL_INDEX => 0, + IS_DEFERRED => 0, + TYPE => 'POINTER', + POINTER_TYPE => "ref", + POINTER_INDEX => 0, + LEVEL => 'EMBEDDED' + }, + { + 'IS_DEFERRED' => 1, + 'LEVEL_INDEX' => 1, + 'DATA_TYPE' => 'uint8', + 'CONTAINS_DEFERRED' => 0, + 'TYPE' => 'DATA', + 'IS_SURROUNDING' => 0, + } +]); + +# Case 3 : ref pointers +# +$e = { + 'FILE' => 'foo.idl', + 'NAME' => 'v', + 'PROPERTIES' => {"ref" => 1}, + 'POINTERS' => 3, + 'TYPE' => 'uint8', + 'PARENT' => { TYPE => 'STRUCT' }, + 'LINE' => 42 }; + +is_deeply(GetElementLevelTable($e, "unique"), [ + { + LEVEL_INDEX => 0, + IS_DEFERRED => 0, + TYPE => 'POINTER', + POINTER_TYPE => "ref", + POINTER_INDEX => 0, + LEVEL => 'EMBEDDED' + }, + { + LEVEL_INDEX => 1, + IS_DEFERRED => 1, + TYPE => 'POINTER', + POINTER_TYPE => "unique", + POINTER_INDEX => 1, + LEVEL => 'EMBEDDED' + }, + { + LEVEL_INDEX => 2, + IS_DEFERRED => 1, + TYPE => 'POINTER', + POINTER_TYPE => "unique", + POINTER_INDEX => 2, + LEVEL => 'EMBEDDED' + }, + { + 'IS_DEFERRED' => 1, + 'LEVEL_INDEX' => 3, + 'DATA_TYPE' => 'uint8', + 'CONTAINS_DEFERRED' => 0, + 'TYPE' => 'DATA', + 'IS_SURROUNDING' => 0, + } +]); + +# Case 3 : ref pointers +# +$e = { + 'FILE' => 'foo.idl', + 'NAME' => 'v', + 'PROPERTIES' => {"ref" => 1}, + 'POINTERS' => 3, + 'TYPE' => 'uint8', + 'PARENT' => { TYPE => 'STRUCT' }, + 'LINE' => 42 }; + +is_deeply(GetElementLevelTable($e, "ref"), [ + { + LEVEL_INDEX => 0, + IS_DEFERRED => 0, + TYPE => 'POINTER', + POINTER_TYPE => "ref", + POINTER_INDEX => 0, + LEVEL => 'EMBEDDED' + }, + { + LEVEL_INDEX => 1, + IS_DEFERRED => 1, + TYPE => 'POINTER', + POINTER_TYPE => "ref", + POINTER_INDEX => 1, + LEVEL => 'EMBEDDED' + }, + { + LEVEL_INDEX => 2, + IS_DEFERRED => 1, + TYPE => 'POINTER', + POINTER_TYPE => "ref", + POINTER_INDEX => 2, + LEVEL => 'EMBEDDED' + }, + { + 'IS_DEFERRED' => 1, + 'LEVEL_INDEX' => 3, + 'DATA_TYPE' => 'uint8', + 'CONTAINS_DEFERRED' => 0, + 'TYPE' => 'DATA', + 'IS_SURROUNDING' => 0, + } +]); + +# Case 4 : top-level ref pointers +# +$e = { + 'FILE' => 'foo.idl', + 'NAME' => 'v', + 'PROPERTIES' => {"ref" => 1}, + 'POINTERS' => 1, + 'TYPE' => 'uint8', + 'PARENT' => { TYPE => 'FUNCTION' }, + 'LINE' => 42 }; + +is_deeply(GetElementLevelTable($e, "unique"), [ + { + LEVEL_INDEX => 0, + IS_DEFERRED => 0, + TYPE => 'POINTER', + POINTER_TYPE => "ref", + POINTER_INDEX => 0, + LEVEL => 'TOP' + }, + { + 'IS_DEFERRED' => 0, + 'LEVEL_INDEX' => 1, + 'DATA_TYPE' => 'uint8', + 'CONTAINS_DEFERRED' => 0, + 'TYPE' => 'DATA', + 'IS_SURROUNDING' => 0, + } +]); + +# Case 4 : top-level ref pointers, triple with pointer_default("unique") +# +$e = { + 'FILE' => 'foo.idl', + 'NAME' => 'v', + 'PROPERTIES' => {"ref" => 1}, + 'POINTERS' => 3, + 'TYPE' => 'uint8', + 'PARENT' => { TYPE => 'FUNCTION' }, + 'LINE' => 42 }; + +is_deeply(GetElementLevelTable($e, "unique"), [ + { + LEVEL_INDEX => 0, + IS_DEFERRED => 0, + TYPE => 'POINTER', + POINTER_TYPE => "ref", + POINTER_INDEX => 0, + LEVEL => 'TOP' + }, + { + LEVEL_INDEX => 1, + IS_DEFERRED => 0, + TYPE => 'POINTER', + POINTER_TYPE => "unique", + POINTER_INDEX => 1, + LEVEL => 'EMBEDDED' + }, + { + LEVEL_INDEX => 2, + IS_DEFERRED => 1, + TYPE => 'POINTER', + POINTER_TYPE => "unique", + POINTER_INDEX => 2, + LEVEL => 'EMBEDDED' + }, + { + 'IS_DEFERRED' => 1, + 'LEVEL_INDEX' => 3, + 'DATA_TYPE' => 'uint8', + 'CONTAINS_DEFERRED' => 0, + 'TYPE' => 'DATA', + 'IS_SURROUNDING' => 0, + } +]); + +# Case 4 : top-level unique pointers, triple with pointer_default("unique") +# +$e = { + 'FILE' => 'foo.idl', + 'NAME' => 'v', + 'PROPERTIES' => {"unique" => 1, "in" => 1}, + 'POINTERS' => 3, + 'TYPE' => 'uint8', + 'PARENT' => { TYPE => 'FUNCTION' }, + 'LINE' => 42 }; + +is_deeply(GetElementLevelTable($e, "unique"), [ + { + LEVEL_INDEX => 0, + IS_DEFERRED => 0, + TYPE => 'POINTER', + POINTER_TYPE => "unique", + POINTER_INDEX => 0, + LEVEL => 'TOP' + }, + { + LEVEL_INDEX => 1, + IS_DEFERRED => 1, + TYPE => 'POINTER', + POINTER_TYPE => "unique", + POINTER_INDEX => 1, + LEVEL => 'EMBEDDED' + }, + { + LEVEL_INDEX => 2, + IS_DEFERRED => 1, + TYPE => 'POINTER', + POINTER_TYPE => "unique", + POINTER_INDEX => 2, + LEVEL => 'EMBEDDED' + }, + { + 'IS_DEFERRED' => 1, + 'LEVEL_INDEX' => 3, + 'DATA_TYPE' => 'uint8', + 'CONTAINS_DEFERRED' => 0, + 'TYPE' => 'DATA', + 'IS_SURROUNDING' => 0, + } +]); + +# Case 4 : top-level unique pointers, triple with pointer_default("ref") +# +$e = { + 'FILE' => 'foo.idl', + 'NAME' => 'v', + 'PROPERTIES' => {"unique" => 1, "in" => 1}, + 'POINTERS' => 3, + 'TYPE' => 'uint8', + 'PARENT' => { TYPE => 'FUNCTION' }, + 'LINE' => 42 }; + +is_deeply(GetElementLevelTable($e, "ref"), [ + { + LEVEL_INDEX => 0, + IS_DEFERRED => 0, + TYPE => 'POINTER', + POINTER_TYPE => "unique", + POINTER_INDEX => 0, + LEVEL => 'TOP' + }, + { + LEVEL_INDEX => 1, + IS_DEFERRED => 1, + TYPE => 'POINTER', + POINTER_TYPE => "ref", + POINTER_INDEX => 1, + LEVEL => 'EMBEDDED' + }, + { + LEVEL_INDEX => 2, + IS_DEFERRED => 1, + TYPE => 'POINTER', + POINTER_TYPE => "ref", + POINTER_INDEX => 2, + LEVEL => 'EMBEDDED' + }, + { + 'IS_DEFERRED' => 1, + 'LEVEL_INDEX' => 3, + 'DATA_TYPE' => 'uint8', + 'CONTAINS_DEFERRED' => 0, + 'TYPE' => 'DATA', + 'IS_SURROUNDING' => 0, + } +]); + +# Case 4 : top-level ref pointers, triple with pointer_default("ref") +# +$e = { + 'FILE' => 'foo.idl', + 'NAME' => 'v', + 'PROPERTIES' => {"ref" => 1}, + 'POINTERS' => 3, + 'TYPE' => 'uint8', + 'PARENT' => { TYPE => 'FUNCTION' }, + 'LINE' => 42 }; + +is_deeply(GetElementLevelTable($e, "ref"), [ + { + LEVEL_INDEX => 0, + IS_DEFERRED => 0, + TYPE => 'POINTER', + POINTER_TYPE => "ref", + POINTER_INDEX => 0, + LEVEL => 'TOP' + }, + { + LEVEL_INDEX => 1, + IS_DEFERRED => 0, + TYPE => 'POINTER', + POINTER_TYPE => "ref", + POINTER_INDEX => 1, + LEVEL => 'EMBEDDED' + }, + { + LEVEL_INDEX => 2, + IS_DEFERRED => 1, + TYPE => 'POINTER', + POINTER_TYPE => "ref", + POINTER_INDEX => 2, + LEVEL => 'EMBEDDED' + }, + { + 'IS_DEFERRED' => 1, + 'LEVEL_INDEX' => 3, + 'DATA_TYPE' => 'uint8', + 'CONTAINS_DEFERRED' => 0, + 'TYPE' => 'DATA', + 'IS_SURROUNDING' => 0, + } +]); + +# representation_type +$e = { + 'FILE' => 'foo.idl', + 'NAME' => 'v', + 'PROPERTIES' => { represent_as => "bar" }, + 'POINTERS' => 0, + 'TYPE' => 'uint8', + 'PARENT' => { TYPE => 'STRUCT' }, + 'LINE' => 42 }; + +$ne = ParseElement($e, undef); +is($ne->{REPRESENTATION_TYPE}, "bar"); + +# representation_type +$e = { + 'FILE' => 'foo.idl', + 'NAME' => 'v', + 'PROPERTIES' => { }, + 'POINTERS' => 0, + 'TYPE' => 'uint8', + 'PARENT' => { TYPE => 'STRUCT' }, + 'LINE' => 42 }; + +$ne = ParseElement($e, undef); +is($ne->{REPRESENTATION_TYPE}, "uint8"); + +is(align_type("hyper"), 8); +is(align_type("uint32"), 4); +is(align_type("uint16"), 2); +is(align_type("uint8"), 1); +is(align_type({ TYPE => "STRUCT", "NAME" => "bla", + ELEMENTS => [ { TYPE => "uint16" } ] }), 4); +is(align_type({ TYPE => "STRUCT", + ELEMENTS => [ { TYPE => "hyper" } ] }), 8); +is(align_type({ TYPE => "TYPEDEF", DATA => { + TYPE => "STRUCT", + ELEMENTS => [ { TYPE => "hyper" } ] }}), 8); +# typedef of struct without body +is(align_type({ TYPE => "TYPEDEF", DATA => { + TYPE => "STRUCT", ELEMENTS => undef }}), 4); +# struct without body +is(align_type({ TYPE => "STRUCT", ELEMENTS => undef }), 4); +# empty struct +is(align_type({ TYPE => "STRUCT", ELEMENTS => [] }), 1); +is(align_type({ TYPE => "STRUCT", "NAME" => "bla", + ELEMENTS => [ { TYPE => "uint8" } ] }), 4); + +is(mapToScalar("someverymuchnotexistingtype"), undef); +is(mapToScalar("uint32"), "uint32"); +is(mapToScalar({TYPE => "ENUM", PARENT => { PROPERTIES => { enum8bit => 1 } } }), "uint8"); +is(mapToScalar({TYPE => "BITMAP", PROPERTIES => { bitmap64bit => 1 } }), + "hyper"); +is(mapToScalar({TYPE => "TYPEDEF", DATA => {TYPE => "ENUM", PARENT => { PROPERTIES => { enum8bit => 1 } } }}), "uint8"); + +my $t; +$t = { + TYPE => "STRUCT", + NAME => "foo", + SURROUNDING_ELEMENT => undef, + ELEMENTS => undef, + PROPERTIES => undef, + ORIGINAL => { + TYPE => "STRUCT", + NAME => "foo" + }, + ALIGN => undef +}; +is_deeply(ParseType($t->{ORIGINAL}, "ref"), $t); + +$t = { + TYPE => "UNION", + NAME => "foo", + SWITCH_TYPE => "uint32", + ELEMENTS => undef, + PROPERTIES => undef, + HAS_DEFAULT => 0, + ORIGINAL => { + TYPE => "UNION", + NAME => "foo" + } +}; +is_deeply(ParseType($t->{ORIGINAL}, "ref"), $t); + +ok(not can_contain_deferred("uint32")); +ok(can_contain_deferred("some_unknown_type")); +ok(can_contain_deferred({ TYPE => "STRUCT", + ELEMENTS => [ { TYPE => "uint32", POINTERS => 40 } ]})); +ok(can_contain_deferred({ TYPE => "TYPEDEF", + DATA => { TYPE => "STRUCT", + ELEMENTS => [ { TYPE => "uint32", POINTERS => 40 } ]}})); +ok(not can_contain_deferred({ TYPE => "STRUCT", + ELEMENTS => [ { TYPE => "uint32" } ]})); +ok(not can_contain_deferred({ TYPE => "TYPEDEF", + DATA => { TYPE => "STRUCT", + ELEMENTS => [ { TYPE => "uint32" } ]}})); +ok(can_contain_deferred({ TYPE => "STRUCT", + ELEMENTS => [ { TYPE => "someunknowntype" } ]})); +# Make sure the elements for a enum without body aren't filled in +ok(not defined(ParseType({TYPE => "ENUM", NAME => "foo" }, "ref")->{ELEMENTS})); +# Make sure the elements for a bitmap without body aren't filled in +ok(not defined(ParseType({TYPE => "BITMAP", NAME => "foo" }, "ref")->{ELEMENTS})); +# Make sure the elements for a union without body aren't filled in +ok(not defined(ParseType({TYPE => "UNION", NAME => "foo" }, "ref")->{ELEMENTS})); diff --git a/source/pidl/tests/ndr_align.pl b/source/pidl/tests/ndr_align.pl new file mode 100755 index 00000000000..ea97f7189c3 --- /dev/null +++ b/source/pidl/tests/ndr_align.pl @@ -0,0 +1,143 @@ +#!/usr/bin/perl +# NDR alignment tests +# (C) 2005 Jelmer Vernooij. Published under the GNU GPL +use strict; + +use Test::More tests => 5 * 8; +use FindBin qw($RealBin); +use lib "$RealBin"; +use Util qw(test_samba4_ndr); + +test_samba4_ndr('align-uint8-uint16', +' + typedef [public] struct { + uint8 x; + uint16 y; + } bla; +', +' + struct ndr_push *ndr = ndr_push_init_ctx(NULL, NULL); + struct bla r; + uint8_t expected[] = { 0x0D, 0x00, 0xef, 0xbe }; + DATA_BLOB expected_blob = { expected, 4 }; + DATA_BLOB result_blob; + r.x = 13; + r.y = 0xbeef; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_push_bla(ndr, NDR_SCALARS|NDR_BUFFERS, &r))) + return 1; + + result_blob = ndr_push_blob(ndr); + + if (data_blob_cmp(&result_blob, &expected_blob) != 0) + return 2; +'); + +test_samba4_ndr('align-uint8-uint32', +' + typedef [public] struct { + uint8 x; + uint32 y; + } bla; +', +' + struct ndr_push *ndr = ndr_push_init_ctx(NULL, NULL); + struct bla r; + uint8_t expected[] = { 0x0D, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xef, 0xbe }; + DATA_BLOB expected_blob = { expected, 8 }; + DATA_BLOB result_blob; + r.x = 13; + r.y = 0xbeefbeef; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_push_bla(ndr, NDR_SCALARS|NDR_BUFFERS, &r))) + return 1; + + result_blob = ndr_push_blob(ndr); + + if (data_blob_cmp(&result_blob, &expected_blob) != 0) + return 2; +'); + + +test_samba4_ndr('align-uint8-hyper', +' + typedef [public] struct { + uint8 x; + hyper y; + } bla; +', +' + struct ndr_push *ndr = ndr_push_init_ctx(NULL, NULL); + struct bla r; + uint8_t expected[] = { 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe }; + DATA_BLOB expected_blob = { expected, 16 }; + DATA_BLOB result_blob; + r.x = 13; + r.y = 0xbeefbeefbeefbeefLLU; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_push_bla(ndr, NDR_SCALARS|NDR_BUFFERS, &r))) + return 1; + + result_blob = ndr_push_blob(ndr); + + if (data_blob_cmp(&result_blob, &expected_blob) != 0) + return 2; +'); + +test_samba4_ndr('noalignflag-uint8-uint16', +' + typedef [public] struct { + uint8 x; + uint16 y; + } bla; +', +' + struct ndr_push *ndr = ndr_push_init_ctx(NULL, NULL); + struct bla r; + uint8_t expected[] = { 0x0D, 0xef, 0xbe }; + DATA_BLOB expected_blob = { expected, 3 }; + DATA_BLOB result_blob; + ndr->flags |= LIBNDR_FLAG_NOALIGN; + + r.x = 13; + r.y = 0xbeef; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_push_bla(ndr, NDR_SCALARS|NDR_BUFFERS, &r))) + return 1; + + result_blob = ndr_push_blob(ndr); + + if (data_blob_cmp(&result_blob, &expected_blob) != 0) + return 2; +'); + +test_samba4_ndr('align-blob-align2', +' + typedef [public] struct { + uint8 x; + [flag(LIBNDR_FLAG_ALIGN2)] DATA_BLOB data; + uint8 y; + } blie; +', +' + struct ndr_push *ndr = ndr_push_init_ctx(NULL, NULL); + struct blie r; + uint8_t data[] = { 0x01, 0x02 }; + uint8_t expected[] = { 0x0D, 0x00, 0x0E }; + DATA_BLOB expected_blob = { expected, 3 }; + DATA_BLOB result_blob; + + r.x = 13; + r.y = 14; + r.data.data = data; + r.data.length = 2; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_push_blie(ndr, NDR_SCALARS|NDR_BUFFERS, &r))) + return 1; + + result_blob = ndr_push_blob(ndr); + + if (data_blob_cmp(&result_blob, &expected_blob) != 0) + return 2; +'); diff --git a/source/pidl/tests/ndr_alloc.pl b/source/pidl/tests/ndr_alloc.pl new file mode 100755 index 00000000000..3a22eccf4b9 --- /dev/null +++ b/source/pidl/tests/ndr_alloc.pl @@ -0,0 +1,118 @@ +#!/usr/bin/perl +# NDR allocation tests +# (C) 2005 Jelmer Vernooij. Published under the GNU GPL +use strict; + +use Test::More tests => 5 * 8; +use FindBin qw($RealBin); +use lib "$RealBin"; +use Util qw(test_samba4_ndr); + +# Check that an outgoing scalar pointer is allocated correctly + +test_samba4_ndr("alloc-scalar", +' + typedef struct { + uint8 *x; + } bla; + + [public] void TestAlloc([in] bla foo); +',' + uint8_t data[] = { 0xde, 0xad, 0xbe, 0xef, 0x03 }; + DATA_BLOB b = { data, 5 }; + struct ndr_pull *ndr = ndr_pull_init_blob(&b, NULL, NULL); + struct TestAlloc r; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_pull_TestAlloc(ndr, NDR_IN, &r))) + return 1; + + if (r.in.foo.x == NULL) + return 2; + + if (*r.in.foo.x != 0x03) + return 3; +' +); + +# Check that an outgoing buffer pointer is allocated correctly +test_samba4_ndr("alloc-buffer", +' + typedef struct { uint8 data; } blie; + typedef struct { blie *x; } bla; + + [public] void TestAlloc([in] bla foo); +',' + uint8_t data[] = { 0xde, 0xad, 0xbe, 0xef, 0x03 }; + DATA_BLOB b = { data, 5 }; + struct ndr_pull *ndr = ndr_pull_init_blob(&b, NULL, NULL); + struct TestAlloc r; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_pull_TestAlloc(ndr, NDR_IN, &r))) + return 1; + + if (r.in.foo.x == NULL) + return 2; + + if (r.in.foo.x->data != 0x03) + return 3; +' +); + +# Check that ref pointers aren't allocated by default +test_samba4_ndr("ref-noalloc-null", +' + [public] void TestAlloc([in,ref] uint8 *t); +',' + uint8_t data[] = { 0x03 }; + DATA_BLOB b = { data, 1 }; + struct ndr_pull *ndr = ndr_pull_init_blob(&b, NULL, NULL); + struct TestAlloc r; + r.in.t = NULL; + + if (NDR_ERR_CODE_IS_SUCCESS(ndr_pull_TestAlloc(ndr, NDR_IN, &r))) + return 1; +' +); + +# Check that ref pointers aren't allocated by default +test_samba4_ndr("ref-noalloc", +' + [public] void TestAlloc([in,ref] uint8 *t); +',' + uint8_t data[] = { 0x03 }; + DATA_BLOB b = { data, 1 }; + struct ndr_pull *ndr = ndr_pull_init_blob(&b, NULL, NULL); + struct TestAlloc r; + uint8_t x; + r.in.t = &x; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_pull_TestAlloc(ndr, NDR_IN, &r))) + return 1; + + if (*r.in.t != 0x03) + return 2; +' +); + +# Check that an outgoing ref pointer is allocated correctly +test_samba4_ndr("ref-alloc", +' + [public] void TestAlloc([in,ref] uint8 *t); +',' + uint8_t data[] = { 0x03 }; + DATA_BLOB b = { data, 1 }; + struct ndr_pull *ndr = ndr_pull_init_blob(&b, NULL, NULL); + struct TestAlloc r; + ndr->flags |= LIBNDR_FLAG_REF_ALLOC; + r.in.t = NULL; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_pull_TestAlloc(ndr, NDR_IN, &r))) + return 1; + + if (r.in.t == NULL) + return 2; + + if (*r.in.t != 0x03) + return 3; +' +); diff --git a/source/pidl/tests/ndr_array.pl b/source/pidl/tests/ndr_array.pl new file mode 100755 index 00000000000..a81c304f5bf --- /dev/null +++ b/source/pidl/tests/ndr_array.pl @@ -0,0 +1,37 @@ +#!/usr/bin/perl +# Array testing +# (C) 2005 Jelmer Vernooij <jelmer@samba.org> +# Published under the GNU General Public License +use strict; + +use Test::More tests => 8; +use FindBin qw($RealBin); +use lib "$RealBin"; +use Util qw(test_samba4_ndr); + +test_samba4_ndr( + 'Fixed-Array', + + '[public] void Test([in] uint8 x[10]);', + + ' + uint8_t data[] = {1,2,3,4,5,6,7,8,9,10}; + int i; + DATA_BLOB b; + struct ndr_pull *ndr; + struct Test r; + + b.data = data; + b.length = 10; + ndr = ndr_pull_init_blob(&b, mem_ctx, NULL); + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_pull_Test(ndr, NDR_IN, &r))) + return 1; + + if (ndr->offset != 10) + return 2; + + for (i = 0; i < 10; i++) { + if (r.in.x[i] != i+1) return 3; + } +'); diff --git a/source/pidl/tests/ndr_compat.pl b/source/pidl/tests/ndr_compat.pl new file mode 100755 index 00000000000..967509cbf82 --- /dev/null +++ b/source/pidl/tests/ndr_compat.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +# (C) 2007 Jelmer Vernooij <jelmer@samba.org> +# Published under the GNU General Public License +use strict; + +use Test::More tests => 2; +use FindBin qw($RealBin); +use lib "$RealBin"; +use Util; +use Parse::Pidl; +use Parse::Pidl::IDL; + +sub parse_idl($) +{ + my $idl = shift; + my $pidl = Parse::Pidl::IDL::parse_string("interface echo { $idl }; ", "nofile"); + Parse::Pidl::NDR::Parse($pidl); +} + +test_warnings("", sub {parse_idl("void x();"); }); +test_warnings("nofile:0: top-level [out] pointer `x' is not a [ref] pointer\n", sub {parse_idl("void x([out,unique] int *x);"); }); diff --git a/source/pidl/tests/ndr_deprecations.pl b/source/pidl/tests/ndr_deprecations.pl new file mode 100755 index 00000000000..7771bbb3df7 --- /dev/null +++ b/source/pidl/tests/ndr_deprecations.pl @@ -0,0 +1,26 @@ +#!/usr/bin/perl +# (C) 2007 Jelmer Vernooij <jelmer@samba.org> +# Published under the GNU General Public License +use strict; +use warnings; + +use Test::More tests => 1; +use FindBin qw($RealBin); +use lib "$RealBin"; +use Util; +use Parse::Pidl::Util qw(MyDumper); +use Parse::Pidl::NDR qw(ValidElement); + +# Case 1 + +my $e = { + 'FILE' => 'foo.idl', + 'NAME' => 'v', + 'PROPERTIES' => {"subcontext" => 1}, + 'POINTERS' => 0, + 'TYPE' => 'uint8', + 'PARENT' => { TYPE => 'STRUCT' }, + 'LINE' => 42 }; + +test_warnings("foo.idl:42: subcontext() is deprecated. Use represent_as() or transmit_as() instead\n", + sub { ValidElement($e); }); diff --git a/source/pidl/tests/ndr_fullptr.pl b/source/pidl/tests/ndr_fullptr.pl new file mode 100755 index 00000000000..edb15563dc4 --- /dev/null +++ b/source/pidl/tests/ndr_fullptr.pl @@ -0,0 +1,44 @@ +#!/usr/bin/perl +# Simple tests for unique pointers +# (C) 2006 Jelmer Vernooij <jelmer@samba.org>. +# Published under the GNU General Public License. +use strict; + +use Test::More tests => 1 * 8; +use FindBin qw($RealBin); +use lib "$RealBin"; +use Util qw(test_samba4_ndr); + +SKIP: { + skip "full pointers not supported yet", 8; + +test_samba4_ndr("fullptr-push-dup", +' + [public] uint16 echo_TestFull([in,ptr] uint32 *x, [in,ptr] uint32 *y); +', +' + struct ndr_push *ndr = ndr_push_init_ctx(NULL, NULL); + uint32_t v = 13; + struct echo_TestFull r; + r.in.x = &v; + r.in.y = &v; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_push_echo_TestFull(ndr, NDR_IN, &r))) { + fprintf(stderr, "push failed\n"); + return 1; + } + + if (ndr->offset != 12) { + fprintf(stderr, "Offset(%d) != 12\n", ndr->offset); + return 2; + } + + if (ndr->data[0] != ndr->data[8] || + ndr->data[1] != ndr->data[9] || + ndr->data[2] != ndr->data[10] || + ndr->data[3] != ndr->data[11]) { + fprintf(stderr, "Data incorrect\n"); + return 3; + } +'); +} diff --git a/source/pidl/tests/ndr_refptr.pl b/source/pidl/tests/ndr_refptr.pl new file mode 100755 index 00000000000..3a19a3f8789 --- /dev/null +++ b/source/pidl/tests/ndr_refptr.pl @@ -0,0 +1,526 @@ +#!/usr/bin/perl +# Simple tests for pidl's handling of ref pointers, based +# on tridge's ref_notes.txt +# (C) 2005 Jelmer Vernooij <jelmer@samba.org>. +# Published under the GNU General Public License. +use strict; + +use Test::More tests => 22 * 8; +use FindBin qw($RealBin); +use lib "$RealBin"; +use Util qw(test_samba4_ndr); + +test_samba4_ndr("noptr-push", +' typedef struct { + uint16 x; + } xstruct; + + [public] uint16 echo_TestRef([in] xstruct foo); +', +' + struct ndr_push *ndr = ndr_push_init_ctx(NULL, NULL); + uint16_t v = 13; + struct echo_TestRef r; + r.in.foo.x = v; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_push_echo_TestRef(ndr, NDR_IN, &r))) { + fprintf(stderr, "push failed\n"); + return 1; + } + + if (ndr->offset != 2) { + fprintf(stderr, "Offset(%d) != 2\n", ndr->offset); + return 2; + } + + if (ndr->data[0] != 13 || ndr->data[1] != 0) { + fprintf(stderr, "Data incorrect\n"); + return 3; + } +'); + +test_samba4_ndr("ptr-embedded-push", +' typedef struct { + uint16 *x; + } xstruct; + + [public] uint16 echo_TestRef([in] xstruct foo); +', +' + uint16_t v = 13; + struct ndr_push *ndr = ndr_push_init_ctx(NULL, NULL); + struct echo_TestRef r; + r.in.foo.x = &v; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_push_echo_TestRef(ndr, NDR_IN, &r))) + return 1; + + if (ndr->offset != 6) + return 2; + + if (ndr->data[0] == 0 && ndr->data[1] == 0 && + ndr->data[2] == 0 && ndr->data[3] == 0) + return 3; + + if (ndr->data[4] != 13 || ndr->data[5] != 0) + return 4; +'); + +test_samba4_ndr("ptr-embedded-push-null", +' typedef struct { + uint16 *x; + } xstruct; + + [public] uint16 echo_TestRef([in] xstruct foo); +', +' + struct ndr_push *ndr = ndr_push_init_ctx(NULL, NULL); + struct echo_TestRef r; + r.in.foo.x = NULL; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_push_echo_TestRef(ndr, NDR_IN, &r))) + return 1; + + if (ndr->offset != 4) + return 2; + + if (ndr->data[0] != 0 || ndr->data[1] != 0 || + ndr->data[2] != 0 || ndr->data[3] != 0) + return 3; +'); + +test_samba4_ndr("refptr-embedded-push", +' + typedef struct { + [ref] uint16 *x; + } xstruct; + + [public] uint16 echo_TestRef([in] xstruct foo); +', +' + uint16_t v = 13; + struct ndr_push *ndr = ndr_push_init_ctx(NULL, NULL); + struct echo_TestRef r; + r.in.foo.x = &v; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_push_echo_TestRef(ndr, NDR_IN, &r))) + return 1; + + if (ndr->offset != 6) + return 2; + + if (ndr->data[0] == 0 && ndr->data[1] == 0 && + ndr->data[2] == 0 && ndr->data[3] == 0) + return 3; + + if (ndr->data[4] != 13 || ndr->data[5] != 0) + return 4; +'); + +test_samba4_ndr("refptr-embedded-push-null", +' + typedef struct { + [ref] uint16 *x; + } xstruct; + + [public] uint16 echo_TestRef([in] xstruct foo); +', +' + struct ndr_push *ndr = ndr_push_init_ctx(NULL, NULL); + struct echo_TestRef r; + r.in.foo.x = NULL; + + if (NDR_ERR_CODE_IS_SUCCESS(ndr_push_echo_TestRef(ndr, NDR_IN, &r))) + return 1; + /* Windows gives [client runtime error 0x6f4] */ +'); + +test_samba4_ndr("ptr-top-push", +' + typedef struct { + uint16 x; + } xstruct; + + [public] uint16 echo_TestRef([in] xstruct *foo); +', +' + struct ndr_push *ndr = ndr_push_init_ctx(NULL, NULL); + struct echo_TestRef r; + struct xstruct s; + s.x = 13; + r.in.foo = &s; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_push_echo_TestRef(ndr, NDR_IN, &r))) + return 1; + + if (ndr->offset != 2) + return 2; + + if (ndr->data[0] != 13 || ndr->data[1] != 0) + return 3; +'); + +test_samba4_ndr("ptr-top-push-null", +' + typedef struct { + uint16 x; + } xstruct; + + [public] uint16 echo_TestRef([in] xstruct *foo); +', +' + struct ndr_push *ndr = ndr_push_init_ctx(NULL, NULL); + struct echo_TestRef r; + r.in.foo = NULL; + + if (NDR_ERR_CODE_IS_SUCCESS(ndr_push_echo_TestRef(ndr, NDR_IN, &r))) + return 1; + + /* Windows gives [client runtime error 0x6f4] */ +'); + + +test_samba4_ndr("refptr-top-push", +' + typedef struct { + uint16 x; + } xstruct; + + [public] uint16 echo_TestRef([in,ref] xstruct *foo); +', +' + struct ndr_push *ndr = ndr_push_init_ctx(NULL, NULL); + struct echo_TestRef r; + struct xstruct s; + s.x = 13; + r.in.foo = &s; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_push_echo_TestRef(ndr, NDR_IN, &r))) + return 1; + + if (ndr->offset != 2) + return 2; + + if (ndr->data[0] != 13 || ndr->data[1] != 0) + return 3; +'); + +test_samba4_ndr("refptr-top-push-null", +' + typedef struct { + uint16 x; + } xstruct; + + [public] uint16 echo_TestRef([in,ref] xstruct *foo); +', +' + struct ndr_push *ndr = ndr_push_init_ctx(NULL, NULL); + struct echo_TestRef r; + r.in.foo = NULL; + + if (NDR_ERR_CODE_IS_SUCCESS(ndr_push_echo_TestRef(ndr, NDR_IN, &r))) + return 1; + + /* Windows gives [client runtime error 0x6f4] */ +'); + + +test_samba4_ndr("uniqueptr-top-push", +' typedef struct { + uint16 x; + } xstruct; + + [public] uint16 echo_TestRef([in,unique] xstruct *foo); +', +' + struct ndr_push *ndr = ndr_push_init_ctx(NULL, NULL); + struct echo_TestRef r; + struct xstruct s; + s.x = 13; + r.in.foo = &s; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_push_echo_TestRef(ndr, NDR_IN, &r))) + return 1; + + if (ndr->offset != 6) + return 2; + + if (ndr->data[0] == 0 && ndr->data[1] == 0 && + ndr->data[2] == 0 && ndr->data[3] == 0) + return 3; + + if (ndr->data[4] != 13 || ndr->data[5] != 0) + return 4; +'); + +test_samba4_ndr("uniqueptr-top-push-null", +' typedef struct { + uint16 x; + } xstruct; + + [public] uint16 echo_TestRef([in,unique] xstruct *foo); +', +' + struct ndr_push *ndr = ndr_push_init_ctx(NULL, NULL); + struct echo_TestRef r; + r.in.foo = NULL; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_push_echo_TestRef(ndr, NDR_IN, &r))) + return 1; + + if (ndr->offset != 4) + return 2; + + if (ndr->data[0] != 0 || ndr->data[1] != 0 || + ndr->data[2] != 0 || ndr->data[3] != 0) + return 3; +'); + + +test_samba4_ndr("ptr-top-out-pull", +' + typedef struct { + uint16 x; + } xstruct; + + [public] void echo_TestRef([out] xstruct *foo); +', +' + uint8_t data[] = { 0x0D, 0x00 }; + DATA_BLOB b = { data, 2 }; + struct ndr_pull *ndr = ndr_pull_init_blob(&b, NULL, NULL); + struct xstruct s; + struct echo_TestRef r; + + r.out.foo = &s; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_pull_echo_TestRef(ndr, NDR_OUT, &r))) + return 1; + + if (!r.out.foo) + return 2; + + if (r.out.foo->x != 13) + return 3; +'); + +test_samba4_ndr("ptr-top-out-pull-null", +' + typedef struct { + uint16 x; + } xstruct; + + [public] void echo_TestRef([out] xstruct *foo); +', +' + uint8_t data[] = { 0x0D, 0x00 }; + DATA_BLOB b = { data, 2 }; + struct ndr_pull *ndr = ndr_pull_init_blob(&b, NULL, NULL); + struct echo_TestRef r; + + r.out.foo = NULL; + + if (NDR_ERR_CODE_IS_SUCCESS(ndr_pull_echo_TestRef(ndr, NDR_OUT, &r))) + return 1; + + /* Windows gives [client runtime error 0x6f4] */ +'); + + +test_samba4_ndr("refptr-top-out-pull", +' + typedef struct { + uint16 x; + } xstruct; + + [public] void echo_TestRef([out,ref] xstruct *foo); +', +' + uint8_t data[] = { 0x0D, 0x00 }; + DATA_BLOB b = { data, 2 }; + struct ndr_pull *ndr = ndr_pull_init_blob(&b, NULL, NULL); + struct xstruct s; + struct echo_TestRef r; + + r.out.foo = &s; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_pull_echo_TestRef(ndr, NDR_OUT, &r))) + return 1; + + if (!r.out.foo) + return 2; + + if (r.out.foo->x != 13) + return 3; +'); + +test_samba4_ndr("refptr-top-out-pull-null", +' + typedef struct { + uint16 x; + } xstruct; + + [public] void echo_TestRef([out,ref] xstruct *foo); +', +' + uint8_t data[] = { 0x0D, 0x00 }; + DATA_BLOB b = { data, 2 }; + struct ndr_pull *ndr = ndr_pull_init_blob(&b, NULL, NULL); + struct echo_TestRef r; + + r.out.foo = NULL; + + if (NDR_ERR_CODE_IS_SUCCESS(ndr_pull_echo_TestRef(ndr, NDR_OUT, &r))) + return 1; + + /* Windows gives [client runtime error 0x6f4] */ +'); + + +test_samba4_ndr("ptr-top-push-double", +' + [public] void echo_TestRef([in] uint16 **foo); +', +' struct ndr_push *ndr = ndr_push_init_ctx(NULL, NULL); + struct echo_TestRef r; + uint16_t v = 13; + uint16_t *pv = &v; + r.in.foo = &pv; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_push_echo_TestRef(ndr, NDR_IN, &r))) + return 1; + + if (ndr->offset != 6) + return 2; + + if (ndr->data[0] == 0 && ndr->data[1] == 0 && + ndr->data[2] == 0 && ndr->data[3] == 0) + return 3; + + if (ndr->data[4] != 0x0D || ndr->data[5] != 0x00) + return 4; +'); + +SKIP: { + skip "ptr-top-push-double-sndnull is known to fail", 8; + +test_samba4_ndr("ptr-top-push-double-sndnull", +' + [public] void echo_TestRef([in] uint16 **foo); +', +' struct ndr_push *ndr = ndr_push_init_ctx(NULL, NULL); + struct echo_TestRef r; + uint16_t *pv = NULL; + r.in.foo = &pv; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_push_echo_TestRef(ndr, NDR_IN, &r))) + return 1; + + if (ndr->offset != 4) + return 2; + + if (ndr->data[0] != 0 || ndr->data[1] != 0 || + ndr->data[2] != 0 || ndr->data[3] != 0) + return 3; +'); +} + +test_samba4_ndr("ptr-top-push-double-fstnull", +' + [public] void echo_TestRef([in] uint16 **foo); +', +' struct ndr_push *ndr = ndr_push_init_ctx(NULL, NULL); + struct echo_TestRef r; + r.in.foo = NULL; + + if (NDR_ERR_CODE_IS_SUCCESS(ndr_push_echo_TestRef(ndr, NDR_IN, &r))) + return 1; + + /* Windows gives [client runtime error 0x6f4] */ + +'); + + +test_samba4_ndr("refptr-top-push-double", +' + [public] void echo_TestRef([in,ref] uint16 **foo); +', +' struct ndr_push *ndr = ndr_push_init_ctx(NULL, NULL); + struct echo_TestRef r; + uint16_t v = 13; + uint16_t *pv = &v; + r.in.foo = &pv; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_push_echo_TestRef(ndr, NDR_IN, &r))) + return 1; + + if (ndr->offset != 6) + return 2; + + if (ndr->data[0] == 0 && ndr->data[1] == 0 && + ndr->data[2] == 0 && ndr->data[3] == 0) + return 3; + + if (ndr->data[4] != 0x0D || ndr->data[5] != 0x00) + return 4; +'); + +SKIP: { + + skip "refptr-top-push-double-sndnull is known to fail", 8; + +test_samba4_ndr("refptr-top-push-double-sndnull", +' + [public] void echo_TestRef([in,ref] uint16 **foo); +', +' struct ndr_push *ndr = ndr_push_init_ctx(NULL, NULL); + struct echo_TestRef r; + uint16_t *pv = NULL; + r.in.foo = &pv; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_push_echo_TestRef(ndr, NDR_IN, &r))) + return 1; + + if (ndr->offset != 4) + return 2; + + if (ndr->data[0] != 0 || ndr->data[1] != 0 || + ndr->data[2] != 0 || ndr->data[3] != 0) + return 3; +'); +} + +test_samba4_ndr("refptr-top-push-double-fstnull", +' + [public] void echo_TestRef([in,ref] uint16 **foo); +', +' struct ndr_push *ndr = ndr_push_init_ctx(NULL, NULL); + struct echo_TestRef r; + r.in.foo = NULL; + + if (NDR_ERR_CODE_IS_SUCCESS(ndr_push_echo_TestRef(ndr, NDR_IN, &r))) + return 1; + + /* Windows gives [client runtime error 0x6f4] */ + +'); + +SKIP: { + skip "ignore-ptrs are not supported yet", 8; +test_samba4_ndr("ignore-ptr", +' + [public] void echo_TestRef([in,ignore] uint16 *foo, [in] uint16 *bar); +', +' struct ndr_push *ndr = ndr_push_init_ctx(NULL, NULL); + struct echo_TestRef r; + uint16_t v = 10; + r.in.foo = &v; + r.in.bar = &v; + + if (NDR_ERR_CODE_IS_SUCCESS(ndr_push_echo_TestRef(ndr, NDR_IN, &r))) + return 1; + + if (ndr->offset != 4) + return 2; +'); +} diff --git a/source/pidl/tests/ndr_represent.pl b/source/pidl/tests/ndr_represent.pl new file mode 100755 index 00000000000..af0f91e1f58 --- /dev/null +++ b/source/pidl/tests/ndr_represent.pl @@ -0,0 +1,71 @@ +#!/usr/bin/perl +# NDR represent_as() / transmit_as() tests +# (C) 2006 Jelmer Vernooij. Published under the GNU GPL +use strict; + +use Test::More tests => 2 * 8; +use FindBin qw($RealBin); +use lib "$RealBin"; +use Util qw(test_samba4_ndr); + +test_samba4_ndr('represent_as-simple', +' + void bla([in,represent_as(uint32)] uint8 x); +', +' + uint8_t expected[] = { 0x0D }; + DATA_BLOB in_blob = { expected, 1 }; + struct ndr_pull *ndr = ndr_pull_init_blob(&in_blob, NULL, NULL); + struct bla r; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_pull_bla(ndr, NDR_SCALARS|NDR_BUFFERS, &r))) + return 1; + + if (r.in.x != 13) + return 2; +', +' +enum ndr_err_code ndr_uint8_to_uint32(uint8_t from, uint32_t *to) +{ + *to = from; + return NDR_ERR_SUCCESS; +} + +enum ndr_err_code ndr_uint32_to_uint8(uint32_t from, uint8_t *to) +{ + *to = from; + return NDR_ERR_SUCCESS; +} +' +); + +test_samba4_ndr('transmit_as-simple', +' + void bla([in,transmit_as(uint32)] uint8 x); +', +' + uint8_t expected[] = { 0x0D }; + DATA_BLOB in_blob = { expected, 1 }; + struct ndr_pull *ndr = ndr_pull_init_blob(&in_blob, NULL, NULL); + struct bla r; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_pull_bla(ndr, NDR_SCALARS|NDR_BUFFERS, &r))) + return 1; + + if (r.in.x != 13) + return 2; +', +' +enum ndr_err_code ndr_uint8_to_uint32(uint8_t from, uint32_t *to) +{ + *to = from; + return NDR_ERR_SUCCESS; +} + +enum ndr_err_code ndr_uint32_to_uint8(uint32_t from, uint8_t *to) +{ + *to = from; + return NDR_ERR_SUCCESS; +} +' +); diff --git a/source/pidl/tests/ndr_simple.pl b/source/pidl/tests/ndr_simple.pl new file mode 100755 index 00000000000..15e07d56934 --- /dev/null +++ b/source/pidl/tests/ndr_simple.pl @@ -0,0 +1,28 @@ +#!/usr/bin/perl +# Some simple tests for pidl +# (C) 2005 Jelmer Vernooij <jelmer@samba.org> +# Published under the GNU General Public License +use strict; + +use Test::More tests => 8; +use FindBin qw($RealBin); +use lib "$RealBin"; +use Util qw(test_samba4_ndr); + +test_samba4_ndr("simple", "void Test(); ", +" + uint8_t data[] = { 0x02 }; + uint8_t result; + DATA_BLOB b; + struct ndr_pull *ndr; + + b.data = data; + b.length = 1; + ndr = ndr_pull_init_blob(&b, mem_ctx, NULL); + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_pull_uint8(ndr, NDR_SCALARS, &result))) + return 1; + + if (result != 0x02) + return 2; +"); diff --git a/source/pidl/tests/ndr_string.pl b/source/pidl/tests/ndr_string.pl new file mode 100755 index 00000000000..b6e64066d67 --- /dev/null +++ b/source/pidl/tests/ndr_string.pl @@ -0,0 +1,90 @@ +#!/usr/bin/perl +# String tests for pidl +# (C) 2005 Jelmer Vernooij <jelmer@samba.org> +# Published under the GNU General Public License +use strict; + +use Test::More tests => 3 * 8; +use FindBin qw($RealBin); +use lib "$RealBin"; +use Util qw(test_samba4_ndr); + +test_samba4_ndr("string-pull-empty", +' [public] void TestString([in,flag(STR_ASCII|LIBNDR_FLAG_STR_SIZE4)] string data);', +' + uint8_t data[] = { 0x00, 0x00, 0x00, 0x00 }; + DATA_BLOB b = { data, 4 }; + struct ndr_pull *ndr = ndr_pull_init_blob(&b, NULL, + smb_iconv_convenience_init(NULL, "ASCII", "UTF8", true)); + struct TestString r; + r.in.data = NULL; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_pull_TestString(ndr, NDR_IN, &r))) + return 1; + + if (r.in.data == NULL) + return 2; + + if (r.in.data[0] != 0) + return 3; +'); + +test_samba4_ndr("string-ascii-pull", +' + [public] void TestString([in,flag(STR_ASCII|LIBNDR_FLAG_STR_SIZE4)] string data); +', +' + uint8_t data[] = { 0x03, 0x00, 0x00, 0x00, + \'f\', \'o\', \'o\', 0 }; + DATA_BLOB b = { data, 8 }; + struct ndr_pull *ndr = ndr_pull_init_blob(&b, NULL, + smb_iconv_convenience_init(NULL, "ASCII", "UTF8", true)); + struct TestString r; + r.in.data = NULL; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_pull_TestString(ndr, NDR_IN, &r))) + return 1; + + if (r.in.data == NULL) + return 2; + + if (strncmp(r.in.data, "foo", 3) != 0) + return 3; + + if (r.in.data[4] != 0) + return 4; +'); + +SKIP: { + skip "doesn't seem to work yet", 8; + +test_samba4_ndr("string-out", +' + [public] void TestString([out,string,charset(UNIX)] uint8 **data); +', +' + uint8_t data[] = { 0x03, 0x00, 0x00, 0x00, + \'f\', \'o\', \'o\', 0 }; + DATA_BLOB b = { data, 8 }; + struct ndr_pull *ndr = ndr_pull_init_blob(&b, NULL, + smb_iconv_convenience_init(NULL, "ASCII", "UTF8", true)); + struct TestString r; + char *str = NULL; + r.out.data = &str; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_pull_TestString(ndr, NDR_IN, &r))) + return 1; + + if (r.out.data == NULL) + return 2; + + if (*r.out.data == NULL) + return 3; + + if (strncmp(r.out.data, "foo", 3) != 0) + return 4; + + if (r.out.data[4] != 0) + return 5; +'); +} diff --git a/source/pidl/tests/ndr_tagtype.pl b/source/pidl/tests/ndr_tagtype.pl new file mode 100755 index 00000000000..f171b33baf6 --- /dev/null +++ b/source/pidl/tests/ndr_tagtype.pl @@ -0,0 +1,66 @@ +#!/usr/bin/perl +# Support for tagged types +# (C) 2005 Jelmer Vernooij. Published under the GNU GPL +use strict; + +use Test::More tests => 3 * 8; +use FindBin qw($RealBin); +use lib "$RealBin"; +use Util qw(test_samba4_ndr); + +test_samba4_ndr('struct-notypedef', '[public] struct bla { uint8 x; }; ', +' + struct ndr_push *ndr = ndr_push_init_ctx(NULL, NULL); + struct bla r; + uint8_t expected[] = { 0x0D }; + DATA_BLOB expected_blob = { expected, 1 }; + DATA_BLOB result_blob; + r.x = 13; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_push_STRUCT_bla(ndr, NDR_SCALARS|NDR_BUFFERS, &r))) + return 1; + + result_blob = ndr_push_blob(ndr); + + if (data_blob_cmp(&result_blob, &expected_blob) != 0) + return 2; +'); + +test_samba4_ndr('struct-notypedef-used', '[public] struct bla { uint8 x; }; + [public] void myfn([in] struct bla r); ', +' + struct ndr_push *ndr = ndr_push_init_ctx(NULL, NULL); + struct myfn fn; + uint8_t expected[] = { 0x0D }; + DATA_BLOB expected_blob = { expected, 1 }; + DATA_BLOB result_blob; + fn.in.r.x = 13; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_push_myfn(ndr, NDR_IN, &fn))) + return 1; + + result_blob = ndr_push_blob(ndr); + + if (data_blob_cmp(&result_blob, &expected_blob) != 0) + return 2; +'); + + +test_samba4_ndr('struct-notypedef-embedded', 'struct bla { uint8 x; }; + [public] struct myst { struct bla r; }; ', +' + struct ndr_push *ndr = ndr_push_init_ctx(NULL, NULL); + struct myst st; + uint8_t expected[] = { 0x0D }; + DATA_BLOB expected_blob = { expected, 1 }; + DATA_BLOB result_blob; + st.r.x = 13; + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_push_STRUCT_myst(ndr, NDR_IN, &st))) + return 1; + + result_blob = ndr_push_blob(ndr); + + if (data_blob_cmp(&result_blob, &expected_blob) != 0) + return 2; +'); diff --git a/source/pidl/tests/parse_idl.pl b/source/pidl/tests/parse_idl.pl new file mode 100755 index 00000000000..4a6db4a924d --- /dev/null +++ b/source/pidl/tests/parse_idl.pl @@ -0,0 +1,164 @@ +#!/usr/bin/perl +# Some simple tests for pidls parsing routines +# (C) 2005 Jelmer Vernooij <jelmer@samba.org> +# Published under the GNU General Public License +use strict; + +use Test::More tests => 65 * 2 + 7; +use FindBin qw($RealBin); +use lib "$RealBin"; +use Util qw(test_errors); +use Parse::Pidl::IDL; +use Parse::Pidl::NDR; + +sub testok($$) +{ + my ($name, $data) = @_; + + test_errors("", sub { + my $pidl = Parse::Pidl::IDL::parse_string($data, "<$name>"); + ok (defined($pidl), $name); + }); +} + +sub testfail($$$) +{ + my ($name, $data, $error) = @_; + + test_errors($error, sub { + my $pidl = Parse::Pidl::IDL::parse_string($data, "<$name>"); + + ok ((not defined $pidl), $name); + }); +} + +testfail "unknowntag", "bla test {};", + "<unknowntag>:0: Syntax error near 'bla'\n"; +testok "test1", "interface test { void Test(); }; "; +testok "voidtest", "interface test { int Testx(void); }; "; +testfail "voidtest", "interface test { Test(); }; ", + "<voidtest>:0: Syntax error near '('\n"; +testok "argtest", "interface test { int Test(int a, long b, uint32 c); }; "; +testok "array1", "interface test { int Test(int a[]); };"; +testok "array2", "interface test { int Test(int a[2]); };"; +testok "array3", "interface test { int Test(int a[b]); };"; +testfail "array4", "interface test { int Test(int[] a); };", + "<array4>:0: Syntax error near '['\n"; +testok "ptr1", "interface test { int Test(int *a); };"; +testok "ptr2", "interface test { int Test(int **a); };"; +testok "ptr3", "interface test { int Test(int ***a); };"; +testfail "empty1", "interface test { };", "<empty1>:0: Syntax error near '}'\n"; +testfail "empty2", "", ""; +testok "attr1", "[uuid(\"myuuid\"),attr] interface test { int Test(int ***a); };"; +testok "attr2", "interface test { [public] int Test(); };"; +testok "attr3", "[attr1] [attr2] interface test { [public] int Test(); };"; +testok "multfn", "interface test { int test1(); int test2(); };"; +testok "multif", "interface test { int test1(); }; interface test2 { int test2(); };"; +testok "tdstruct1", "interface test { typedef struct { } foo; };"; +testok "tdstruct2", "interface test { typedef struct { int a; } foo; };"; +testok "tdstruct3", "interface test { typedef struct { int a; int b; } foo; };"; +testfail "tdstruct4", "interface test { typedef struct { int a, int b; } foo; };", + "<tdstruct4>:0: Syntax error near ','\n"; +testok "struct1", "interface test { struct x { }; };"; +testok "struct2", "interface test { struct x { int a; }; };"; +testok "struct3", "interface test { struct x { int a; int b; }; };"; +testfail "struct4", "interface test { struct x { int a, int b; }; };", + "<struct4>:0: Syntax error near ','\n"; +testfail "struct5", "interface test { struct { int a; } x; };", + "<struct5>:0: Syntax error near 'x'\n"; +testok "tdunion1", "interface test { typedef union { } a; };"; +testok "tdunion2", "interface test { typedef union { int a; } a; };"; +testok "union1", "interface test { union a { }; };"; +testok "union2", "interface test { union x { int a; }; };"; +testfail "union3", "interface test { union { int a; } x; };", + "<union3>:0: Syntax error near 'x'\n"; +testok "typedef1", "interface test { typedef int a; };"; +testfail "typedef2", "interface test { typedef x; };", + "<typedef2>:0: Syntax error near ';'\n"; +testok "tdenum1", "interface test { typedef enum { A=1, B=2, C} a; };"; +testok "enum1", "interface test { enum a { A=1, B=2, C}; };"; +testfail "enum2", "interface test { enum { A=1, B=2, C} a; };", + "<enum2>:0: Syntax error near 'a'\n"; +testok "nested1", "interface test { struct x { struct { int a; } z; }; };"; +testok "nested2", "interface test { struct x { struct y { int a; } z; }; };"; +testok "bitmap1", "interface test { bitmap x { a=1 }; };"; +testok "unsigned", "interface test { struct x { unsigned short y; }; };"; +testok "struct-property", "interface test { [public] struct x { short y; }; };"; +testok "signed", "interface test { struct x { signed short y; }; };"; +testok "declarg", "interface test { void test(struct { int x; } a); };"; +testok "structarg", "interface test { void test(struct a b); };"; +testfail "structargmissing", "interface test { void test(struct a); };", + "<structargmissing>:0: Syntax error near ')'\n"; +testok "structqual", "interface test { struct x { struct y z; }; };"; +testok "unionqual", "interface test { struct x { union y z; }; };"; +testok "enumqual", "interface test { struct x { enum y z; }; };"; +testok "bitmapqual", "interface test { struct x { bitmap y z; }; };"; +testok "emptystructdecl", "interface test { struct x; };"; +testok "emptyenumdecl", "interface test { enum x; };"; +testok "emptytdstructdecl", "interface test { typedef struct x y; };"; +testok "import", "import \"foo.idl\";"; +testok "include", "include \"foo.h\";"; +testfail "import-noquotes", "import foo.idl;", + "<import-noquotes>:0: Syntax error near 'foo'\n"; +testfail "include-noquotes", "include foo.idl;", + "<include-noquotes>:0: Syntax error near 'foo'\n"; +testok "importlib", "importlib \"foo.idl\";"; +testfail "import-nosemicolon", "import \"foo.idl\"", + "<import-nosemicolon>:0: Syntax error near 'foo.idl'\n"; +testok "import-multiple", "import \"foo.idl\", \"bar.idl\";"; +testok "include-multiple", "include \"foo.idl\", \"bar.idl\";"; +testok "empty-struct", "interface test { struct foo { }; }"; +testok "typedef-double", "interface test { typedef struct foo { } foo; }"; +testok "cpp-quote", "cpp_quote(\"bla\")"; + +my $x = Parse::Pidl::IDL::parse_string("interface foo { struct x {}; }", "<foo>"); + +is_deeply($x, + [ { 'FILE' => '<foo>', 'NAME' => 'foo', 'DATA' => [ + { 'NAME' => 'x', 'TYPE' => 'STRUCT', ELEMENTS => [] } ], + 'TYPE' => 'INTERFACE', 'LINE' => 0 } ]); + +$x = Parse::Pidl::IDL::parse_string("interface foo { struct x; }", "<foo>"); +is_deeply($x, + [ { 'FILE' => '<foo>', 'NAME' => 'foo', 'DATA' => [ + { 'NAME' => 'x', 'TYPE' => 'STRUCT' } ], + 'TYPE' => 'INTERFACE', 'LINE' => 0 } ]); + +$x = Parse::Pidl::IDL::parse_string("cpp_quote(\"foobar\")", "<quote>"); +is_deeply($x, + [ { 'FILE' => '<quote>', 'DATA' => '"foobar"', + 'TYPE' => 'CPP_QUOTE', 'LINE' => 0 } ]); + +# A typedef of a struct without body +$x = Parse::Pidl::IDL::parse_string("interface foo { typedef struct x y; }", "<foo>"); + +is_deeply($x, + [ { 'FILE' => '<foo>', 'NAME' => 'foo', 'DATA' => [ + { 'FILE' => '<foo>', 'LINE' => 0, 'NAME' => 'y', 'TYPE' => 'TYPEDEF', DATA => { + TYPE => 'STRUCT', NAME => 'x' } } ], + 'TYPE' => 'INTERFACE', 'LINE' => 0 } ]); + +# A typedef of a struct with empty body +$x = Parse::Pidl::IDL::parse_string("interface foo { typedef struct {} y; }", "<foo>"); + +is_deeply($x, + [ { 'FILE' => '<foo>', 'NAME' => 'foo', 'DATA' => [ + { 'FILE' => '<foo>', 'LINE' => 0, 'NAME' => 'y', 'TYPE' => 'TYPEDEF', DATA => { TYPE => 'STRUCT', ELEMENTS => [] } } ], + 'TYPE' => 'INTERFACE', 'LINE' => 0 } ]); + +# A typedef of a bitmap with no body +$x = Parse::Pidl::IDL::parse_string("interface foo { typedef bitmap x y; }", "<foo>"); + +is_deeply($x, + [ { 'FILE' => '<foo>', 'NAME' => 'foo', 'DATA' => [ + { 'FILE' => '<foo>', 'LINE' => 0, 'NAME' => 'y', 'TYPE' => 'TYPEDEF', DATA => { TYPE => 'BITMAP', NAME => 'x' } } ], + 'TYPE' => 'INTERFACE', 'LINE' => 0 } ]); + + +# A typedef of a union with no body +$x = Parse::Pidl::IDL::parse_string("interface foo { typedef union x y; }", "<foo>"); + +is_deeply($x, + [ { 'FILE' => '<foo>', 'NAME' => 'foo', 'DATA' => [ + { 'FILE' => '<foo>', 'LINE' => 0, 'NAME' => 'y', 'TYPE' => 'TYPEDEF', DATA => { TYPE => 'UNION', NAME => 'x' } } ], + 'TYPE' => 'INTERFACE', 'LINE' => 0 } ]); diff --git a/source/pidl/tests/samba-ejs.pl b/source/pidl/tests/samba-ejs.pl new file mode 100755 index 00000000000..094d37a103b --- /dev/null +++ b/source/pidl/tests/samba-ejs.pl @@ -0,0 +1,37 @@ +#!/usr/bin/perl +# (C) 2007 Jelmer Vernooij <jelmer@samba.org> +# Published under the GNU General Public License +use strict; +use warnings; + +use Test::More tests => 10; +use FindBin qw($RealBin); +use lib "$RealBin"; +use Util; +use Parse::Pidl::Util qw(MyDumper); +use Parse::Pidl::Samba4::EJS qw(check_null_pointer + fn_declare TypeFunctionName); + +my $ejs = new Parse::Pidl::Samba4::EJS(); + +$ejs->check_null_pointer("bla"); +is($ejs->{res}, ""); + +$ejs = new Parse::Pidl::Samba4::EJS(); +$ejs->check_null_pointer("*bla"); +is($ejs->{res}, "if (bla == NULL) return NT_STATUS_INVALID_PARAMETER_MIX;\n"); + +$ejs = new Parse::Pidl::Samba4::EJS(); +$ejs->fn_declare({ PROPERTIES => { public => 1 } }, "myproto(int x)"); +is($ejs->{res}, "_PUBLIC_ myproto(int x)\n"); +is($ejs->{res_hdr}, "myproto(int x);\n"); + +$ejs = new Parse::Pidl::Samba4::EJS(); +$ejs->fn_declare({ PROPERTIES => {} }, "mybla(int foo)"); +is($ejs->{res}, "static mybla(int foo)\n"); +is($ejs->{res_hdr}, ""); + +is(TypeFunctionName("ejs_pull", "uint32"), "ejs_pull_uint32"); +is(TypeFunctionName("ejs_pull", {TYPE => "ENUM", NAME => "bar"}), "ejs_pull_ENUM_bar"); +is(TypeFunctionName("ejs_pull", {TYPE => "TYPEDEF", NAME => "bar", DATA => undef}), "ejs_pull_bar"); +is(TypeFunctionName("ejs_push", {TYPE => "STRUCT", NAME => "bar"}), "ejs_push_STRUCT_bar"); diff --git a/source/pidl/tests/samba-ndr.pl b/source/pidl/tests/samba-ndr.pl new file mode 100755 index 00000000000..59f046b171e --- /dev/null +++ b/source/pidl/tests/samba-ndr.pl @@ -0,0 +1,293 @@ +#!/usr/bin/perl +# (C) 2007 Jelmer Vernooij <jelmer@samba.org> +# Published under the GNU General Public License +use strict; +use warnings; + +use Test::More tests => 31; +use FindBin qw($RealBin); +use lib "$RealBin"; +use Util; +use strict; +use Parse::Pidl::Util qw(MyDumper); +use Parse::Pidl::Samba4::NDR::Parser qw(check_null_pointer + NeededFunction NeededElement NeededType + NeededInterface TypeFunctionName ParseElementPrint); + +my $output; +sub print_fn($) { my $x = shift; $output.=$x; } + +# Test case 1: Simple unique pointer dereference + +$output = ""; +my $fn = check_null_pointer({ + PARENT => { + ELEMENTS => [ + { + NAME => "bla", + LEVELS => [ + { TYPE => "POINTER", + POINTER_INDEX => 0, + POINTER_TYPE => "unique" }, + { TYPE => "DATA" } + ], + }, + ] + } +}, { bla => "r->in.bla" }, \&print_fn, "return;"); + + +test_warnings("", sub { $fn->("r->in.bla"); }); + +is($output, "if (r->in.bla == NULL) return;"); + +# Test case 2: Simple ref pointer dereference + +$output = ""; +$fn = check_null_pointer({ + PARENT => { + ELEMENTS => [ + { + NAME => "bla", + LEVELS => [ + { TYPE => "POINTER", + POINTER_INDEX => 0, + POINTER_TYPE => "ref" }, + { TYPE => "DATA" } + ], + }, + ] + } +}, { bla => "r->in.bla" }, \&print_fn, undef); + +test_warnings("", sub { $fn->("r->in.bla"); }); + +is($output, ""); + +# Test case 3: Illegal dereference + +$output = ""; +$fn = check_null_pointer({ + FILE => "nofile", + LINE => 1, + PARENT => { + ELEMENTS => [ + { + NAME => "bla", + LEVELS => [ + { TYPE => "DATA" } + ], + }, + ] + } +}, { bla => "r->in.bla" }, \&print_fn, undef); + +test_warnings("nofile:1: too much dereferences for `bla'\n", + sub { $fn->("r->in.bla"); }); + +is($output, ""); + +# Test case 4: Double pointer dereference + +$output = ""; +$fn = check_null_pointer({ + PARENT => { + ELEMENTS => [ + { + NAME => "bla", + LEVELS => [ + { TYPE => "POINTER", + POINTER_INDEX => 0, + POINTER_TYPE => "unique" }, + { TYPE => "POINTER", + POINTER_INDEX => 1, + POINTER_TYPE => "unique" }, + { TYPE => "DATA" } + ], + }, + ] + } +}, { bla => "r->in.bla" }, \&print_fn, "return;"); + +test_warnings("", + sub { $fn->("*r->in.bla"); }); + +is($output, "if (*r->in.bla == NULL) return;"); + +# Test case 5: Unknown variable + +$output = ""; +$fn = check_null_pointer({ + FILE => "nofile", + LINE => 2, + PARENT => { + ELEMENTS => [ + { + NAME => "bla", + LEVELS => [ + { TYPE => "DATA" } + ], + }, + ] + } +}, { }, \&print_fn, "return;"); + +test_warnings("nofile:2: unknown dereferenced expression `r->in.bla'\n", + sub { $fn->("r->in.bla"); }); + +is($output, "if (r->in.bla == NULL) return;"); + +my $needed = {}; +NeededElement({ TYPE => "foo", REPRESENTATION_TYPE => "foo" }, "pull", $needed); +is_deeply($needed, { ndr_pull_foo => 1 }); + +# old settings should be kept +$needed = { ndr_pull_foo => 0 }; +NeededElement({ TYPE => "foo", REPRESENTATION_TYPE => "foo" }, "pull", $needed); +is_deeply($needed, { ndr_pull_foo => 0 }); + +# print/pull/push are independent of each other +$needed = { ndr_pull_foo => 0 }; +NeededElement({ TYPE => "foo", REPRESENTATION_TYPE => "foo" }, "print", $needed); +is_deeply($needed, { ndr_pull_foo => 0, ndr_print_foo => 1 }); + +$needed = { }; +NeededFunction({ NAME => "foo", ELEMENTS => [ { TYPE => "bar", REPRESENTATION_TYPE => "bar" } ] }, $needed); +is_deeply($needed, { ndr_pull_foo => 1, ndr_print_foo => 1, ndr_push_foo => 1, + ndr_pull_bar => 1, ndr_print_bar => 1, ndr_push_bar => 1}); + +# push/pull/print are always set for functions +$needed = { ndr_pull_foo => 0 }; +NeededFunction({ NAME => "foo", ELEMENTS => [ { TYPE => "bar", REPRESENTATION_TYPE => "bar" } ] }, $needed); +is_deeply($needed, { ndr_pull_foo => 1, ndr_print_foo => 1, ndr_push_foo => 1, + ndr_pull_bar => 1, ndr_push_bar => 1, ndr_print_bar => 1}); + +# public structs are always needed +$needed = {}; +NeededType({ NAME => "bla", TYPE => "TYPEDEF", + DATA => { TYPE => "STRUCT", ELEMENTS => [] } }, + $needed, "pull"); +is_deeply($needed, { }); + +$needed = {}; +NeededInterface({ TYPES => [ { PROPERTIES => { public => 1 }, NAME => "bla", + TYPE => "TYPEDEF", + DATA => { TYPE => "STRUCT", ELEMENTS => [] } } ] }, + $needed); +is_deeply($needed, { ndr_pull_bla => 1, ndr_push_bla => 1, ndr_print_bla => 1 }); + +# make sure types for elements are set too +$needed = {}; +NeededInterface({ TYPES => [ { PROPERTIES => { public => 1 }, NAME => "bla", + TYPE => "TYPEDEF", + DATA => { TYPE => "STRUCT", + ELEMENTS => [ { TYPE => "bar", REPRESENTATION_TYPE => "bar" } ] } } ] }, + $needed); +is_deeply($needed, { ndr_pull_bla => 1, ndr_pull_bar => 1, ndr_push_bla => 1, ndr_push_bar => 1, + ndr_print_bla => 1, ndr_print_bar => 1}); + +$needed = {}; +NeededInterface({ TYPES => [ { PROPERTIES => { gensize => 1}, NAME => "bla", + TYPE => "TYPEDEF", + DATA => { TYPE => "STRUCT", + ELEMENTS => [ { TYPE => "bar", REPRESENTATION_TYPE => "bar" } ] } } ] }, + $needed); +is_deeply($needed, { ndr_size_bla => 1 }); + +# make sure types for elements are set too +$needed = { ndr_pull_bla => 1 }; +NeededType({ NAME => "bla", + TYPE => "TYPEDEF", + DATA => { TYPE => "STRUCT", + ELEMENTS => [ { TYPE => "bar", REPRESENTATION_TYPE => "bar" } ] } }, + $needed, "pull"); +is_deeply($needed, { ndr_pull_bla => 1, ndr_pull_bar => 1 }); + +$needed = {}; +NeededInterface({ TYPES => [ { PROPERTIES => { public => 1}, + NAME => "bla", + TYPE => "TYPEDEF", + DATA => { TYPE => "STRUCT", + ELEMENTS => [ { TYPE => "bar", REPRESENTATION_TYPE => "rep" } ] } } ] }, $needed); +is_deeply($needed, { ndr_pull_bla => 1, ndr_push_bla => 1, ndr_print_bla => 1, + ndr_print_rep => 1, + ndr_pull_bar => 1, ndr_push_bar => 1, + ndr_bar_to_rep => 1, ndr_rep_to_bar => 1}); + +my $generator = new Parse::Pidl::Samba4::NDR::Parser(); +$generator->ParseStructPush({ + NAME => "mystruct", + TYPE => "STRUCT", + PROPERTIES => {}, + ALIGN => 4, + ELEMENTS => [ ]}, "x"); +is($generator->{res}, "if (ndr_flags & NDR_SCALARS) { + NDR_CHECK(ndr_push_align(ndr, 4)); +} +if (ndr_flags & NDR_BUFFERS) { +} +"); + +$generator = new Parse::Pidl::Samba4::NDR::Parser(); +my $e = { + NAME => "el1", + TYPE => "mytype", + REPRESENTATION_TYPE => "mytype", + PROPERTIES => {}, + LEVELS => [ + { LEVEL_INDEX => 0, TYPE => "DATA", DATA_TYPE => "mytype" } +] }; +$generator->ParseStructPush({ + NAME => "mystruct", + TYPE => "STRUCT", + PROPERTIES => {}, + ALIGN => 4, + SURROUNDING_ELEMENT => $e, + ELEMENTS => [ $e ]}, "x"); +is($generator->{res}, "if (ndr_flags & NDR_SCALARS) { + NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, ndr_string_array_size(ndr, x->el1))); + NDR_CHECK(ndr_push_align(ndr, 4)); + NDR_CHECK(ndr_push_mytype(ndr, NDR_SCALARS, &x->el1)); +} +if (ndr_flags & NDR_BUFFERS) { +} +"); + +is(TypeFunctionName("ndr_pull", "uint32"), "ndr_pull_uint32"); +is(TypeFunctionName("ndr_pull", {TYPE => "ENUM", NAME => "bar"}), "ndr_pull_ENUM_bar"); +is(TypeFunctionName("ndr_pull", {TYPE => "TYPEDEF", NAME => "bar", DATA => undef}), "ndr_pull_bar"); +is(TypeFunctionName("ndr_push", {TYPE => "STRUCT", NAME => "bar"}), "ndr_push_STRUCT_bar"); + +# check noprint works +$generator = new Parse::Pidl::Samba4::NDR::Parser(); +$generator->ParseElementPrint({ NAME => "x", TYPE => "rt", REPRESENTATION_TYPE => "rt", + PROPERTIES => { noprint => 1}, + LEVELS => [ { TYPE => "DATA", DATA_TYPE => "rt"} ]}, "var", { "x" => "r->foobar" } ); +is($generator->{res}, ""); + +$generator = new Parse::Pidl::Samba4::NDR::Parser(); +$generator->ParseElementPrint({ NAME => "x", TYPE => "rt", REPRESENTATION_TYPE => "rt", + PROPERTIES => {}, + LEVELS => [ { TYPE => "DATA", DATA_TYPE => "rt" }]}, "var", { "x" => "r->foobar" } ); +is($generator->{res}, "ndr_print_rt(ndr, \"x\", &var);\n"); + +# make sure that a print function for an element with value() set works +$generator = new Parse::Pidl::Samba4::NDR::Parser(); +$generator->ParseElementPrint({ NAME => "x", TYPE => "uint32", REPRESENTATION_TYPE => "uint32", + PROPERTIES => { value => "23" }, + LEVELS => [ { TYPE => "DATA", DATA_TYPE => "uint32"} ]}, "var", { "x" => "r->foobar" } ); +is($generator->{res}, "ndr_print_uint32(ndr, \"x\", (ndr->flags & LIBNDR_PRINT_SET_VALUES)?23:var);\n"); + +$generator = new Parse::Pidl::Samba4::NDR::Parser(); +$generator->AuthServiceStruct("bridge", "\"rot13\",\"onetimepad\""); +is($generator->{res}, "static const char * const bridge_authservice_strings[] = { + \"rot13\", + \"onetimepad\", +}; + +static const struct ndr_interface_string_array bridge_authservices = { + .count = 2, + .names = bridge_authservice_strings +}; + +"); diff --git a/source/pidl/tests/samba3-cli.pl b/source/pidl/tests/samba3-cli.pl new file mode 100755 index 00000000000..dd494706a50 --- /dev/null +++ b/source/pidl/tests/samba3-cli.pl @@ -0,0 +1,128 @@ +#!/usr/bin/perl +# (C) 2007 Jelmer Vernooij <jelmer@samba.org> +# Published under the GNU General Public License +use strict; +use warnings; + +use Test::More tests => 9; +use FindBin qw($RealBin); +use lib "$RealBin"; +use Util; +use Parse::Pidl::Util qw(MyDumper); +use Parse::Pidl::Samba3::ClientNDR qw(ParseFunction ParseOutputArgument); +use Parse::Pidl::Samba4::Header qw(GenerateFunctionInEnv GenerateFunctionOutEnv); + +# Make sure GenerateFunctionInEnv and GenerateFunctionOutEnv work +my $fn = { ELEMENTS => [ { DIRECTION => ["in"], NAME => "foo" } ] }; +is_deeply({ "foo" => "r.in.foo" }, GenerateFunctionInEnv($fn, "r.")); +is_deeply({ "foo" => "r.in.foo" }, GenerateFunctionOutEnv($fn, "r.")); + +$fn = { ELEMENTS => [ { DIRECTION => ["out", "in"], NAME => "foo" } ] }; +is_deeply({ "foo" => "r.in.foo" }, GenerateFunctionInEnv($fn, "r.")); +is_deeply({ "foo" => "r.out.foo" }, GenerateFunctionOutEnv($fn, "r.")); + +$fn = { ELEMENTS => [ { DIRECTION => ["out"], NAME => "foo" } ] }; +is_deeply({ }, GenerateFunctionInEnv($fn, "r.")); +is_deeply({ "foo" => "r.out.foo" }, GenerateFunctionOutEnv($fn, "r.")); + +my $x = new Parse::Pidl::Samba3::ClientNDR(); + +$fn = { NAME => "bar", ELEMENTS => [ ] }; +$x->ParseFunction("foo", $fn); +is($x->{res}, +"NTSTATUS rpccli_bar(struct rpc_pipe_client *cli, + TALLOC_CTX *mem_ctx) +{ +\tstruct bar r; +\tNTSTATUS status; + +\t/* In parameters */ + +\tif (DEBUGLEVEL >= 10) { +\t\tNDR_PRINT_IN_DEBUG(bar, &r); +\t} + + status = cli_do_rpc_ndr(cli, + mem_ctx, + PI_FOO, + &ndr_table_foo, + NDR_BAR, + &r); + +\tif (!NT_STATUS_IS_OK(status)) { +\t\treturn status; +\t} + +\tif (DEBUGLEVEL >= 10) { +\t\tNDR_PRINT_OUT_DEBUG(bar, &r); +\t} + +\tif (NT_STATUS_IS_ERR(status)) { +\t\treturn status; +\t} + +\t/* Return variables */ + +\t/* Return result */ +\treturn NT_STATUS_OK; +} + +"); + +$x = new Parse::Pidl::Samba3::ClientNDR(); + +$fn = { NAME => "bar", ELEMENTS => [ ], RETURN_TYPE => "WERROR" }; +$x->ParseFunction("foo", $fn); +is($x->{res}, +"NTSTATUS rpccli_bar(struct rpc_pipe_client *cli, + TALLOC_CTX *mem_ctx, + WERROR *werror) +{ +\tstruct bar r; +\tNTSTATUS status; + +\t/* In parameters */ + +\tif (DEBUGLEVEL >= 10) { +\t\tNDR_PRINT_IN_DEBUG(bar, &r); +\t} + + status = cli_do_rpc_ndr(cli, + mem_ctx, + PI_FOO, + &ndr_table_foo, + NDR_BAR, + &r); + +\tif (!NT_STATUS_IS_OK(status)) { +\t\treturn status; +\t} + +\tif (DEBUGLEVEL >= 10) { +\t\tNDR_PRINT_OUT_DEBUG(bar, &r); +\t} + +\tif (NT_STATUS_IS_ERR(status)) { +\t\treturn status; +\t} + +\t/* Return variables */ + +\t/* Return result */ +\tif (werror) { +\t\t*werror = r.out.result; +\t} + +\treturn werror_to_ntstatus(r.out.result); +} + +"); + +$x = new Parse::Pidl::Samba3::ClientNDR(); + +$fn = { NAME => "bar", ELEMENTS => [ ], RETURN_TYPE => "WERROR" }; +my $e = { NAME => "foo", ORIGINAL => { FILE => "f", LINE => -1 }, + LEVELS => [ { TYPE => "ARRAY", SIZE_IS => "mysize" }, { TYPE => "DATA", DATA_TYPE => "int" } ]}; + +$x->ParseOutputArgument($fn, $e); +is($x->{res}, "memcpy(foo, r.out.foo, mysize * sizeof(*foo));\n"); diff --git a/source/pidl/tests/samba3-srv.pl b/source/pidl/tests/samba3-srv.pl new file mode 100644 index 00000000000..ba3903058cc --- /dev/null +++ b/source/pidl/tests/samba3-srv.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +# (C) 2008 Jelmer Vernooij <jelmer@samba.org> +# Published under the GNU General Public License +use strict; +use warnings; + +use Test::More tests => 1; +use FindBin qw($RealBin); +use lib "$RealBin"; +use Util; +use Parse::Pidl::Util qw(MyDumper has_property); +use Parse::Pidl::Samba3::ServerNDR qw(DeclLevel); + +my $l = { TYPE => "DATA", DATA_TYPE => "uint32" }; +my $e = { FILE => "foo", LINE => 0, PROPERTIES => { }, TYPE => "uint32", + LEVELS => [ $l ] }; + +is("uint32_t", DeclLevel($e, 0)); diff --git a/source/pidl/tests/tdr.pl b/source/pidl/tests/tdr.pl new file mode 100755 index 00000000000..506999f99f4 --- /dev/null +++ b/source/pidl/tests/tdr.pl @@ -0,0 +1,49 @@ +#!/usr/bin/perl +# (C) 2007 Jelmer Vernooij <jelmer@samba.org> +# Published under the GNU General Public License +use strict; +use warnings; + +use Test::More tests => 6; +use FindBin qw($RealBin); +use lib "$RealBin"; +use Util; +use Parse::Pidl::Samba4::TDR qw(ParserType); + +my $tdr = new Parse::Pidl::Samba4::TDR(); + +$tdr->ParserType({TYPE => "STRUCT", NAME => "foo", PROPERTIES => {public => 1}}, "pull"); +is($tdr->{ret}, "NTSTATUS tdr_pull_foo (struct tdr_pull *tdr, TALLOC_CTX *mem_ctx, struct foo *v) +{ + return NT_STATUS_OK; +} + +"); +is($tdr->{ret_hdr}, "NTSTATUS tdr_pull_foo (struct tdr_pull *tdr, TALLOC_CTX *mem_ctx, struct foo *v);\n"); + + +$tdr = new Parse::Pidl::Samba4::TDR(); +$tdr->ParserType({TYPE => "UNION", NAME => "bar", PROPERTIES => {public => 1}}, "pull"); +is($tdr->{ret}, "NTSTATUS tdr_pull_bar(struct tdr_pull *tdr, TALLOC_CTX *mem_ctx, int level, union bar *v) +{ + switch (level) { + } + return NT_STATUS_OK; + +} + +"); +is($tdr->{ret_hdr}, "NTSTATUS tdr_pull_bar(struct tdr_pull *tdr, TALLOC_CTX *mem_ctx, int level, union bar *v);\n"); + +$tdr = new Parse::Pidl::Samba4::TDR(); +$tdr->ParserType({TYPE => "UNION", NAME => "bar", PROPERTIES => {}}, "pull"); +is($tdr->{ret}, "static NTSTATUS tdr_pull_bar(struct tdr_pull *tdr, TALLOC_CTX *mem_ctx, int level, union bar *v) +{ + switch (level) { + } + return NT_STATUS_OK; + +} + +"); +is($tdr->{ret_hdr}, ""); diff --git a/source/pidl/tests/test_util.pl b/source/pidl/tests/test_util.pl new file mode 100755 index 00000000000..2d59f6283bb --- /dev/null +++ b/source/pidl/tests/test_util.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +# (C) 2007 Jelmer Vernooij <jelmer@samba.org> +# Published under the GNU General Public License +use strict; + +use Test::More tests => 6; +use FindBin qw($RealBin); +use lib "$RealBin"; +use Util qw(test_warnings test_errors); +use Parse::Pidl qw(warning error); + +test_warnings("", sub {}); + +test_warnings("x:1: msg\n", sub { warning({FILE => "x", LINE => 1}, "msg"); }); +test_warnings("", sub {}); + +test_errors("", sub {}); + +test_errors("x:1: msg\n", sub { error({FILE => "x", LINE => 1}, "msg"); }); +test_errors("", sub {}); + diff --git a/source/pidl/tests/typelist.pl b/source/pidl/tests/typelist.pl new file mode 100755 index 00000000000..54f4d345865 --- /dev/null +++ b/source/pidl/tests/typelist.pl @@ -0,0 +1,85 @@ +#!/usr/bin/perl +# (C) 2007 Jelmer Vernooij <jelmer@samba.org> +# Published under the GNU General Public License +use strict; +use warnings; + +use Test::More tests => 54; +use FindBin qw($RealBin); +use lib "$RealBin"; +use Util; +use Parse::Pidl::Typelist qw(hasType typeHasBody getType mapTypeName expandAlias + mapScalarType addType typeIs is_scalar scalar_is_reference + enum_type_fn bitmap_type_fn mapType); + +is("foo", expandAlias("foo")); +is("uint32", expandAlias("DWORD")); +is("int32", expandAlias("int")); +is("", expandAlias("")); +is("int32", expandAlias("int32")); + +is("uint32_t", mapScalarType("uint32")); +is("void", mapScalarType("void")); +is("uint64_t", mapScalarType("hyper")); + +my $x = { TYPE => "ENUM", NAME => "foo", EXTRADATA => 1 }; +addType($x); +is_deeply($x, getType("foo")); +is(undef, getType("bloebla")); +is_deeply(getType({ TYPE => "STRUCT" }), { TYPE => "STRUCT" }); +is_deeply(getType({ TYPE => "ENUM", NAME => "foo" }), $x); +is_deeply(getType("uint16"), { + NAME => "uint16", + TYPE => "TYPEDEF", + DATA => { NAME => "uint16", TYPE => "SCALAR" }}); + +is(0, typeIs("someUnknownType", "ENUM")); +is(0, typeIs("foo", "ENUM")); +addType({NAME => "mytypedef", TYPE => "TYPEDEF", DATA => { TYPE => "ENUM" }}); +is(1, typeIs("mytypedef", "ENUM")); +is(0, typeIs("mytypedef", "BITMAP")); +is(1, typeIs({ TYPE => "ENUM"}, "ENUM")); +is(0, typeIs({ TYPE => "BITMAP"}, "ENUM")); +is(1, typeIs("uint32", "SCALAR")); +is(0, typeIs("uint32", "ENUM")); + +is(1, hasType("foo")); +is(0, hasType("nonexistant")); +is(0, hasType({TYPE => "ENUM", NAME => "someUnknownType"})); +is(1, hasType({TYPE => "ENUM", NAME => "foo"})); +is(1, hasType({TYPE => "ENUM"})); +is(1, hasType({TYPE => "STRUCT"})); + +is(1, is_scalar("uint32")); +is(0, is_scalar("nonexistant")); +is(1, is_scalar({TYPE => "ENUM"})); +is(0, is_scalar({TYPE => "STRUCT"})); +is(1, is_scalar({TYPE => "TYPEDEF", DATA => {TYPE => "ENUM" }})); +is(1, is_scalar("mytypedef")); + +is(1, scalar_is_reference("string")); +is(0, scalar_is_reference("uint32")); +is(0, scalar_is_reference({TYPE => "STRUCT", NAME => "echo_foobar"})); + +is("uint8", enum_type_fn({TYPE => "ENUM", PARENT=>{PROPERTIES => {enum8bit => 1}}})); +is("uint32", enum_type_fn({TYPE => "ENUM", PARENT=>{PROPERTIES => {v1_enum => 1}}})); +is("uint16", enum_type_fn({TYPE => "ENUM", PARENT=>{PROPERTIES => {}}})); + +is("uint8", bitmap_type_fn({TYPE => "BITMAP", PROPERTIES => {bitmap8bit => 1}})); +is("uint16", bitmap_type_fn({TYPE => "BITMAP", PROPERTIES => {bitmap16bit => 1}})); +is("hyper", bitmap_type_fn({TYPE => "BITMAP", PROPERTIES => {bitmap64bit => 1}})); +is("uint32", bitmap_type_fn({TYPE => "BITMAP", PROPERTIES => {}})); + +is("enum foo", mapType({TYPE => "ENUM"}, "foo")); +is("union foo", mapType({TYPE => "UNION"}, "foo")); +is("struct foo", mapType({TYPE => "STRUCT"}, "foo")); +is("uint8_t", mapType({TYPE => "BITMAP", PROPERTIES => {bitmap8bit => 1}}, "foo")); +is("uint8_t", mapType({TYPE => "SCALAR"}, "uint8")); +is("uint32_t", mapType({TYPE => "TYPEDEF", DATA => {TYPE => "SCALAR"}}, "uint32")); + +is("void", mapTypeName(undef)); +is("uint32_t", mapTypeName("uint32")); +is("int32_t", mapTypeName("int")); + +ok(not typeHasBody({TYPE => "TYPEDEF", DATA => { TYPE => "STRUCT" }})); +ok(typeHasBody({TYPE => "TYPEDEF", DATA => { TYPE => "STRUCT", ELEMENTS => [] }})); diff --git a/source/pidl/tests/util.pl b/source/pidl/tests/util.pl new file mode 100755 index 00000000000..d4d4244e9e3 --- /dev/null +++ b/source/pidl/tests/util.pl @@ -0,0 +1,115 @@ +#!/usr/bin/perl +# (C) 2007 Jelmer Vernooij <jelmer@samba.org> +# Published under the GNU General Public License +use strict; +use warnings; + +use Test::More tests => 72; +use FindBin qw($RealBin); +use lib "$RealBin"; +use Util; +use Parse::Pidl qw(error); +use Parse::Pidl::Util; + +# has_property() +is(undef, has_property({}, "foo")); +is(undef, has_property({PROPERTIES => {}}, "foo")); +is("data", has_property({PROPERTIES => {foo => "data"}}, "foo")); +is(undef, has_property({PROPERTIES => {foo => undef}}, "foo")); + +# is_constant() +ok(is_constant("2")); +ok(is_constant("256")); +ok(is_constant("0x400")); +ok(is_constant("0x4BC")); +ok(not is_constant("0x4BGC")); +ok(not is_constant("str")); +ok(not is_constant("2 * expr")); + +# make_str() +is("\"bla\"", make_str("bla")); +is("\"bla\"", make_str("\"bla\"")); +is("\"\"bla\"\"", make_str("\"\"bla\"\"")); +is("\"bla\"\"", make_str("bla\"")); +is("\"foo\"bar\"", make_str("foo\"bar")); + +is("bla", unmake_str("\"bla\"")); +is("\"bla\"", unmake_str("\"\"bla\"\"")); + +# print_uuid() +is(undef, print_uuid("invalid")); +is("{0x12345778,0x1234,0xabcd,{0xef,0x00},{0x01,0x23,0x45,0x67,0x89,0xac}}", + print_uuid("12345778-1234-abcd-ef00-0123456789ac")); +is("{0x12345778,0x1234,0xabcd,{0xef,0x00},{0x01,0x23,0x45,0x67,0x89,0xac}}", + print_uuid("\"12345778-1234-abcd-ef00-0123456789ac\"")); + +# property_matches() +# missing property +ok(not property_matches({PROPERTIES => {}}, "x", "data")); +# data not matching +ok(not property_matches({PROPERTIES => {x => "bar"}}, "x", "data")); +# data matching exactly +ok(property_matches({PROPERTIES => {x => "data"}}, "x", "data")); +# regex matching +ok(property_matches({PROPERTIES => {x => "data"}}, "x", "^([dat]+)\$")); + +# ParseExpr() +is(undef, ParseExpr("", {}, undef)); +is("a", ParseExpr("a", {"b" => "2"}, undef)); +is("2", ParseExpr("a", {"a" => "2"}, undef)); +is("2 * 2", ParseExpr("a*a", {"a" => "2"}, undef)); +is("r->length + r->length", + ParseExpr("length+length", {"length" => "r->length"}, undef)); +is("2 / 2 * (r->length)", + ParseExpr("constant/constant*(len)", {"constant" => "2", + "len" => "r->length"}, undef)); +is("2 + 2 - r->length", + ParseExpr("constant+constant-len", {"constant" => "2", + "len" => "r->length"}, undef)); +is("*r->length", ParseExpr("*len", { "len" => "r->length"}, undef)); +is("**r->length", ParseExpr("**len", { "len" => "r->length"}, undef)); +is("r->length & 2", ParseExpr("len&2", { "len" => "r->length"}, undef)); +is("&r->length", ParseExpr("&len", { "len" => "r->length"}, undef)); +is("calc()", ParseExpr("calc()", { "foo" => "2"}, undef)); +is("calc(2 * 2)", ParseExpr("calc(foo * 2)", { "foo" => "2"}, undef)); +is("strlen(\"data\")", ParseExpr("strlen(foo)", { "foo" => "\"data\""}, undef)); +is("strlen(\"data\", 4)", ParseExpr("strlen(foo, 4)", { "foo" => "\"data\""}, undef)); +is("foo / bar", ParseExpr("foo / bar", { "bla" => "\"data\""}, undef)); +is("r->length % 2", ParseExpr("len%2", { "len" => "r->length"}, undef)); +is("r->length == 2", ParseExpr("len==2", { "len" => "r->length"}, undef)); +is("r->length != 2", ParseExpr("len!=2", { "len" => "r->length"}, undef)); +is("pr->length", ParseExpr("pr->length", { "p" => "r"}, undef)); +is("r->length", ParseExpr("p->length", { "p" => "r"}, undef)); +is("_foo / bla32", ParseExpr("_foo / bla32", { "bla" => "\"data\""}, undef)); +is("foo.bar.blah", ParseExpr("foo.blah", { "foo" => "foo.bar"}, undef)); +is("\"bla\"", ParseExpr("\"bla\"", {}, undef)); +is("1 << 2", ParseExpr("1 << 2", {}, undef)); +is("1 >> 2", ParseExpr("1 >> 2", {}, undef)); +is("0x200", ParseExpr("0x200", {}, undef)); +is("2?3:0", ParseExpr("2?3:0", {}, undef)); +is("~0", ParseExpr("~0", {}, undef)); +is("b->a->a", ParseExpr("a->a->a", {"a" => "b"}, undef)); +is("b.a.a", ParseExpr("a.a.a", {"a" => "b"}, undef)); + +test_errors("nofile:0: Parse error in `~' near `~'\n", sub { + is(undef, ParseExpr("~", {}, {FILE => "nofile", LINE => 0})); }); + +test_errors("nofile:0: Got pointer, expected integer\n", sub { + is(undef, ParseExprExt("foo", {}, {FILE => "nofile", LINE => 0}, + undef, sub { my $x = shift; + error({FILE => "nofile", LINE => 0}, + "Got pointer, expected integer"); + return undef; }))}); + +is("b.a.a", ParseExpr("b.a.a", {"a" => "b"}, undef)); +is("((rr_type) == NBT_QTYPE_NETBIOS)", ParseExpr("((rr_type)==NBT_QTYPE_NETBIOS)", {}, undef)); +is("talloc_check_name", ParseExpr("talloc_check_name", {}, undef)); +is("talloc_check_name()", ParseExpr("talloc_check_name()", {}, undef)); +is("talloc_check_name(ndr)", ParseExpr("talloc_check_name(ndr)", {}, undef)); +is("talloc_check_name(ndr, 1)", ParseExpr("talloc_check_name(ndr,1)", {}, undef)); +is("talloc_check_name(ndr, \"struct ndr_push\")", ParseExpr("talloc_check_name(ndr,\"struct ndr_push\")", {}, undef)); +is("((rr_type) == NBT_QTYPE_NETBIOS) && talloc_check_name(ndr, \"struct ndr_push\")", ParseExpr("((rr_type)==NBT_QTYPE_NETBIOS)&&talloc_check_name(ndr,\"struct ndr_push\")", {}, undef)); +is("(rdata).data.length", ParseExpr("(rdata).data.length", {}, undef)); +is("((rdata).data.length == 2)", ParseExpr("((rdata).data.length==2)", {}, undef)); +is("((rdata).data.length == 2)?0:rr_type", ParseExpr("((rdata).data.length==2)?0:rr_type", {}, undef)); +is("((((rr_type) == NBT_QTYPE_NETBIOS) && talloc_check_name(ndr, \"struct ndr_push\") && ((rdata).data.length == 2))?0:rr_type)", ParseExpr("((((rr_type)==NBT_QTYPE_NETBIOS)&&talloc_check_name(ndr,\"struct ndr_push\")&&((rdata).data.length==2))?0:rr_type)", {}, undef)); diff --git a/source/pidl/tests/wireshark-conf.pl b/source/pidl/tests/wireshark-conf.pl new file mode 100755 index 00000000000..e8cb2d21b84 --- /dev/null +++ b/source/pidl/tests/wireshark-conf.pl @@ -0,0 +1,205 @@ +#!/usr/bin/perl +# (C) 2007 Jelmer Vernooij <jelmer@samba.org> +# Published under the GNU General Public License +# test parsing wireshark conformance files +use strict; +use warnings; + +use Test::More tests => 49; +use FindBin qw($RealBin); +use lib "$RealBin"; +use Util; +use Parse::Pidl::Util qw(MyDumper); +use Parse::Pidl::Wireshark::Conformance qw(ReadConformanceFH valid_ft_type valid_base_type); + +sub parse_conf($) +{ + my $str = shift; + open(TMP, "+>", undef) or die("unable to open temp file"); + print TMP $str; + seek(TMP, 0, 0); + my $data = {}; + ReadConformanceFH(*TMP, $data, "nofile") or return undef; + close(TMP); + return $data; +} + +ok(parse_conf("\n"), undef); +ok(parse_conf(" \n"), undef); +ok(parse_conf("CODE START\nCODE END\n")); +test_warnings("nofile:1: Expecting CODE END\n", sub { is(parse_conf("CODE START\n"), undef); }); +ok(parse_conf("#foobar\n"), undef); +test_warnings("nofile:1: Unknown command `foobar'\n", + sub { ok(parse_conf("foobar\n"), undef); }); + +test_warnings("nofile:1: incomplete HF_RENAME command\n", + sub { parse_conf("HF_RENAME\n"); }); + +is_deeply(parse_conf("HF_RENAME foo bar\n")->{hf_renames}->{foo}, + { OLDNAME => "foo", NEWNAME => "bar", POS => {FILE => "nofile", LINE => 1}, USED => 0}); + +is_deeply(parse_conf("NOEMIT\n"), { "noemit_dissector" => 1 }); +is_deeply(parse_conf("NOEMIT foo\n"), { "noemit" => { "foo" => 1 } }); + +test_warnings("nofile:1: incomplete MANUAL command\n", + sub { parse_conf("MANUAL\n"); } ); + +is_deeply(parse_conf("MANUAL foo\n"), { manual => {foo => 1}}); + +test_errors("nofile:1: incomplete INCLUDE command\n", + sub { parse_conf("INCLUDE\n"); } ); + +test_warnings("nofile:1: incomplete FIELD_DESCRIPTION command\n", + sub { parse_conf("FIELD_DESCRIPTION foo\n"); }); + +is_deeply(parse_conf("FIELD_DESCRIPTION foo \"my description\"\n"), + { fielddescription => { foo => { DESCRIPTION => "\"my description\"", POS => { FILE => "nofile", LINE => 1}, USED => 0 }}}); + +is_deeply(parse_conf("FIELD_DESCRIPTION foo my description\n"), + { fielddescription => { foo => { DESCRIPTION => "my", POS => { FILE => "nofile", LINE => 1}, USED => 0 }}}); + +is_deeply(parse_conf("CODE START\ndata\nCODE END\n"), { override => "data\n" }); +is_deeply(parse_conf("CODE START\ndata\nmore data\nCODE END\n"), { override => "data\nmore data\n" }); +test_warnings("nofile:1: Unknown command `CODE'\n", + sub { parse_conf("CODE END\n"); } ); + +is_deeply(parse_conf("TYPE winreg_String dissect_myminregstring(); FT_STRING BASE_DEC 0 0 2\n"), { types => { winreg_String => { + NAME => "winreg_String", + POS => { FILE => "nofile", LINE => 1 }, + USED => 0, + DISSECTOR_NAME => "dissect_myminregstring();", + FT_TYPE => "FT_STRING", + BASE_TYPE => "BASE_DEC", + MASK => 0, + VALSSTRING => 0, + ALIGNMENT => 2}}}); + +ok(valid_ft_type("FT_UINT32")); +ok(not valid_ft_type("BLA")); +ok(not valid_ft_type("ft_uint32")); +ok(valid_ft_type("FT_BLA")); + +ok(valid_base_type("BASE_DEC")); +ok(valid_base_type("BASE_HEX")); +ok(not valid_base_type("base_dec")); +ok(not valid_base_type("BLA")); +ok(not valid_base_type("BASEDEC")); + +test_errors("nofile:1: incomplete TYPE command\n", + sub { parse_conf("TYPE mytype dissector\n"); }); + +test_warnings("nofile:1: dissector name does not contain `dissect'\n", + sub { parse_conf("TYPE winreg_String myminregstring; FT_STRING BASE_DEC 0 0 2\n"); }); + +test_warnings("nofile:1: invalid FT_TYPE `BLA'\n", + sub { parse_conf("TYPE winreg_String dissect_myminregstring; BLA BASE_DEC 0 0 2\n"); }); + +test_warnings("nofile:1: invalid BASE_TYPE `BLOE'\n", + sub { parse_conf("TYPE winreg_String dissect_myminregstring; FT_UINT32 BLOE 0 0 2\n"); }); + +is_deeply(parse_conf("TFS hf_bla \"True string\" \"False String\"\n"), + { tfs => { hf_bla => { + TRUE_STRING => "\"True string\"", + FALSE_STRING => "\"False String\"" } } }); + +test_errors("nofile:1: incomplete TFS command\n", + sub { parse_conf("TFS hf_bla \"Trues\""); } ); + +test_errors("nofile:1: incomplete PARAM_VALUE command\n", + sub { parse_conf("PARAM_VALUE\n"); }); + +is_deeply(parse_conf("PARAM_VALUE Life 42\n"), + { dissectorparams => { + Life => { + DISSECTOR => "Life", + POS => { FILE => "nofile", LINE => 1 }, + PARAM => 42, + USED => 0 + } + } + }); + +is_deeply(parse_conf("STRIP_PREFIX bla_\n"), + { strip_prefixes => [ "bla_" ] }); + +is_deeply(parse_conf("STRIP_PREFIX bla_\nSTRIP_PREFIX bloe\n"), + { strip_prefixes => [ "bla_", "bloe" ] }); + +is_deeply(parse_conf("PROTOCOL atsvc \"Scheduling jobs on remote machines\" \"at\" \"atsvc\"\n"), + { protocols => { + atsvc => { + LONGNAME => "\"Scheduling jobs on remote machines\"", + SHORTNAME => "\"at\"", + FILTERNAME => "\"atsvc\"" + } + } + } +); + +is_deeply(parse_conf("IMPORT bla\n"), { + imports => { + bla => { + NAME => "bla", + DATA => "", + USED => 0, + POS => { FILE => "nofile", LINE => 1 } + } + } + } +); + +is_deeply(parse_conf("IMPORT bla fn1 fn2 fn3\n"), { + imports => { + bla => { + NAME => "bla", + DATA => "fn1 fn2 fn3", + USED => 0, + POS => { FILE => "nofile", LINE => 1 } + } + } + } +); + +test_errors("nofile:1: no dissectorname specified\n", + sub { parse_conf("IMPORT\n"); } ); + +test_errors("nofile:1: incomplete HF_FIELD command\n", + sub { parse_conf("HF_FIELD hf_idx\n"); }); + +test_errors("nofile:1: incomplete ETT_FIELD command\n", + sub { parse_conf("ETT_FIELD\n"); }); + +is_deeply(parse_conf("TYPE winreg_String dissect_myminregstring(); FT_STRING BASE_DEC 0 0 0 2\n"), { + types => { + winreg_String => { + NAME => "winreg_String", + POS => { FILE => "nofile", LINE => 1 }, + USED => 0, + DISSECTOR_NAME => "dissect_myminregstring();", + FT_TYPE => "FT_STRING", + BASE_TYPE => "BASE_DEC", + MASK => 0, + VALSSTRING => 0, + ALIGNMENT => 0 + } + } + } +); + + +is_deeply(parse_conf("TYPE winreg_String \"offset = dissect_myminregstring(\@HF\@);\" FT_STRING BASE_DEC 0 0 0 2\n"), { + types => { + winreg_String => { + NAME => "winreg_String", + POS => { FILE => "nofile", LINE => 1 }, + USED => 0, + DISSECTOR_NAME => "offset = dissect_myminregstring(\@HF\@);", + FT_TYPE => "FT_STRING", + BASE_TYPE => "BASE_DEC", + MASK => 0, + VALSSTRING => 0, + ALIGNMENT => 0 + } + } + } +); diff --git a/source/pidl/tests/wireshark-ndr.pl b/source/pidl/tests/wireshark-ndr.pl new file mode 100755 index 00000000000..7e17fe6341f --- /dev/null +++ b/source/pidl/tests/wireshark-ndr.pl @@ -0,0 +1,274 @@ +#!/usr/bin/perl +# (C) 2007 Jelmer Vernooij <jelmer@samba.org> +# Published under the GNU General Public License +# test parsing wireshark conformance files +use strict; +use warnings; + +use Test::More tests => 40; +use FindBin qw($RealBin); +use lib "$RealBin"; +use Util; +use Parse::Pidl::Util qw(MyDumper); +use strict; +use Parse::Pidl::Wireshark::NDR qw(field2name %res PrintIdl StripPrefixes RegisterInterfaceHandoff register_hf_field ProcessImport ProcessInclude find_type DumpEttList DumpEttDeclaration DumpHfList DumpHfDeclaration DumpFunctionTable register_type register_ett); + +is("Access Mask", field2name("access_mask")); +is("Accessmask", field2name("AccessMask")); + +my $x = new Parse::Pidl::Wireshark::NDR(); +$x->PrintIdl("foo\nbar\n"); +is("/* IDL: foo */ +/* IDL: bar */ + +", $x->{res}->{code}); + +is("bla_foo", StripPrefixes("bla_foo", [])); +is("foo", StripPrefixes("bla_foo", ["bla"])); +is("foo_bla", StripPrefixes("foo_bla", ["bla"])); + +$x = new Parse::Pidl::Wireshark::NDR(); +$x->RegisterInterfaceHandoff({}); +is($x->{res}->{code}, ""); +ok(not defined($x->{hf_used}->{hf_bla_opnum})); + +$x = new Parse::Pidl::Wireshark::NDR(); +$x->{res}->{code} = ""; +$x->RegisterInterfaceHandoff({UUID => "uuid", NAME => "bla"}); +is($x->{res}->{code}, 'void proto_reg_handoff_dcerpc_bla(void) +{ + dcerpc_init_uuid(proto_dcerpc_bla, ett_dcerpc_bla, + &uuid_dcerpc_bla, ver_dcerpc_bla, + bla_dissectors, hf_bla_opnum); +} +'); +is($x->{hf_used}->{hf_bla_opnum}, 1); + +$x->{conformance} = {}; +is("hf_bla_idx", + $x->register_hf_field("hf_bla_idx", "bla", "my.filter", "FT_UINT32", "BASE_HEX", "NULL", 0xF, undef)); +is_deeply($x->{conformance}, { + header_fields => { + "hf_bla_idx" => { + INDEX => "hf_bla_idx", + NAME => "bla", + FILTER => "my.filter", + BASE_TYPE => "BASE_HEX", + FT_TYPE => "FT_UINT32", + VALSSTRING => "NULL", + BLURB => undef, + MASK => 0xF + } + }, + hf_renames => {}, + fielddescription => {} +}); + +$x->{conformance} = { fielddescription => { hf_bla_idx => { DESCRIPTION => "Some Description" }}}; +is("hf_bla_idx", + $x->register_hf_field("hf_bla_idx", "bla", "my.filter", "FT_UINT32", "BASE_HEX", "NULL", 0xF, undef)); +is_deeply($x->{conformance}, { + fielddescription => { + hf_bla_idx => { + DESCRIPTION => "Some Description", + USED => 1 + } + }, + header_fields => { + "hf_bla_idx" => { + INDEX => "hf_bla_idx", + NAME => "bla", + FILTER => "my.filter", + BASE_TYPE => "BASE_HEX", + FT_TYPE => "FT_UINT32", + VALSSTRING => "NULL", + BLURB => "Some Description", + MASK => 0xF + } + }, + hf_renames => {}, +}); + +$x->{conformance} = { fielddescription => { hf_bla_idx => { DESCRIPTION => "Some Description" }}}; +is("hf_bla_idx", + $x->register_hf_field("hf_bla_idx", "bla", "my.filter", "FT_UINT32", "BASE_HEX", "NULL", 0xF, + "Actual Description")); +is_deeply($x->{conformance}, { + fielddescription => { + hf_bla_idx => { DESCRIPTION => "Some Description" } + }, + header_fields => { + "hf_bla_idx" => { + INDEX => "hf_bla_idx", + NAME => "bla", + FILTER => "my.filter", + BASE_TYPE => "BASE_HEX", + FT_TYPE => "FT_UINT32", + VALSSTRING => "NULL", + BLURB => "Actual Description", + MASK => 0xF + } + }, + hf_renames => {}, +}); + + + +$x->{conformance} = { hf_renames => { "hf_bla_idx" => { NEWNAME => "hf_bloe_idx" } } }; +$x->register_hf_field("hf_bla_idx", "bla", "my.filter", "FT_UINT32", "BASE_HEX", "NULL", 0xF, undef); +is_deeply($x->{conformance}, { + hf_renames => { hf_bla_idx => { USED => 1, NEWNAME => "hf_bloe_idx" } } }); + +$x->{hf_used} = { hf_bla => 1 }; +test_warnings("", sub { + $x->CheckUsed({ header_fields => { foo => { INDEX => "hf_bla" }}})}); + +$x->{hf_used} = { }; +test_warnings("hf field `hf_bla' not used\n", sub { + $x->CheckUsed({ header_fields => { foo => { INDEX => "hf_bla" }}})}); + +test_warnings("hf field `hf_id' not used\n", + sub { $x->CheckUsed({ + hf_renames => { + hf_id => { + OLDNAME => "hf_id", + NEWNAME => "hf_newid", + USED => 0 + } + } +}); } ); + +test_warnings("dissector param never used\n", + sub { $x->CheckUsed({ + dissectorparams => { + dissect_foo => { + PARAM => 42, + USED => 0 + } + } +}); } ); + +test_warnings("description never used\n", + sub { $x->CheckUsed({ + fielddescription => { + hf_bla => { + USED => 0 + } + } +}); } ); + +test_warnings("import never used\n", + sub { $x->CheckUsed({ + imports => { + bla => { + USED => 0 + } + } +}); } ); + +test_warnings("nofile:1: type never used\n", + sub { $x->CheckUsed({ + types => { + bla => { + USED => 0, + POS => { FILE => "nofile", LINE => 1 } + } + } +}); } ); + +test_warnings("True/False description never used\n", + sub { $x->CheckUsed({ + tfs => { + hf_bloe => { + USED => 0 + } + } +}); } ); + +$x = new Parse::Pidl::Wireshark::NDR(); +$x->ProcessImport("security", "bla"); +is($x->{res}->{hdr}, "#include \"packet-dcerpc-bla.h\"\n\n"); + +$x = new Parse::Pidl::Wireshark::NDR(); +$x->ProcessImport("\"bla.idl\"", "\"foo.idl\""); +is($x->{res}->{hdr}, "#include \"packet-dcerpc-bla.h\"\n" . + "#include \"packet-dcerpc-foo.h\"\n\n"); + +$x = new Parse::Pidl::Wireshark::NDR(); +$x->ProcessInclude("foo.h", "bla.h", "bar.h"); +is($x->{res}->{hdr}, "#include \"foo.h\"\n" . + "#include \"bla.h\"\n" . + "#include \"bar.h\"\n\n"); + +$x->{conformance} = {types => { bla => "brainslug" } }; +is("brainslug", $x->find_type("bla")); + +is(DumpEttList(["ett_t1", "ett_bla"]), + "\tstatic gint *ett[] = {\n" . + "\t\t&ett_t1,\n" . + "\t\t&ett_bla,\n" . + "\t};\n"); + +is(DumpEttList(), "\tstatic gint *ett[] = {\n\t};\n"); +is(DumpEttList(["bla"]), "\tstatic gint *ett[] = {\n\t\t&bla,\n\t};\n"); + +is(DumpEttDeclaration(["void", "zoid"]), + "\n/* Ett declarations */\n" . + "static gint void = -1;\n" . + "static gint zoid = -1;\n" . + "\n"); + +is(DumpEttDeclaration(), "\n/* Ett declarations */\n\n"); + +$x->{conformance} = { + header_fields => { + hf_bla => { INDEX => "hf_bla", NAME => "Bla", FILTER => "bla.field", FT_TYPE => "FT_UINT32", BASE_TYPE => "BASE_DEC", VALSSTRING => "NULL", MASK => 0xFF, BLURB => "NULL" } + } +}; + +is($x->DumpHfList(), "\tstatic hf_register_info hf[] = { + { &hf_bla, + { \"Bla\", \"bla.field\", FT_UINT32, BASE_DEC, NULL, 255, \"NULL\", HFILL }}, + }; +"); + +is($x->DumpHfDeclaration(), " +/* Header field declarations */ +static gint hf_bla = -1; + +"); + +is(DumpFunctionTable({ + NAME => "someif", + FUNCTIONS => [ { NAME => "fn1", OPNUM => 3 }, { NAME => "someif_fn2", OPNUM => 2 } ] }), +'static dcerpc_sub_dissector someif_dissectors[] = { + { 3, "fn1", + someif_dissect_fn1_request, someif_dissect_fn1_response}, + { 2, "fn2", + someif_dissect_fn2_request, someif_dissect_fn2_response}, + { 0, NULL, NULL, NULL } +}; +'); + +$x->{conformance} = {}; +$x->register_type("bla_type", "dissect_bla", "FT_UINT32", "BASE_HEX", 0xFF, "NULL", 4); +is_deeply($x->{conformance}, { + types => { + bla_type => { + NAME => "bla_type", + DISSECTOR_NAME => "dissect_bla", + FT_TYPE => "FT_UINT32", + BASE_TYPE => "BASE_HEX", + MASK => 255, + VALSSTRING => "NULL", + ALIGNMENT => 4 + } + } + } +); + +$x->{ett} = []; +$x->register_ett("name"); +is_deeply($x->{ett}, ["name"]); +$x->register_ett("leela"); +is_deeply($x->{ett}, ["name", "leela"]); |