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
12 pidl - IDL Compiler written in Perl
17 pidl [--outputdir[=OUTNAME]] [--parse-idl-tree] [--dump-idl-tree] [--dump-ndr-tree] [--ndr-header[=OUTPUT]] [--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] [<idlfile>.idl]...
21 pidl is an IDL compiler written in Perl that aims to be somewhat
22 compatible with the midl compiler. IDL stands for
23 "Interface Definition Language".
25 pidl can generate stubs for DCE/RPC server code, DCE/RPC
26 client code and ethereal dissectors for DCE/RPC traffic.
28 IDL compilers like pidl take a description
29 of an interface as their input and use it to generate C
30 (though support for other languages may be added later) code that
31 can use these interfaces, pretty print data sent
32 using these interfaces, or even generate ethereal
33 dissectors that can parse data sent over the
34 wire by these interfaces.
36 pidl takes IDL files in the same format as is used by midl,
37 converts it to a .pidl file (which contains pidl's internal representation of the interface) and can then generate whatever output you need.
38 .pidl files should be used for debugging purposes only. Write your
39 interface definitions in .idl format.
41 The goal of pidl is to implement a IDL compiler that can be used
42 while developing the RPC subsystem in Samba (for
43 both marshalling/unmarshalling and debugging purposes).
51 Show list of available options.</para></listitem>
53 =item I<--outputdir OUTNAME>
55 Write output files to the specified directory. Defaults to the current
58 =item I<--parse-idl-tree>
60 Read internal tree structure from input files rather
61 then assuming they contain IDL.
65 Generate a new IDL file. File will be named OUTNAME.idl.</para></listitem>
69 Generate a C header file for the specified interface. Filename defaults to OUTNAME.h.
73 Generate a C header file with the prototypes for the NDR parsers. Filename defaults to ndr_OUTNAME.h.
77 Generate a C file containing NDR parsers. Filename defaults to ndr_OUTNAME.c.
81 Generate boilerplate for the RPC server that implements
82 the interface. Filename defaults to ndr_OUTNAME_s.c.
86 Generate stubs for a RPC server that implements the interface. Output will
91 Generate an Ethereal dissector (in C) for the interface. Filename
92 defaults to packet-dcerpc-OUTNAME.c.
94 Pidl will read additional data from an ethereal conformance file if present.
95 Such a file should have the same location as the IDL file but with the
96 extension I<cnf> rather then I<idl>. See below for details on the format of
101 Parse an IDL file, generate a new IDL file based on the internal data
102 structures and see if there are any differences with the original IDL file.
103 Useful for debugging pidl.
105 =item I<--dump-idl-tree>
107 Tell pidl to dump the internal tree representation of an IDL
108 file the to disk. Useful for debugging pidl.
110 =item I<--dump-ndr-tree>
112 Tell pidl to dump the internal NDR information tree it generated
113 from the IDL file to disk. Useful for debugging pidl.
119 IDL files are always preprocessed using the C preprocessor.
121 Pretty much everything in an interface (the interface itself, functions,
122 parameters) can have attributes (or properties whatever name you give them).
123 Attributes always prepend the element they apply to and are surrounded
124 by square brackets ([]). Multiple attributes are separated by comma's;
125 arguments to attributes are specified between parentheses.
127 See the section COMPATIBILITY for the list of attributes that
130 C-style comments can be used.
132 =head2 CONFORMANT ARRAYS
134 A conformant array is one with that ends in [*] or []. The strange
135 things about conformant arrays are:
138 =item they can only appear as the last element of a structure
139 =item the array size appears before the structure itself on the wire.
148 [size_is(count)] long s[*];
151 it appears like this:
153 [size_is] [abc] [count] [foo] [s...]
155 the first [size_is] field is the allocation size of the array, and
156 occurs before the array elements and even before the structure
159 Note that size_is() can refer to a constant, but that doesn't change
160 the wire representation. It does not make the array a fixed array.
162 midl.exe would write the above array as the following C header:
171 pidl takes a different approach, and writes it like this:
180 =head2 VARYING ARRAYS
182 A varying array looks like this:
188 [size_is(count)] long *s;
191 This will look like this on the wire:
193 [abc] [count] [foo] [PTR_s] [count] [s...]
197 A fixed array looks like this:
203 The NDR representation looks just like 10 separate long
204 declarations. The array size is not encoded on the wire.
206 pidl also supports "inline" arrays, which are not part of the IDL/NDR
207 standard. These are declared like this:
216 This appears like this:
218 [foo] [count] [bar] [s...]
220 Fixed arrays are an extension added to support some of the strange
221 embedded structures in security descriptors and spoolss.
223 This section is by no means complete. See the OpenGroup and MSDN
224 documentation for additional information.
226 =head1 COMPATIBILITY WITH MIDL
228 =head2 Missing features in pidl
230 The following MIDL features are not (yet) implemented in pidl
231 or are implemented with an incompatible interface:
234 =item Asynchronous communication
235 =item Typelibs (.tlb files)
236 =item Datagram support (ncadg_*)
239 =head2 Supported attributes
241 in, out, ref, length_is, switch_is, size_is, uuid, case, default, string,
242 unique, ptr, pointer_default, v1_enum, object, helpstring, range, local,
243 call_as, endpoint, switch_type, progid, coclass, iid_is.
245 =head2 PIDL Specific properties
251 The [public] property on a structure or union is a pidl extension that
252 forces the generated pull/push functions to be non-static. This allows
253 you to declare types that can be used between modules. If you don't
254 specify [public] then pull/push functions for other than top-level
255 functions are declared static.
259 The [noprint] property is a pidl extension that allows you to specify
260 that pidl should not generate a ndr_print_*() function for that
261 structure or union. This is used when you wish to define your own
262 print function that prints a structure in a nicer manner. A good
263 example is the use of [noprint] on dom_sid, which allows the
264 pretty-printing of SIDs.
268 The [value(expression)] property is a pidl extension that allows you
269 to specify the value of a field when it is put on the wire. This
270 allows fields that always have a well-known value to be automatically
271 filled in, thus making the API more programmer friendly. The
272 expression can be any C expression.
276 The [relative] property can be supplied on a pointer. When it is used
277 it declares the pointer as a spoolss style "relative" pointer, which
278 means it appears on the wire as an offset within the current
279 encapsulating structure. This is not part of normal IDL/NDR, but it is
280 a very useful extension as it avoids the manual encoding of many
283 =item subcontext(length)
285 Specifies that a size of I<length>
286 bytes should be read, followed by a blob of that size,
287 which will be parsed as NDR.
291 Specify boolean options, mostly used for
292 low-level NDR options. Several options
293 can be specified using the | character.
294 Note that flags are inherited by substructures!
298 The [nodiscriminant] property on a union means that the usual uint16
299 discriminent field at the start of the union on the wire is
300 omitted. This is not normally allowed in IDL/NDR, but is used for some
305 Specify that the array or string uses the specified
306 charset. If this attribute is specified, pidl will
307 take care of converting the character data from this format
308 to the host format. Commonly used values are UCS2, DOS and UTF8.
312 =head2 Unsupported MIDL properties
314 aggregatable, appobject, async_uuid, bindable, control, cpp_quote,
315 defaultbind, defaultcollelem, defaultvalue, defaultvtable, dispinterface,
316 displaybind, dual, entry, first_is, helpcontext, helpfile, helpstringcontext,
317 helpstringdll, hidden, idl_module, idl_quote, id, immediatebind, importlib,
318 import, include, includelib, last_is, lcid, licensed, max_is, module,
319 ms_union, no_injected_text, nonbrowsable, noncreatable, nonextensible, odl,
320 oleautomation, optional, pragma, propget, propputref, propput, readonly,
321 requestedit, restricted, retval, source, transmit_as, uidefault,
322 usesgetlasterror, vararg, vi_progid, wire_marshal.
324 =head1 ETHEREAL CONFORMANCE FILES
326 Pidl needs additional data for ethereal output. This data is read from
327 so-called conformance files. This section describes the format of these
330 Conformance files are simple text files with a single command on each line.
331 Empty lines and lines starting with a '#' character are ignored.
332 Arguments to commands are seperated by spaces.
334 The following commands are currently supported:
338 =item TYPE name dissector ft_type base_type mask valsstring alignment
340 Register new data type with specified name, what dissector function to call
341 and what properties to give header fields for elements of this type.
345 Suppress emitting a dissect_type function for the specified type
347 =item PARAM_VALUE type param
349 Set parameter to specify to dissector function for given type.
351 =item HF_FIELD hf title filter ft_type base_type valsstring mask description
353 Generate a custom header field with specified properties.
355 =item HF_RENAME old_hf_name new_hf_name
357 Force the use of new_hf_name when the parser generator was going to
360 This can be used in conjunction with HF_FIELD in order to make more then
361 one element use the same filter name.
363 =item STRIP_PREFIX prefix
365 Remove the specified prefix from all function names (if present).
367 =item PROTOCOL longname shortname filtername
369 Change the short-, long- and filter-name for the current interface in
372 =item FIELD_DESCRIPTION field desc
374 Change description for the specified header field. `field' is the hf name of the field.
376 =item IMPORT dissector code...
378 Code to insert when generating the specified dissector. @HF@ and
379 @PARAM@ will be substituted.
385 # Generating an ethereal parser
386 $ ./pidl --eth-parser -- atsvc.idl
388 # Generating a TDR parser
389 $ ./pidl --tdr-parser --tdr-header --header -- regf.idl
393 This man page is correct for version 4.0 of the Samba suite. L<http://www.samba.org/>.
397 L<http://msdn.microsoft.com/library/en-us/rpc/rpc/field_attributes.asp>
398 L<http://wiki.ethereal.com/DCE/RPC>
403 pidl was written by Andrew Tridgell, Stefan Metzmacher, Tim Potter and Jelmer
406 This manpage was written by Jelmer Vernooij, partially based on the original
407 pidl README by Andrew Tridgell.
412 use FindBin qw($RealBin);
414 use lib "$RealBin/lib";
418 use Parse::Pidl::Util;
419 use Parse::Pidl::ODL;
421 #####################################################################
422 # save a data structure into a file
423 sub SaveStructure($$)
425 my($filename,$v) = @_;
426 FileSave($filename, Parse::Pidl::Util::MyDumper($v));
429 #####################################################################
430 # load a data structure from a file (as saved with SaveStructure)
434 my $contents = FileLoad($f);
435 defined $contents || return undef;
436 return eval "$contents";
439 #####################################################################
440 # read a file into a string
443 my($filename) = shift;
445 open(INPUTFILE, $filename) || return undef;
446 my($saved_delim) = $/;
448 my($data) = <INPUTFILE>;
454 #####################################################################
455 # write a string into a file
458 my($filename) = shift;
461 open(FILE, ">$filename") || die "can't open $filename";
467 my($opt_parse_idl_tree) = 0;
468 my($opt_dump_idl_tree);
469 my($opt_dump_ndr_tree);
470 my($opt_dump_idl) = 0;
471 my($opt_uint_enums) = 0;
475 my($opt_template) = 0;
487 my($opt_outputdir) = '.';
488 my($opt_verbose) = 0;
489 my($opt_warn_compat) = 0;
491 #########################################
495 print "perl IDL parser and code generator
496 Copyright (C) tridge\@samba.org
498 Usage: pidl [options] [--] <idlfile> [<idlfile>...]
501 --help this help page
502 --outputdir=OUTDIR put output in OUTDIR/ [.]
503 --warn-compat warn about incompatibility with other compilers
508 --dump-idl-tree[=FILE] dump internal representation to file [BASENAME.pidl]
509 --parse-idl-tree read internal representation instead of IDL
510 --dump-ndr-tree[=FILE] dump internal NDR data tree to file [BASENAME.ndr]
511 --dump-idl regenerate IDL file
512 --diff run diff on original IDL and dumped output
515 --header[=OUTFILE] create generic header file [BASENAME.h]
516 --uint-enums don't use C enums, instead use uint* types
517 --ndr-header[=OUTFILE] create a C NDR-specific header file [ndr_BASENAME.h]
518 --ndr-parser[=OUTFILE] create a C NDR parser [ndr_BASENAME.c]
519 --client[=OUTFILE] create a C NDR client [ndr_BASENAME_c.c]
520 --tdr-header[=OUTFILE] create a C TDR header file [tdr_BASENAME.h]
521 --tdr-parser[=OUTFILE] create a C TDR parser [tdr_BASENAME.c]
522 --ejs[=OUTFILE] create ejs wrapper file [BASENAME_ejs.c]
523 --swig[=OUTFILE] create swig wrapper file [BASENAME.i]
524 --server[=OUTFILE] create server boilerplate [ndr_BASENAME_s.c]
525 --template print a template for a pipe
526 --dcom-proxy[=OUTFILE] create DCOM proxy [ndr_BASENAME_p.c]
527 --com-header[=OUTFILE] create header for COM [com_BASENAME.h]
530 --eth-parser[=OUTFILE] create ethereal parser and header
537 'help|h|?' => \$opt_help,
538 'outputdir=s' => \$opt_outputdir,
539 'dump-idl' => \$opt_dump_idl,
540 'dump-idl-tree:s' => \$opt_dump_idl_tree,
541 'parse-idl-tree' => \$opt_parse_idl_tree,
542 'dump-ndr-tree:s' => \$opt_dump_ndr_tree,
543 'uint-enums' => \$opt_uint_enums,
544 'ndr-header:s' => \$opt_ndr_header,
545 'header:s' => \$opt_header,
546 'server:s' => \$opt_server,
547 'tdr-header:s' => \$opt_tdr_header,
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
570 my $idl_file = shift;
571 my $outputdir = $opt_outputdir;
575 my $basename = basename($idl_file, ".idl");
577 unless ($opt_quiet) { print "Compiling $idl_file\n"; }
579 if ($opt_parse_idl_tree) {
580 $pidl = LoadStructure($idl_file);
581 defined $pidl || die "Failed to load $idl_file";
583 require Parse::Pidl::IDL;
584 my $idl_parser = new Parse::Pidl::IDL;
586 $pidl = $idl_parser->parse_idl($idl_file);
587 defined @$pidl || die "Failed to parse $idl_file";
588 require Parse::Pidl::Typelist;
589 Parse::Pidl::Typelist::LoadIdl($pidl);
592 if (defined($opt_dump_idl_tree)) {
593 my($pidl_file) = ($opt_dump_idl_tree or "$outputdir/$basename.pidl");
594 SaveStructure($pidl_file, $pidl) or die "Failed to save $pidl_file\n";
597 if ($opt_uint_enums) {
598 Parse::Pidl::Util::setUseUintEnums(1);
602 require Parse::Pidl::Dump;
603 print Parse::Pidl::Dump($pidl);
607 my($tempfile) = "$outputdir/$basename.tmp";
608 FileSave($tempfile, IdlDump::Dump($pidl));
609 system("diff -wu $idl_file $tempfile");
613 if (defined($opt_com_header)) {
614 require Parse::Pidl::Samba::COM::Header;
615 my $res = Parse::Pidl::Samba::COM::Header::Parse($pidl);
617 my $comh_filename = ($opt_com_header or "$outputdir/com_$basename.h");
618 FileSave($comh_filename,
619 "#include \"librpc/gen_ndr/ndr_orpc.h\"\n" .
620 "#include \"$outputdir/ndr_$basename.h\"\n" .
625 if (defined($opt_dcom_proxy)) {
626 require Parse::Pidl::Samba::COM::Proxy;
627 my $res = Parse::Pidl::Samba::COM::Proxy::Parse($pidl);
629 my ($client) = ($opt_dcom_proxy or "$outputdir/$basename\_p.c");
631 "#include \"includes.h\"\n" .
632 "#include \"$outputdir/com_$basename.h\"\n" .
633 "#include \"lib/com/dcom/dcom.h\"\n" .$res);
637 if ($opt_warn_compat) {
638 require Parse::Pidl::Compat;
639 Parse::Pidl::Compat::Check($pidl);
642 $pidl = Parse::Pidl::ODL::ODL2IDL($pidl);
644 if (defined($opt_ndr_header) or defined($opt_eth_parser) or
645 defined($opt_client) or defined($opt_server) or
646 defined($opt_ndr_parser) or defined($opt_ejs) or
647 defined($opt_dump_ndr_tree)) {
648 require Parse::Pidl::NDR;
649 Parse::Pidl::NDR::Validate($pidl);
650 $ndr = Parse::Pidl::NDR::Parse($pidl);
653 if (defined($opt_dump_ndr_tree)) {
654 my($ndr_file) = ($opt_dump_ndr_tree or "$outputdir/$basename.ndr");
655 SaveStructure($ndr_file, $ndr) or die "Failed to save $ndr_file\n";
658 if (defined($opt_header)) {
659 my $header = ($opt_header or "$outputdir/$basename.h");
660 require Parse::Pidl::Samba::Header;
661 FileSave($header, Parse::Pidl::Samba::Header::Parse($pidl));
664 if (defined($opt_ndr_header)) {
665 my $header = ($opt_ndr_header or "$outputdir/ndr_$basename.h");
666 require Parse::Pidl::Samba::NDR::Header;
667 FileSave($header, Parse::Pidl::Samba::NDR::Header::Parse($pidl, $basename));
668 if (defined($opt_swig)) {
669 require Parse::Pidl::Samba::SWIG;
670 my($filename) = ($opt_swig or "$outputdir/$basename.i");
671 Parse::Pidl::Samba::SWIG::RewriteHeader($pidl, $header, $filename);
675 my $h_filename = "$outputdir/ndr_$basename.h";
676 if (defined($opt_client)) {
677 require Parse::Pidl::Samba::NDR::Client;
678 my ($client) = ($opt_client or "$outputdir/ndr_$basename\_c.c");
680 FileSave($client, Parse::Pidl::Samba::NDR::Client::Parse($ndr,$h_filename));
683 if (defined($opt_ejs)) {
684 require Parse::Pidl::Samba::EJS;
685 require Parse::Pidl::Samba::EJSHeader;
686 FileSave("$outputdir/ndr_$basename\_ejs.c", Parse::Pidl::Samba::EJS::Parse($ndr, $h_filename));
688 FileSave("$outputdir/ndr_$basename\_ejs.h", Parse::Pidl::Samba::EJSHeader::Parse($ndr));
691 if (defined($opt_server)) {
692 require Parse::Pidl::Samba::NDR::Server;
695 foreach my $x (@{$pidl}) {
696 next if ($x->{TYPE} ne "INTERFACE");
698 if (Parse::Pidl::Util::has_property($x, "object")) {
699 require Parse::Pidl::Samba::COM::Stub;
700 $dcom .= Parse::Pidl::Samba::COM::Stub::ParseInterface($x);
704 FileSave(($opt_server or "$outputdir/ndr_$basename\_s.c"), Parse::Pidl::Samba::NDR::Server::Parse($ndr,$h_filename));
708 #include \"includes.h\"
709 #include \"$h_filename\"
710 #include \"rpc_server/dcerpc_server.h\"
711 #include \"rpc_server/common/common.h\"
715 FileSave("$outputdir/$basename\_d.c", $dcom);
719 if (defined($opt_ndr_parser)) {
720 my $parser = ($opt_ndr_parser or "$outputdir/ndr_$basename.c");
721 require Parse::Pidl::Samba::NDR::Parser;
722 FileSave($parser, Parse::Pidl::Samba::NDR::Parser::Parse($ndr, $parser));
725 if (defined($opt_eth_parser)) {
726 require Parse::Pidl::Ethereal::NDR;
727 my($eparser) = ($opt_eth_parser or "$outputdir/packet-dcerpc-$basename.c");
728 my $eheader = $eparser;
729 $eheader =~ s/\.c$/\.h/;
730 my $cnffile = $idl_file;
731 $cnffile =~ s/\.idl$/\.cnf/;
733 my ($dp, $dh) = Parse::Pidl::Ethereal::NDR::Parse($ndr, $idl_file, $eheader, $cnffile);
734 FileSave($eparser, $dp) if defined($dp);
735 FileSave($eheader, $dh) if defined($dh);
738 my $tdr_parser = ($opt_tdr_parser or "$outputdir/tdr_$basename.c");
739 my $tdr_header = ($opt_tdr_header or "$outputdir/tdr_$basename.h");
740 if (defined($opt_tdr_parser)) {
741 require Parse::Pidl::Samba::TDR;
742 FileSave($tdr_parser, Parse::Pidl::Samba::TDR::Parser($pidl, $tdr_header));
745 if (defined($opt_tdr_header)) {
746 require Parse::Pidl::Samba::TDR;
747 FileSave($tdr_header, Parse::Pidl::Samba::TDR::Header($pidl, $outputdir,$basename));
751 require Parse::Pidl::Samba::Template;
752 print Parse::Pidl::Samba::Template::Parse($pidl);
756 if (scalar(@ARGV) == 0) {
757 print "pidl: no input files\n";
761 process_file($_) foreach (@ARGV);