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-2007
8
# released under the GNU GPL
14
pidl - An IDL compiler written in Perl
20
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]...
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 Wireshark 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 Wireshark
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.
60
=item I<--outputdir OUTNAME>
62
Write output files to the specified directory. Defaults to the current
65
=item I<--includedir DIR>
67
Add DIR to the search path used by the preprocessor. This option can be
68
specified multiple times.
70
=item I<--parse-idl-tree>
72
Read internal tree structure from input files rather
73
than assuming they contain IDL.
77
Generate a new IDL file. File will be named OUTNAME.idl.
81
Generate a C header file for the specified interface. Filename defaults to OUTNAME.h.
85
Generate a C file and C header containing NDR parsers. The filename for
86
the parser defaults to ndr_OUTNAME.c. The header filename will be the
87
parser filename with the extension changed from .c to .h.
91
Generate a C file and C header containing TDR parsers. The filename for
92
the parser defaults to tdr_OUTNAME.c. The header filename will be the
93
parser filename with the extension changed from .c to .h.
97
Write type information to the specified file.
101
Generate boilerplate for the RPC server that implements
102
the interface. Filename defaults to ndr_OUTNAME_s.c.
106
Generate stubs for a RPC server that implements the interface. Output will
107
be written to stdout.
111
Generate an Wireshark dissector (in C) and header file. The dissector filename
112
defaults to packet-dcerpc-OUTNAME.c while the header filename defaults to
113
packet-dcerpc-OUTNAME.h.
115
Pidl will read additional data from an Wireshark conformance file if present.
116
Such a file should have the same location as the IDL file but with the
117
extension I<cnf> rather than I<idl>. See L<Parse::Pidl::Wireshark::Conformance>
118
for details on the format of this file.
122
Parse an IDL file, generate a new IDL file based on the internal data
123
structures and see if there are any differences with the original IDL file.
124
Useful for debugging pidl.
126
=item I<--dump-idl-tree>
128
Tell pidl to dump the internal tree representation of an IDL
129
file the to disk. Useful for debugging pidl.
131
=item I<--dump-ndr-tree>
133
Tell pidl to dump the internal NDR information tree it generated
134
from the IDL file to disk. Useful for debugging pidl.
136
=item I<--samba3-ndr-client>
138
Generate client calls for Samba3, to be placed in rpc_client/. Instead of
139
calling out to the code in Samba3's rpc_parse/, this will call out to
140
Samba4's NDR code instead.
142
=item I<--samba3-ndr-server>
144
Generate server calls for Samba3, to be placed in rpc_server/. Instead of
145
calling out to the code in Samba3's rpc_parse/, this will call out to
146
Samba4's NDR code instead.
152
IDL files are always preprocessed using the C preprocessor.
154
Pretty much everything in an interface (the interface itself, functions,
155
parameters) can have attributes (or properties whatever name you give them).
156
Attributes always prepend the element they apply to and are surrounded
157
by square brackets ([]). Multiple attributes are separated by comma's;
158
arguments to attributes are specified between parentheses.
160
See the section COMPATIBILITY for the list of attributes that
163
C-style comments can be used.
165
=head2 CONFORMANT ARRAYS
167
A conformant array is one with that ends in [*] or []. The strange
168
things about conformant arrays are that they can only appear as the last
169
element of a structure (unless there is a pointer to the conformant array,
170
of course) and the array size appears before the structure itself on the wire.
178
[size_is(count)] long s[*];
181
it appears like this:
183
[size_is] [abc] [count] [foo] [s...]
185
the first [size_is] field is the allocation size of the array, and
186
occurs before the array elements and even before the structure
189
Note that size_is() can refer to a constant, but that doesn't change
190
the wire representation. It does not make the array a fixed array.
192
midl.exe would write the above array as the following C header:
201
pidl takes a different approach, and writes it like this:
210
=head2 VARYING ARRAYS
212
A varying array looks like this:
218
[size_is(count)] long *s;
221
This will look like this on the wire:
223
[abc] [count] [foo] [PTR_s] [count] [s...]
227
A fixed array looks like this:
233
The NDR representation looks just like 10 separate long
234
declarations. The array size is not encoded on the wire.
236
pidl also supports "inline" arrays, which are not part of the IDL/NDR
237
standard. These are declared like this:
246
This appears like this:
248
[foo] [count] [bar] [s...]
250
Fixed arrays are an extension added to support some of the strange
251
embedded structures in security descriptors and spoolss.
253
This section is by no means complete. See the OpenGroup and MSDN
254
documentation for additional information.
256
=head1 COMPATIBILITY WITH MIDL
258
=head2 Missing features in pidl
260
The following MIDL features are not (yet) implemented in pidl
261
or are implemented with an incompatible interface:
267
Asynchronous communication
271
Typelibs (.tlb files)
275
Datagram support (ncadg_*)
279
=head2 Supported attributes and statements
281
in, out, ref, length_is, switch_is, size_is, uuid, case, default, string,
282
unique, ptr, pointer_default, v1_enum, object, helpstring, range, local,
283
call_as, endpoint, switch_type, progid, coclass, iid_is, represent_as,
284
transmit_as, import, include, cpp_quote.
286
=head2 PIDL Specific properties
292
The [public] property on a structure or union is a pidl extension that
293
forces the generated pull/push functions to be non-static. This allows
294
you to declare types that can be used between modules. If you don't
295
specify [public] then pull/push functions for other than top-level
296
functions are declared static.
300
The [noprint] property is a pidl extension that allows you to specify
301
that pidl should not generate a ndr_print_*() function for that
302
structure or union. This is used when you wish to define your own
303
print function that prints a structure in a nicer manner. A good
304
example is the use of [noprint] on dom_sid, which allows the
305
pretty-printing of SIDs.
309
The [value(expression)] property is a pidl extension that allows you
310
to specify the value of a field when it is put on the wire. This
311
allows fields that always have a well-known value to be automatically
312
filled in, thus making the API more programmer friendly. The
313
expression can be any C expression.
317
The [relative] property can be supplied on a pointer. When it is used
318
it declares the pointer as a spoolss style "relative" pointer, which
319
means it appears on the wire as an offset within the current
320
encapsulating structure. This is not part of normal IDL/NDR, but it is
321
a very useful extension as it avoids the manual encoding of many
324
=item subcontext(length)
326
Specifies that a size of I<length>
327
bytes should be read, followed by a blob of that size,
328
which will be parsed as NDR.
330
subcontext() is deprecated now, and should not be used in new code.
331
Instead, use represent_as() or transmit_as().
335
Specify boolean options, mostly used for
336
low-level NDR options. Several options
337
can be specified using the | character.
338
Note that flags are inherited by substructures!
342
The [nodiscriminant] property on a union means that the usual uint16
343
discriminent field at the start of the union on the wire is
344
omitted. This is not normally allowed in IDL/NDR, but is used for some
349
Specify that the array or string uses the specified
350
charset. If this attribute is specified, pidl will
351
take care of converting the character data from this format
352
to the host format. Commonly used values are UCS2, DOS and UTF8.
356
=head2 Unsupported MIDL properties or statements
358
aggregatable, appobject, async_uuid, bindable, control,
359
defaultbind, defaultcollelem, defaultvalue, defaultvtable, dispinterface,
360
displaybind, dual, entry, first_is, helpcontext, helpfile, helpstringcontext,
361
helpstringdll, hidden, idl_module, idl_quote, id, immediatebind, importlib,
362
includelib, last_is, lcid, licensed, max_is, module,
363
ms_union, no_injected_text, nonbrowsable, noncreatable, nonextensible, odl,
364
oleautomation, optional, pragma, propget, propputref, propput, readonly,
365
requestedit, restricted, retval, source, uidefault,
366
usesgetlasterror, vararg, vi_progid, wire_marshal.
370
# Generating an Wireshark parser
371
$ ./pidl --ws-parser -- atsvc.idl
373
# Generating a TDR parser and header
374
$ ./pidl --tdr-parser --header -- regf.idl
376
# Generating a Samba3 client and server
377
$ ./pidl --samba3-ndr-client --samba3-ndr-server -- dfs.idl
379
# Generating a Samba4 NDR parser, client and server
380
$ ./pidl --ndr-parser --ndr-client --ndr-server -- samr.idl
384
L<http://msdn.microsoft.com/library/en-us/rpc/rpc/field_attributes.asp>,
385
L<http://wiki.wireshark.org/DCE/RPC>,
386
L<http://www.samba.org/>,
391
pidl is licensed under the GNU General Public License L<http://www.gnu.org/licenses/gpl.html>.
395
pidl was written by Andrew Tridgell, Stefan Metzmacher, Tim Potter and Jelmer
396
Vernooij. The current maintainer is Jelmer Vernooij.
398
This manpage was written by Jelmer Vernooij, partially based on the original
399
pidl README by Andrew Tridgell.
405
use FindBin qw($RealBin $Script);
406
use lib "$RealBin/lib";
407
use lib "$RealBin/../share/perl5";
410
use Parse::Pidl qw ( $VERSION );
411
use Parse::Pidl::Util;
412
use Parse::Pidl::ODL;
414
#####################################################################
415
# save a data structure into a file
416
sub SaveStructure($$)
418
my($filename,$v) = @_;
419
FileSave($filename, Parse::Pidl::Util::MyDumper($v));
422
#####################################################################
423
# load a data structure from a file (as saved with SaveStructure)
427
my $contents = FileLoad($f);
428
defined $contents || return undef;
429
return eval "$contents";
432
#####################################################################
433
# read a file into a string
436
my($filename) = shift;
438
open(INPUTFILE, $filename) || return undef;
439
my($saved_delim) = $/;
441
my($data) = <INPUTFILE>;
447
#####################################################################
448
# write a string into a file
451
my($filename) = shift;
454
open(FILE, ">$filename") || die "can't open $filename";
459
my(@opt_incdirs) = ();
461
my($opt_version) = 0;
462
my($opt_parse_idl_tree) = 0;
463
my($opt_dump_idl_tree);
464
my($opt_dump_ndr_tree);
465
my($opt_dump_idl) = 0;
468
my($opt_samba3_header);
469
my($opt_samba3_parser);
470
my($opt_samba3_server);
471
my($opt_samba3_ndr_client);
472
my($opt_samba3_ndr_server);
473
my($opt_template) = 0;
484
my($opt_outputdir) = '.';
485
my($opt_verbose) = 0;
486
my($opt_warn_compat) = 0;
490
#########################################
494
print "perl IDL parser and code generator\n";
497
Copyright (C) Andrew Tridgell <tridge\@samba.org>
498
Copyright (C) Jelmer Vernooij <jelmer\@samba.org>
500
Usage: $Script [options] [--] <idlfile> [<idlfile>...]
503
--help this help page
504
--version show pidl version
505
--outputdir=OUTDIR put output in OUTDIR/ [.]
506
--warn-compat warn about incompatibility with other compilers
509
--includedir DIR search DIR for included files
512
--dump-idl-tree[=FILE] dump internal representation to file [BASENAME.pidl]
513
--parse-idl-tree read internal representation instead of IDL
514
--dump-ndr-tree[=FILE] dump internal NDR data tree to file [BASENAME.ndr]
515
--dump-idl regenerate IDL file
516
--diff run diff on original IDL and dumped output
517
--typelib print type information
520
--header[=OUTFILE] create generic header file [BASENAME.h]
521
--ndr-parser[=OUTFILE] create a C NDR parser [ndr_BASENAME.c]
522
--client[=OUTFILE] create a C NDR client [ndr_BASENAME_c.c]
523
--tdr-parser[=OUTFILE] create a C TDR parser [tdr_BASENAME.c]
524
--ejs[=OUTFILE] create ejs wrapper file [BASENAME_ejs.c]
525
--python[=OUTFILE] create python wrapper file [py_BASENAME.c]
526
--swig[=OUTFILE] create swig wrapper file [BASENAME.i]
527
--server[=OUTFILE] create server boilerplate [ndr_BASENAME_s.c]
528
--template print a template for a pipe
529
--dcom-proxy[=OUTFILE] create DCOM proxy [ndr_BASENAME_p.c]
530
--com-header[=OUTFILE] create header for COM [com_BASENAME.h]
533
--samba3-ndr-client[=OUTF] create client calls for Samba3
534
using Samba4's NDR code [cli_BASENAME.c]
535
--samba3-ndr-server[=OUTF] create server call wrapper for Samba3
536
using Samba4's NDR code [srv_BASENAME.c]
539
--ws-parser[=OUTFILE] create Wireshark parser and header
544
#########################################
548
print "perl IDL version $VERSION\n";
552
my $result = GetOptions (
553
'help|h|?' => \$opt_help,
554
'version' => \$opt_version,
555
'outputdir=s' => \$opt_outputdir,
556
'dump-idl' => \$opt_dump_idl,
557
'dump-idl-tree:s' => \$opt_dump_idl_tree,
558
'parse-idl-tree' => \$opt_parse_idl_tree,
559
'dump-ndr-tree:s' => \$opt_dump_ndr_tree,
560
'samba3-ndr-client:s' => \$opt_samba3_ndr_client,
561
'samba3-ndr-server:s' => \$opt_samba3_ndr_server,
562
'header:s' => \$opt_header,
563
'server:s' => \$opt_server,
564
'typelib:s' => \$opt_typelib,
565
'tdr-parser:s' => \$opt_tdr_parser,
566
'template' => \$opt_template,
567
'ndr-parser:s' => \$opt_ndr_parser,
568
'client:s' => \$opt_client,
569
'ws-parser:s' => \$opt_ws_parser,
571
'python' => \$opt_python,
572
'diff' => \$opt_diff,
573
'swig:s' => \$opt_swig,
574
'dcom-proxy:s' => \$opt_dcom_proxy,
575
'com-header:s' => \$opt_com_header,
576
'quiet' => \$opt_quiet,
577
'verbose' => \$opt_verbose,
578
'warn-compat' => \$opt_warn_compat,
579
'includedir=s@' => \@opt_incdirs
598
my $idl_file = shift;
599
my $outputdir = $opt_outputdir;
603
my $basename = basename($idl_file, ".idl");
605
unless ($opt_quiet) { print "Compiling $idl_file\n"; }
607
if ($opt_parse_idl_tree) {
608
$pidl = LoadStructure($idl_file);
609
defined $pidl || die "Failed to load $idl_file";
611
require Parse::Pidl::IDL;
613
$pidl = Parse::Pidl::IDL::parse_file($idl_file, \@opt_incdirs);
614
defined @$pidl || die "Failed to parse $idl_file";
617
require Parse::Pidl::Typelist;
618
Parse::Pidl::Typelist::LoadIdl($pidl);
620
if (defined($opt_dump_idl_tree)) {
621
my($pidl_file) = ($opt_dump_idl_tree or "$outputdir/$basename.pidl");
622
SaveStructure($pidl_file, $pidl) or die "Failed to save $pidl_file\n";
626
require Parse::Pidl::Dump;
627
print Parse::Pidl::Dump($pidl);
631
my($tempfile) = "$outputdir/$basename.tmp";
632
FileSave($tempfile, IdlDump::Dump($pidl));
633
system("diff -wu $idl_file $tempfile");
637
my $comh_filename = ($opt_com_header or "$outputdir/com_$basename.h");
638
if (defined($opt_com_header)) {
639
require Parse::Pidl::Samba4::COM::Header;
640
my $res = Parse::Pidl::Samba4::COM::Header::Parse($pidl,"$outputdir/ndr_$basename.h");
642
FileSave($comh_filename, $res);
646
if (defined($opt_dcom_proxy)) {
647
require Parse::Pidl::Samba4::COM::Proxy;
648
my $res = Parse::Pidl::Samba4::COM::Proxy::Parse($pidl,$comh_filename);
650
my ($client) = ($opt_dcom_proxy or "$outputdir/$basename\_p.c");
651
FileSave($client, $res);
655
if ($opt_warn_compat) {
656
require Parse::Pidl::Compat;
657
Parse::Pidl::Compat::Check($pidl);
660
$pidl = Parse::Pidl::ODL::ODL2IDL($pidl, dirname($idl_file), \@opt_incdirs);
662
if (defined($opt_ws_parser) or
663
defined($opt_client) or
664
defined($opt_server) or
665
defined($opt_header) or
666
defined($opt_ndr_parser) or
668
defined($opt_python) or
669
defined($opt_dump_ndr_tree) or
670
defined($opt_samba3_header) or
671
defined($opt_samba3_parser) or
672
defined($opt_samba3_server) or
673
defined($opt_swig) or
674
defined($opt_samba3_ndr_client) or
675
defined($opt_samba3_ndr_server)) {
676
require Parse::Pidl::NDR;
677
$ndr = Parse::Pidl::NDR::Parse($pidl);
680
if (defined($opt_dump_ndr_tree)) {
681
my($ndr_file) = ($opt_dump_ndr_tree or "$outputdir/$basename.ndr");
682
SaveStructure($ndr_file, $ndr) or die "Failed to save $ndr_file\n";
685
my $gen_header = ($opt_header or "$outputdir/$basename.h");
686
if (defined($opt_header)) {
687
require Parse::Pidl::Samba4::Header;
688
FileSave($gen_header, Parse::Pidl::Samba4::Header::Parse($ndr));
691
my $h_filename = "$outputdir/ndr_$basename.h";
692
if (defined($opt_client)) {
693
require Parse::Pidl::Samba4::NDR::Client;
694
my ($c_client) = ($opt_client or "$outputdir/ndr_$basename\_c.c");
695
my ($c_header) = $c_client;
696
$c_header =~ s/\.c$/.h/;
698
my ($srcd,$hdrd) = Parse::Pidl::Samba4::NDR::Client::Parse(
699
$ndr,$gen_header,$h_filename,$c_header);
701
FileSave($c_client, $srcd);
702
FileSave($c_header, $hdrd);
705
if (defined($opt_swig)) {
706
require Parse::Pidl::Samba4::SWIG;
707
my($filename) = ($opt_swig or "$outputdir/$basename.i");
708
my $code = Parse::Pidl::Samba4::SWIG::Parse($ndr, $basename, "$outputdir/ndr_$basename\_c.h", $gen_header);
709
FileSave($filename, $code);
712
if (defined($opt_ejs)) {
713
require Parse::Pidl::Samba4::EJS;
714
my $generator = new Parse::Pidl::Samba4::EJS();
715
my ($hdr,$prsr) = $generator->Parse($ndr, $h_filename);
716
FileSave("$outputdir/ndr_$basename\_ejs.c", $prsr);
717
FileSave("$outputdir/ndr_$basename\_ejs.h", $hdr);
720
if (defined($opt_python)) {
721
require Parse::Pidl::Samba4::Python;
722
my $generator = new Parse::Pidl::Samba4::Python();
723
my ($hdr,$prsr) = $generator->Parse($basename, $ndr,
724
"$outputdir/ndr_$basename\_c.h", $h_filename);
725
FileSave("$outputdir/py_$basename.c", $prsr);
726
FileSave("$outputdir/py_$basename.h", $hdr);
729
if (defined($opt_server)) {
730
require Parse::Pidl::Samba4::NDR::Server;
732
FileSave(($opt_server or "$outputdir/ndr_$basename\_s.c"), Parse::Pidl::Samba4::NDR::Server::Parse($ndr,$h_filename));
735
if (defined($opt_ndr_parser)) {
736
my $parser_fname = ($opt_ndr_parser or "$outputdir/ndr_$basename.c");
737
require Parse::Pidl::Samba4::NDR::Parser;
738
my $generator = new Parse::Pidl::Samba4::NDR::Parser();
739
my ($header,$parser) = $generator->Parse($ndr, $gen_header, $h_filename);
741
FileSave($parser_fname, $parser);
742
FileSave($h_filename, $header);
746
if (defined($opt_ws_parser)) {
747
require Parse::Pidl::Wireshark::NDR;
748
my($eparser) = ($opt_ws_parser or "$outputdir/packet-dcerpc-$basename.c");
749
my $eheader = $eparser;
750
$eheader =~ s/\.c$/\.h/;
751
my $cnffile = $idl_file;
752
$cnffile =~ s/\.idl$/\.cnf/;
754
my $generator = new Parse::Pidl::Wireshark::NDR();
755
my ($dp, $dh) = $generator->Parse($ndr, $idl_file, $eheader, $cnffile);
756
FileSave($eparser, $dp) if defined($dp);
757
FileSave($eheader, $dh) if defined($dh);
760
if (defined($opt_tdr_parser)) {
761
my $tdr_parser = ($opt_tdr_parser or "$outputdir/tdr_$basename.c");
762
my $tdr_header = $tdr_parser;
763
$tdr_header =~ s/\.c$/\.h/;
764
require Parse::Pidl::Samba4::TDR;
765
my $generator = new Parse::Pidl::Samba4::TDR();
766
my ($hdr,$prsr) = $generator->Parser($pidl, $tdr_header, $gen_header);
767
FileSave($tdr_parser, $prsr);
768
FileSave($tdr_header, $hdr);
771
if (defined($opt_typelib)) {
772
my $typelib = ($opt_typelib or "$outputdir/$basename.tlb");
773
require Parse::Pidl::Typelist;
774
FileSave($typelib, Parse::Pidl::Typelist::GenerateTypeLib());
778
require Parse::Pidl::Samba4::Template;
779
print Parse::Pidl::Samba4::Template::Parse($pidl);
782
if (defined($opt_samba3_ndr_client)) {
783
my $client = ($opt_samba3_ndr_client or "$outputdir/cli_$basename.c");
784
my $header = $client; $header =~ s/\.c$/\.h/;
785
require Parse::Pidl::Samba3::ClientNDR;
786
my $generator = new Parse::Pidl::Samba3::ClientNDR();
787
my ($c_code,$h_code) = $generator->Parse($ndr, $header, $h_filename);
788
FileSave($client, $c_code);
789
FileSave($header, $h_code);
792
if (defined($opt_samba3_ndr_server)) {
793
my $server = ($opt_samba3_ndr_server or "$outputdir/srv_$basename.c");
794
my $header = $server; $header =~ s/\.c$/\.h/;
795
require Parse::Pidl::Samba3::ServerNDR;
796
my ($c_code,$h_code) = Parse::Pidl::Samba3::ServerNDR::Parse($ndr, $header, $h_filename);
797
FileSave($server, $c_code);
798
FileSave($header, $h_code);
803
if (scalar(@ARGV) == 0) {
804
print "$Script: no input files\n";
808
process_file($_) foreach (@ARGV);