3 ###################################################
4 # package to parse IDL files and generate code for
5 # rpc functions in Samba
6 # Copyright tridge@samba.org 2000-2003
7 # Copyright jelmer@samba.org 2005
8 # released under the GNU GPL
14 pidl - An IDL compiler written in Perl
20 pidl [--outputdir[=OUTNAME]] [--parse-idl-tree] [--dump-idl-tree] [--dump-ndr-tree] [--header[=OUTPUT]] [--ejs[=OUTPUT]] [--swig[=OUTPUT]] [--uint-enums] [--ndr-parser[=OUTPUT]] [--client] [--server] [--dcom-proxy] [--com-header] [--warn-compat] [--quiet] [--verbose] [--template] [--eth-parser[=OUTPUT]] [--diff] [--dump-idl] [--tdr-parser[=OUTPUT]] [--samba3-header[=OUTPUT]] [--samba3-parser=[OUTPUT]] [--samba3-server=[OUTPUT]] [--samba3-template[=OUTPUT]] [--samba3-client[=OUTPUT]] [<idlfile>.idl]...
24 pidl is an IDL compiler written in Perl that aims to be somewhat
25 compatible with the midl compiler. IDL is short for
26 "Interface Definition Language".
28 pidl can generate stubs for DCE/RPC server code, DCE/RPC
29 client code and ethereal dissectors for DCE/RPC traffic.
31 IDL compilers like pidl take a description
32 of an interface as their input and use it to generate C
33 (though support for other languages may be added later) code that
34 can use these interfaces, pretty print data sent
35 using these interfaces, or even generate ethereal
36 dissectors that can parse data sent over the
37 wire by these interfaces.
39 pidl takes IDL files in the same format as is used by midl,
40 converts it to a .pidl file (which contains pidl's internal representation of the interface) and can then generate whatever output you need.
41 .pidl files should be used for debugging purposes only. Write your
42 interface definitions in .idl format.
44 The goal of pidl is to implement a IDL compiler that can be used
45 while developing the RPC subsystem in Samba (for
46 both marshalling/unmarshalling and debugging purposes).
54 Show list of available options.
56 =item I<--outputdir OUTNAME>
58 Write output files to the specified directory. Defaults to the current
61 =item I<--parse-idl-tree>
63 Read internal tree structure from input files rather
64 then assuming they contain IDL.
68 Generate a new IDL file. File will be named OUTNAME.idl.
72 Generate a C header file for the specified interface. Filename defaults to OUTNAME.h.
76 Generate a C file and C header containing NDR parsers. The filename for
77 the parser defaults to ndr_OUTNAME.c. The header filename will be the
78 parser filename with the extension changed from .c to .h.
82 Generate a C file and C header containing TDR parsers. The filename for
83 the parser defaults to tdr_OUTNAME.c. The header filename will be the
84 parser filename with the extension changed from .c to .h.
88 Generate boilerplate for the RPC server that implements
89 the interface. Filename defaults to ndr_OUTNAME_s.c.
93 Generate stubs for a RPC server that implements the interface. Output will
98 Generate an Ethereal dissector (in C) and header file. The dissector filename
99 defaults to packet-dcerpc-OUTNAME.c while the header filename defaults to
100 packet-dcerpc-OUTNAME.h.
102 Pidl will read additional data from an ethereal conformance file if present.
103 Such a file should have the same location as the IDL file but with the
104 extension I<cnf> rather then I<idl>. See L<Parse::Pidl::Ethereal::Conformance>
105 for details on the format of this file.
109 Parse an IDL file, generate a new IDL file based on the internal data
110 structures and see if there are any differences with the original IDL file.
111 Useful for debugging pidl.
113 =item I<--dump-idl-tree>
115 Tell pidl to dump the internal tree representation of an IDL
116 file the to disk. Useful for debugging pidl.
118 =item I<--dump-ndr-tree>
120 Tell pidl to dump the internal NDR information tree it generated
121 from the IDL file to disk. Useful for debugging pidl.
123 =item I<--samba3-header>
125 Generate Samba3-style RPC header file. Filename defaults to rpc_BASENAME.h.
127 =item I<--samba3-parser>
129 Generate parser file for Samba3, to be placed in rpc_parse/. Filename defaults
132 =item I<--samba3-server>
134 Generate server file for Samba3, to be placed in rpc_server/. Filename defaults
137 =item I<--samba3-template>
139 Generate template for server-side implementation in Samba3, to be placed in
140 rpc_server/. Filename defaults to srv_BASENAME_nt.c
142 =item I<--samba3-client>
144 Generate client calls for Samba 3, to be placed in rpc_client/. Filename
145 defaults to cli_BASENAME.c.
151 IDL files are always preprocessed using the C preprocessor.
153 Pretty much everything in an interface (the interface itself, functions,
154 parameters) can have attributes (or properties whatever name you give them).
155 Attributes always prepend the element they apply to and are surrounded
156 by square brackets ([]). Multiple attributes are separated by comma's;
157 arguments to attributes are specified between parentheses.
159 See the section COMPATIBILITY for the list of attributes that
162 C-style comments can be used.
164 =head2 CONFORMANT ARRAYS
166 A conformant array is one with that ends in [*] or []. The strange
167 things about conformant arrays are that they can only appear as the last
168 element of a structure (unless there is a pointer to the conformant array,
169 of course) and the array size appears before the structure itself on the wire.
177 [size_is(count)] long s[*];
180 it appears like this:
182 [size_is] [abc] [count] [foo] [s...]
184 the first [size_is] field is the allocation size of the array, and
185 occurs before the array elements and even before the structure
188 Note that size_is() can refer to a constant, but that doesn't change
189 the wire representation. It does not make the array a fixed array.
191 midl.exe would write the above array as the following C header:
200 pidl takes a different approach, and writes it like this:
209 =head2 VARYING ARRAYS
211 A varying array looks like this:
217 [size_is(count)] long *s;
220 This will look like this on the wire:
222 [abc] [count] [foo] [PTR_s] [count] [s...]
226 A fixed array looks like this:
232 The NDR representation looks just like 10 separate long
233 declarations. The array size is not encoded on the wire.
235 pidl also supports "inline" arrays, which are not part of the IDL/NDR
236 standard. These are declared like this:
245 This appears like this:
247 [foo] [count] [bar] [s...]
249 Fixed arrays are an extension added to support some of the strange
250 embedded structures in security descriptors and spoolss.
252 This section is by no means complete. See the OpenGroup and MSDN
253 documentation for additional information.
255 =head1 COMPATIBILITY WITH MIDL
257 =head2 Missing features in pidl
259 The following MIDL features are not (yet) implemented in pidl
260 or are implemented with an incompatible interface:
266 Asynchronous communication
270 Typelibs (.tlb files)
274 Datagram support (ncadg_*)
278 =head2 Supported attributes
280 in, out, ref, length_is, switch_is, size_is, uuid, case, default, string,
281 unique, ptr, pointer_default, v1_enum, object, helpstring, range, local,
282 call_as, endpoint, switch_type, progid, coclass, iid_is, represent_as.
284 =head2 PIDL Specific properties
290 The [public] property on a structure or union is a pidl extension that
291 forces the generated pull/push functions to be non-static. This allows
292 you to declare types that can be used between modules. If you don't
293 specify [public] then pull/push functions for other than top-level
294 functions are declared static.
298 The [noprint] property is a pidl extension that allows you to specify
299 that pidl should not generate a ndr_print_*() function for that
300 structure or union. This is used when you wish to define your own
301 print function that prints a structure in a nicer manner. A good
302 example is the use of [noprint] on dom_sid, which allows the
303 pretty-printing of SIDs.
307 The [value(expression)] property is a pidl extension that allows you
308 to specify the value of a field when it is put on the wire. This
309 allows fields that always have a well-known value to be automatically
310 filled in, thus making the API more programmer friendly. The
311 expression can be any C expression.
315 The [relative] property can be supplied on a pointer. When it is used
316 it declares the pointer as a spoolss style "relative" pointer, which
317 means it appears on the wire as an offset within the current
318 encapsulating structure. This is not part of normal IDL/NDR, but it is
319 a very useful extension as it avoids the manual encoding of many
322 =item subcontext(length)
324 Specifies that a size of I<length>
325 bytes should be read, followed by a blob of that size,
326 which will be parsed as NDR.
330 Specify boolean options, mostly used for
331 low-level NDR options. Several options
332 can be specified using the | character.
333 Note that flags are inherited by substructures!
337 The [nodiscriminant] property on a union means that the usual uint16
338 discriminent field at the start of the union on the wire is
339 omitted. This is not normally allowed in IDL/NDR, but is used for some
344 Specify that the array or string uses the specified
345 charset. If this attribute is specified, pidl will
346 take care of converting the character data from this format
347 to the host format. Commonly used values are UCS2, DOS and UTF8.
351 =head2 Unsupported MIDL properties
353 aggregatable, appobject, async_uuid, bindable, control, cpp_quote,
354 defaultbind, defaultcollelem, defaultvalue, defaultvtable, dispinterface,
355 displaybind, dual, entry, first_is, helpcontext, helpfile, helpstringcontext,
356 helpstringdll, hidden, idl_module, idl_quote, id, immediatebind, importlib,
357 import, include, includelib, last_is, lcid, licensed, max_is, module,
358 ms_union, no_injected_text, nonbrowsable, noncreatable, nonextensible, odl,
359 oleautomation, optional, pragma, propget, propputref, propput, readonly,
360 requestedit, restricted, retval, source, transmit_as, uidefault,
361 usesgetlasterror, vararg, vi_progid, wire_marshal.
365 # Generating an ethereal parser
366 $ ./pidl --eth-parser -- atsvc.idl
368 # Generating a TDR parser and header
369 $ ./pidl --tdr-parser --header -- regf.idl
371 # Generating a Samba3 parser, client and server
372 $ ./pidl --samba3-parser --samba3-server --samba3-client -- dfs.idl
374 # Generating a Samba4 NDR parser, client and server
375 $ ./pidl --ndr-parser --ndr-client --ndr-server -- samr.idl
379 L<http://msdn.microsoft.com/library/en-us/rpc/rpc/field_attributes.asp>,
380 L<http://wiki.ethereal.com/DCE/RPC>,
381 L<http://www.samba.org/>,
386 pidl is licensed under the GNU General Public License L<http://www.gnu.org/licenses/gpl.html>.
390 pidl was written by Andrew Tridgell, Stefan Metzmacher, Tim Potter and Jelmer
391 Vernooij. The current maintainer is Jelmer Vernooij.
393 This manpage was written by Jelmer Vernooij, partially based on the original
394 pidl README by Andrew Tridgell.
400 use FindBin qw($RealBin);
402 use lib "$RealBin/lib";
406 use Parse::Pidl::Util;
407 use Parse::Pidl::ODL;
409 #####################################################################
410 # save a data structure into a file
411 sub SaveStructure($$)
413 my($filename,$v) = @_;
414 FileSave($filename, Parse::Pidl::Util::MyDumper($v));
417 #####################################################################
418 # load a data structure from a file (as saved with SaveStructure)
422 my $contents = FileLoad($f);
423 defined $contents || return undef;
424 return eval "$contents";
427 #####################################################################
428 # read a file into a string
431 my($filename) = shift;
433 open(INPUTFILE, $filename) || return undef;
434 my($saved_delim) = $/;
436 my($data) = <INPUTFILE>;
442 #####################################################################
443 # write a string into a file
446 my($filename) = shift;
449 open(FILE, ">$filename") || die "can't open $filename";
455 my($opt_parse_idl_tree) = 0;
456 my($opt_dump_idl_tree);
457 my($opt_dump_ndr_tree);
458 my($opt_dump_idl) = 0;
459 my($opt_uint_enums) = 0;
462 my($opt_samba3_header);
463 my($opt_samba3_parser);
464 my($opt_samba3_server);
465 my($opt_samba3_template);
466 my($opt_samba3_client);
467 my($opt_template) = 0;
478 my($opt_outputdir) = '.';
479 my($opt_verbose) = 0;
480 my($opt_warn_compat) = 0;
482 #########################################
486 print "perl IDL parser and code generator
487 Copyright (C) Andrew Tridgell <tridge\@samba.org>
488 Copyright (C) Jelmer Vernooij <jelmer\@samba.org>
490 Usage: pidl [options] [--] <idlfile> [<idlfile>...]
493 --help this help page
494 --outputdir=OUTDIR put output in OUTDIR/ [.]
495 --warn-compat warn about incompatibility with other compilers
500 --dump-idl-tree[=FILE] dump internal representation to file [BASENAME.pidl]
501 --parse-idl-tree read internal representation instead of IDL
502 --dump-ndr-tree[=FILE] dump internal NDR data tree to file [BASENAME.ndr]
503 --dump-idl regenerate IDL file
504 --diff run diff on original IDL and dumped output
507 --header[=OUTFILE] create generic header file [BASENAME.h]
508 --uint-enums don't use C enums, instead use uint* types
509 --ndr-parser[=OUTFILE] create a C NDR parser [ndr_BASENAME.c]
510 --client[=OUTFILE] create a C NDR client [ndr_BASENAME_c.c]
511 --tdr-parser[=OUTFILE] create a C TDR parser [tdr_BASENAME.c]
512 --ejs[=OUTFILE] create ejs wrapper file [BASENAME_ejs.c]
513 --swig[=OUTFILE] create swig wrapper file [BASENAME.i]
514 --server[=OUTFILE] create server boilerplate [ndr_BASENAME_s.c]
515 --template print a template for a pipe
516 --dcom-proxy[=OUTFILE] create DCOM proxy [ndr_BASENAME_p.c]
517 --com-header[=OUTFILE] create header for COM [com_BASENAME.h]
520 --samba3-header[=OUTF] create Samba3-style header [rpc_BASENAME.h]
521 --samba3-parser[=OUTF] create parser for Samba3 [parse_BASENAME.c]
522 --samba3-template[=OUTF]create template implementation [srv_BASENAME_nt.c]
523 --samba3-server[=OUTF] create server side wrappers for Samba3 [srv_BASENAME.c]
524 --samba3-client[=OUTF] create client calls for Samba3 [cli_BASENAME.c]
527 --eth-parser[=OUTFILE] create ethereal parser and header
533 my $result = GetOptions (
534 'help|h|?' => \$opt_help,
535 'outputdir=s' => \$opt_outputdir,
536 'dump-idl' => \$opt_dump_idl,
537 'dump-idl-tree:s' => \$opt_dump_idl_tree,
538 'parse-idl-tree' => \$opt_parse_idl_tree,
539 'dump-ndr-tree:s' => \$opt_dump_ndr_tree,
540 'uint-enums' => \$opt_uint_enums,
541 'samba3-header:s' => \$opt_samba3_header,
542 'samba3-parser:s' => \$opt_samba3_parser,
543 'samba3-server:s' => \$opt_samba3_server,
544 'samba3-template:s' => \$opt_samba3_template,
545 'samba3-client:s' => \$opt_samba3_client,
546 'header:s' => \$opt_header,
547 'server:s' => \$opt_server,
548 'tdr-parser:s' => \$opt_tdr_parser,
549 'template' => \$opt_template,
550 'ndr-parser:s' => \$opt_ndr_parser,
551 'client:s' => \$opt_client,
552 'eth-parser:s' => \$opt_eth_parser,
554 'diff' => \$opt_diff,
555 'swig:s' => \$opt_swig,
556 'dcom-proxy:s' => \$opt_dcom_proxy,
557 'com-header:s' => \$opt_com_header,
558 'quiet' => \$opt_quiet,
559 'verbose' => \$opt_verbose,
560 'warn-compat' => \$opt_warn_compat
574 my $idl_file = shift;
575 my $outputdir = $opt_outputdir;
579 my $basename = basename($idl_file, ".idl");
581 unless ($opt_quiet) { print "Compiling $idl_file\n"; }
583 if ($opt_parse_idl_tree) {
584 $pidl = LoadStructure($idl_file);
585 defined $pidl || die "Failed to load $idl_file";
587 require Parse::Pidl::IDL;
589 $pidl = Parse::Pidl::IDL::parse_file($idl_file);
590 defined @$pidl || die "Failed to parse $idl_file";
591 require Parse::Pidl::Typelist;
592 Parse::Pidl::Typelist::LoadIdl($pidl);
595 if (defined($opt_dump_idl_tree)) {
596 my($pidl_file) = ($opt_dump_idl_tree or "$outputdir/$basename.pidl");
597 SaveStructure($pidl_file, $pidl) or die "Failed to save $pidl_file\n";
600 if ($opt_uint_enums) {
601 Parse::Pidl::Util::setUseUintEnums(1);
605 require Parse::Pidl::Dump;
606 print Parse::Pidl::Dump($pidl);
610 my($tempfile) = "$outputdir/$basename.tmp";
611 FileSave($tempfile, IdlDump::Dump($pidl));
612 system("diff -wu $idl_file $tempfile");
616 if (defined($opt_com_header)) {
617 require Parse::Pidl::Samba4::COM::Header;
618 my $res = Parse::Pidl::Samba4::COM::Header::Parse($pidl);
620 my $comh_filename = ($opt_com_header or "$outputdir/com_$basename.h");
621 FileSave($comh_filename,
622 "#include \"librpc/gen_ndr/ndr_orpc.h\"\n" .
623 "#include \"$outputdir/ndr_$basename.h\"\n" .
628 if (defined($opt_dcom_proxy)) {
629 require Parse::Pidl::Samba4::COM::Proxy;
630 my $res = Parse::Pidl::Samba4::COM::Proxy::Parse($pidl);
632 my ($client) = ($opt_dcom_proxy or "$outputdir/$basename\_p.c");
634 "#include \"includes.h\"\n" .
635 "#include \"$outputdir/com_$basename.h\"\n" .
636 "#include \"lib/com/dcom/dcom.h\"\n" .$res);
640 if ($opt_warn_compat) {
641 require Parse::Pidl::Compat;
642 Parse::Pidl::Compat::Check($pidl);
645 $pidl = Parse::Pidl::ODL::ODL2IDL($pidl);
647 if (defined($opt_eth_parser) or
648 defined($opt_client) or defined($opt_server) or
649 defined($opt_ndr_parser) or defined($opt_ejs) or
650 defined($opt_dump_ndr_tree) or defined($opt_samba3_header) or
651 defined($opt_samba3_parser) or defined($opt_samba3_server) or
652 defined($opt_samba3_template) or defined($opt_samba3_client)) {
653 require Parse::Pidl::NDR;
654 $ndr = Parse::Pidl::NDR::Parse($pidl);
657 if (defined($opt_dump_ndr_tree)) {
658 my($ndr_file) = ($opt_dump_ndr_tree or "$outputdir/$basename.ndr");
659 SaveStructure($ndr_file, $ndr) or die "Failed to save $ndr_file\n";
662 my $gen_header = ($opt_header or "$outputdir/$basename.h");
663 if (defined($opt_header)) {
664 require Parse::Pidl::Samba4::Header;
665 FileSave($gen_header, Parse::Pidl::Samba4::Header::Parse($pidl));
668 my $h_filename = "$outputdir/ndr_$basename.h";
669 if (defined($opt_client)) {
670 require Parse::Pidl::Samba4::NDR::Client;
671 my ($client) = ($opt_client or "$outputdir/ndr_$basename\_c.c");
673 FileSave($client, Parse::Pidl::Samba4::NDR::Client::Parse($ndr,$h_filename));
676 if (defined($opt_ejs)) {
677 require Parse::Pidl::Samba4::EJS;
678 my ($hdr,$prsr) = Parse::Pidl::Samba4::EJS::Parse($ndr, $h_filename);
679 FileSave("$outputdir/ndr_$basename\_ejs.c", $prsr);
680 FileSave("$outputdir/ndr_$basename\_ejs.h", $hdr);
683 if (defined($opt_server)) {
684 require Parse::Pidl::Samba4::NDR::Server;
687 foreach my $x (@{$pidl}) {
688 next if ($x->{TYPE} ne "INTERFACE");
690 if (Parse::Pidl::Util::has_property($x, "object")) {
691 require Parse::Pidl::Samba4::COM::Stub;
692 $dcom .= Parse::Pidl::Samba4::COM::Stub::ParseInterface($x);
696 FileSave(($opt_server or "$outputdir/ndr_$basename\_s.c"), Parse::Pidl::Samba4::NDR::Server::Parse($ndr,$h_filename));
700 #include \"includes.h\"
701 #include \"$h_filename\"
702 #include \"rpc_server/dcerpc_server.h\"
703 #include \"rpc_server/common/common.h\"
707 FileSave("$outputdir/$basename\_d.c", $dcom);
711 if (defined($opt_ndr_parser)) {
712 my $parser_fname = ($opt_ndr_parser or "$outputdir/ndr_$basename.c");
713 require Parse::Pidl::Samba4::NDR::Parser;
714 my $header_fname = $parser_fname;
715 $header_fname =~ s/\.c$/\.h/;
716 my ($header,$parser) = Parse::Pidl::Samba4::NDR::Parser::Parse($ndr, $basename);
718 my $baseheader = $h_filename; $baseheader =~ s/\/ndr_/\//;
719 $header = "#include \"$baseheader\"\n$header";
721 $parser = "#include \"includes.h\"\n"
722 . "#include \"librpc/gen_ndr/ndr_misc.h\"\n"
723 . "#include \"librpc/gen_ndr/ndr_dcerpc.h\"\n"
724 . "#include \"$header_fname\"\n\n$parser";
726 FileSave($parser_fname, $parser);
727 FileSave($header_fname, $header);
729 if (defined($opt_swig)) {
730 require Parse::Pidl::Samba4::SWIG;
731 my($filename) = ($opt_swig or "$outputdir/$basename.i");
732 Parse::Pidl::Samba4::SWIG::RewriteHeader($pidl, $header_fname, $filename);
736 if (defined($opt_eth_parser)) {
737 require Parse::Pidl::Ethereal::NDR;
738 my($eparser) = ($opt_eth_parser or "$outputdir/packet-dcerpc-$basename.c");
739 my $eheader = $eparser;
740 $eheader =~ s/\.c$/\.h/;
741 my $cnffile = $idl_file;
742 $cnffile =~ s/\.idl$/\.cnf/;
744 my ($dp, $dh) = Parse::Pidl::Ethereal::NDR::Parse($ndr, $idl_file, $eheader, $cnffile);
745 FileSave($eparser, $dp) if defined($dp);
746 FileSave($eheader, $dh) if defined($dh);
749 if (defined($opt_tdr_parser)) {
750 my $tdr_parser = ($opt_tdr_parser or "$outputdir/tdr_$basename.c");
751 my $tdr_header = $tdr_parser;
752 $tdr_header =~ s/\.c$/\.h/;
753 require Parse::Pidl::Samba4::TDR;
754 my ($hdr,$prsr) = Parse::Pidl::Samba4::TDR::Parser($pidl, $tdr_header, $gen_header);
755 FileSave($tdr_parser, $prsr);
756 FileSave($tdr_header, $hdr);
760 require Parse::Pidl::Samba4::Template;
761 print Parse::Pidl::Samba4::Template::Parse($pidl);
764 if (defined($opt_samba3_header) or defined($opt_samba3_parser) or
765 defined($opt_samba3_server) or defined($opt_samba3_client) or
766 defined($opt_samba3_template)) {
767 require Parse::Pidl::Samba3::Types;
768 Parse::Pidl::Samba3::Types::LoadTypes($ndr);
771 if (defined($opt_samba3_header)) {
772 my $header = ($opt_samba3_header or "$outputdir/rpc_$basename.h");
773 require Parse::Pidl::Samba3::Header;
774 FileSave($header, Parse::Pidl::Samba3::Header::Parse($ndr, $basename));
777 if (defined($opt_samba3_parser)) {
778 my $header = ($opt_samba3_parser or "$outputdir/parse_$basename.c");
779 require Parse::Pidl::Samba3::Parser;
780 FileSave($header, Parse::Pidl::Samba3::Parser::Parse($ndr, $basename));
783 if (defined($opt_samba3_server)) {
784 my $header = ($opt_samba3_server or "$outputdir/srv_$basename.c");
785 require Parse::Pidl::Samba3::Server;
786 FileSave($header, Parse::Pidl::Samba3::Server::Parse($ndr, $basename));
789 if (defined($opt_samba3_template)) {
790 my $header = ($opt_samba3_template or "$outputdir/srv_$basename\_nt.c");
791 require Parse::Pidl::Samba3::Template;
792 FileSave($header, Parse::Pidl::Samba3::Template::Parse($ndr, $basename));
795 if (defined($opt_samba3_client)) {
796 my $header = ($opt_samba3_client or "$outputdir/cli_$basename.c");
797 require Parse::Pidl::Samba3::Client;
798 FileSave($header, Parse::Pidl::Samba3::Client::Parse($ndr, $basename));
803 if (scalar(@ARGV) == 0) {
804 print "pidl: no input files\n";
808 process_file($_) foreach (@ARGV);