bb497bb3a77471e331bdf91128efec86b26bae3e
[ira/wip.git] / pidl / lib / Parse / Pidl / Samba4 / Header.pm
1 ###################################################
2 # create C header files for an IDL structure
3 # Copyright tridge@samba.org 2000
4 # Copyright jelmer@samba.org 2005
5 # released under the GNU GPL
6
7 package Parse::Pidl::Samba4::Header;
8
9 require Exporter;
10
11 @ISA = qw(Exporter);
12 @EXPORT_OK = qw(GenerateFunctionInEnv GenerateFunctionOutEnv EnvSubstituteValue GenerateStructEnv);
13
14 use strict;
15 use Parse::Pidl qw(fatal);
16 use Parse::Pidl::Typelist qw(mapTypeName scalar_is_reference);
17 use Parse::Pidl::Util qw(has_property is_constant unmake_str ParseExpr);
18 use Parse::Pidl::Samba4 qw(is_intree ElementStars ArrayBrackets choose_header);
19
20 use vars qw($VERSION);
21 $VERSION = '0.01';
22
23 my($res);
24 my($tab_depth);
25
26 sub pidl($) { $res .= shift; }
27
28 sub tabs()
29 {
30         my $res = "";
31         $res .="\t" foreach (1..$tab_depth);
32         return $res;
33 }
34
35 #####################################################################
36 # parse a properties list
37 sub HeaderProperties($$)
38 {
39         my($props,$ignores) = @_;
40         my $ret = "";
41
42         foreach my $d (keys %{$props}) {
43                 next if (grep(/^$d$/, @$ignores));
44                 if($props->{$d} ne "1") {
45                         $ret.= "$d($props->{$d}),";
46                 } else {
47                         $ret.="$d,";
48                 }
49         }
50
51         if ($ret) {
52                 pidl "/* [" . substr($ret, 0, -1) . "] */";
53         }
54 }
55
56 #####################################################################
57 # parse a structure element
58 sub HeaderElement($)
59 {
60         my($element) = shift;
61
62         pidl tabs();
63         if (has_property($element, "represent_as")) {
64                 pidl mapTypeName($element->{PROPERTIES}->{represent_as})." ";
65         } else {
66                 if (ref($element->{TYPE}) eq "HASH") {
67                         HeaderType($element, $element->{TYPE}, $element->{TYPE}->{NAME});
68                 } else {
69                         HeaderType($element, $element->{TYPE}, "");
70                 }
71                 pidl " ".ElementStars($element);
72         }
73         pidl $element->{NAME};
74         pidl ArrayBrackets($element);
75
76         pidl ";";
77         if (defined $element->{PROPERTIES}) {
78                 HeaderProperties($element->{PROPERTIES}, ["in", "out"]);
79         }
80         pidl "\n";
81 }
82
83 #####################################################################
84 # parse a struct
85 sub HeaderStruct($$;$)
86 {
87         my($struct,$name,$tail) = @_;
88         pidl "struct $name";
89         pidl $tail if defined($tail) and not defined($struct->{ELEMENTS});
90         return if (not defined($struct->{ELEMENTS}));
91         pidl " {\n";
92         $tab_depth++;
93         my $el_count=0;
94         foreach (@{$struct->{ELEMENTS}}) {
95                 HeaderElement($_);
96                 $el_count++;
97         }
98         if ($el_count == 0) {
99                 # some compilers can't handle empty structures
100                 pidl tabs()."char _empty_;\n";
101         }
102         $tab_depth--;
103         pidl tabs()."}";
104         if (defined $struct->{PROPERTIES}) {
105                 HeaderProperties($struct->{PROPERTIES}, []);
106         }
107         pidl $tail if defined($tail);
108 }
109
110 #####################################################################
111 # parse a enum
112 sub HeaderEnum($$;$)
113 {
114         my($enum,$name,$tail) = @_;
115         my $first = 1;
116
117         pidl "enum $name";
118         if (defined($enum->{ELEMENTS})) {
119                 pidl "\n#ifndef USE_UINT_ENUMS\n";
120                 pidl " {\n";
121                 $tab_depth++;
122                 foreach my $e (@{$enum->{ELEMENTS}}) {
123                         my @enum_els = ();
124                         unless ($first) { pidl ",\n"; }
125                         $first = 0;
126                         pidl tabs();
127                         @enum_els = split(/=/, $e);
128                         if (@enum_els == 2) {
129                                 pidl $enum_els[0];
130                                 pidl "=(int)";
131                                 pidl $enum_els[1];
132                         } else {
133                                 pidl $e;
134                         }
135                 }
136                 pidl "\n";
137                 $tab_depth--;
138                 pidl "}";
139                 pidl "\n";
140                 pidl "#else\n";
141                 my $count = 0;
142                 my $with_val = 0;
143                 my $without_val = 0;
144                 pidl " { __donnot_use_enum_$name=0x7FFFFFFF}\n";
145                 foreach my $e (@{$enum->{ELEMENTS}}) {
146                         my $t = "$e";
147                         my $name;
148                         my $value;
149                         if ($t =~ /(.*)=(.*)/) {
150                                 $name = $1;
151                                 $value = $2;
152                                 $with_val = 1;
153                                 fatal($e->{ORIGINAL}, "you can't mix enum member with values and without values!")
154                                         unless ($without_val == 0);
155                         } else {
156                                 $name = $t;
157                                 $value = $count++;
158                                 $without_val = 1;
159                                 fatal($e->{ORIGINAL}, "you can't mix enum member with values and without values!")
160                                         unless ($with_val == 0);
161                         }
162                         pidl "#define $name ( $value )\n";
163                 }
164                 pidl "#endif\n";
165         }
166         pidl $tail if defined($tail);
167 }
168
169 #####################################################################
170 # parse a bitmap
171 sub HeaderBitmap($$)
172 {
173         my($bitmap,$name) = @_;
174
175         return unless defined($bitmap->{ELEMENTS});
176
177         pidl "/* bitmap $name */\n";
178         pidl "#define $_\n" foreach (@{$bitmap->{ELEMENTS}});
179         pidl "\n";
180 }
181
182 #####################################################################
183 # parse a union
184 sub HeaderUnion($$;$)
185 {
186         my($union,$name,$tail) = @_;
187         my %done = ();
188
189         pidl "union $name";
190         pidl $tail if defined($tail) and not defined($union->{ELEMENTS});
191         return if (not defined($union->{ELEMENTS}));
192         pidl " {\n";
193         $tab_depth++;
194         my $needed = 0;
195         foreach my $e (@{$union->{ELEMENTS}}) {
196                 if ($e->{TYPE} ne "EMPTY") {
197                         if (! defined $done{$e->{NAME}}) {
198                                 HeaderElement($e);
199                         }
200                         $done{$e->{NAME}} = 1;
201                         $needed++;
202                 }
203         }
204         if (!$needed) {
205                 # sigh - some compilers don't like empty structures
206                 pidl tabs()."int _dummy_element;\n";
207         }
208         $tab_depth--;
209         pidl "}";
210
211         if (defined $union->{PROPERTIES}) {
212                 HeaderProperties($union->{PROPERTIES}, []);
213         }
214         pidl $tail if defined($tail);
215 }
216
217 #####################################################################
218 # parse a type
219 sub HeaderType($$$;$)
220 {
221         my($e,$data,$name,$tail) = @_;
222         if (ref($data) eq "HASH") {
223                 ($data->{TYPE} eq "ENUM") && HeaderEnum($data, $name, $tail);
224                 ($data->{TYPE} eq "BITMAP") && HeaderBitmap($data, $name);
225                 ($data->{TYPE} eq "STRUCT") && HeaderStruct($data, $name, $tail);
226                 ($data->{TYPE} eq "UNION") && HeaderUnion($data, $name, $tail);
227                 return;
228         }
229
230         if (has_property($e, "charset")) {
231                 pidl "const char";
232         } else {
233                 pidl mapTypeName($e->{TYPE});
234         }
235         pidl $tail if defined($tail);
236 }
237
238 #####################################################################
239 # parse a typedef
240 sub HeaderTypedef($;$)
241 {
242         my($typedef,$tail) = @_;
243         # Don't print empty "enum foo;", since some compilers don't like it.
244         return if ($typedef->{DATA}->{TYPE} eq "ENUM" and not defined($typedef->{DATA}->{ELEMENTS}));
245         HeaderType($typedef, $typedef->{DATA}, $typedef->{NAME}, $tail) if defined ($typedef->{DATA});
246 }
247
248 #####################################################################
249 # parse a const
250 sub HeaderConst($)
251 {
252         my($const) = shift;
253         if (!defined($const->{ARRAY_LEN}[0])) {
254                 pidl "#define $const->{NAME}\t( $const->{VALUE} )\n";
255         } else {
256                 pidl "#define $const->{NAME}\t $const->{VALUE}\n";
257         }
258 }
259
260 sub ElementDirection($)
261 {
262         my ($e) = @_;
263
264         return "inout" if (has_property($e, "in") and has_property($e, "out"));
265         return "in" if (has_property($e, "in"));
266         return "out" if (has_property($e, "out"));
267         return "inout";
268 }
269
270 #####################################################################
271 # parse a function
272 sub HeaderFunctionInOut($$)
273 {
274         my($fn,$prop) = @_;
275
276         return unless defined($fn->{ELEMENTS});
277
278         foreach my $e (@{$fn->{ELEMENTS}}) {
279                 HeaderElement($e) if (ElementDirection($e) eq $prop);
280         }
281 }
282
283 #####################################################################
284 # determine if we need an "in" or "out" section
285 sub HeaderFunctionInOut_needed($$)
286 {
287         my($fn,$prop) = @_;
288
289         return 1 if ($prop eq "out" && defined($fn->{RETURN_TYPE}));
290
291         return undef unless defined($fn->{ELEMENTS});
292
293         foreach my $e (@{$fn->{ELEMENTS}}) {
294                 return 1 if (ElementDirection($e) eq $prop);
295         }
296
297         return undef;
298 }
299
300 my %headerstructs;
301
302 #####################################################################
303 # parse a function
304 sub HeaderFunction($)
305 {
306         my($fn) = shift;
307
308         return if ($headerstructs{$fn->{NAME}});
309
310         $headerstructs{$fn->{NAME}} = 1;
311
312         pidl "\nstruct $fn->{NAME} {\n";
313         $tab_depth++;
314         my $needed = 0;
315
316         if (HeaderFunctionInOut_needed($fn, "in") or
317             HeaderFunctionInOut_needed($fn, "inout")) {
318                 pidl tabs()."struct {\n";
319                 $tab_depth++;
320                 HeaderFunctionInOut($fn, "in");
321                 HeaderFunctionInOut($fn, "inout");
322                 $tab_depth--;
323                 pidl tabs()."} in;\n\n";
324                 $needed++;
325         }
326
327         if (HeaderFunctionInOut_needed($fn, "out") or
328             HeaderFunctionInOut_needed($fn, "inout")) {
329                 pidl tabs()."struct {\n";
330                 $tab_depth++;
331                 HeaderFunctionInOut($fn, "out");
332                 HeaderFunctionInOut($fn, "inout");
333                 if (defined($fn->{RETURN_TYPE})) {
334                         pidl tabs().mapTypeName($fn->{RETURN_TYPE}) . " result;\n";
335                 }
336                 $tab_depth--;
337                 pidl tabs()."} out;\n\n";
338                 $needed++;
339         }
340
341         if (!$needed) {
342                 # sigh - some compilers don't like empty structures
343                 pidl tabs()."int _dummy_element;\n";
344         }
345
346         $tab_depth--;
347         pidl "};\n\n";
348 }
349
350 sub HeaderImport
351 {
352         my @imports = @_;
353         foreach my $import (@imports) {
354                 $import = unmake_str($import);
355                 $import =~ s/\.idl$//;
356                 pidl choose_header("librpc/gen_ndr/$import\.h", "gen_ndr/$import.h") . "\n";
357         }
358 }
359
360 sub HeaderInclude
361 {
362         my @includes = @_;
363         foreach (@includes) {
364                 pidl "#include $_\n";
365         }
366 }
367
368 #####################################################################
369 # parse the interface definitions
370 sub HeaderInterface($)
371 {
372         my($interface) = shift;
373
374         pidl "#ifndef _HEADER_$interface->{NAME}\n";
375         pidl "#define _HEADER_$interface->{NAME}\n\n";
376
377         foreach my $c (@{$interface->{CONSTS}}) {
378                 HeaderConst($c);
379         }
380
381         foreach my $t (@{$interface->{TYPES}}) {
382                 HeaderTypedef($t, ";\n\n") if ($t->{TYPE} eq "TYPEDEF");
383                 HeaderStruct($t, $t->{NAME}, ";\n\n") if ($t->{TYPE} eq "STRUCT");
384                 HeaderUnion($t, $t->{NAME}, ";\n\n") if ($t->{TYPE} eq "UNION");
385                 HeaderEnum($t, $t->{NAME}, ";\n\n") if ($t->{TYPE} eq "ENUM");
386                 HeaderBitmap($t, $t->{NAME}) if ($t->{TYPE} eq "BITMAP");
387         }
388
389         foreach my $fn (@{$interface->{FUNCTIONS}}) {
390                 HeaderFunction($fn);
391         }
392
393         pidl "#endif /* _HEADER_$interface->{NAME} */\n";
394 }
395
396 sub HeaderQuote($)
397 {
398         my($quote) = shift;
399
400         pidl unmake_str($quote->{DATA}) . "\n";
401 }
402
403 #####################################################################
404 # parse a parsed IDL into a C header
405 sub Parse($)
406 {
407         my($ndr) = shift;
408         $tab_depth = 0;
409
410         $res = "";
411         %headerstructs = ();
412         pidl "/* header auto-generated by pidl */\n\n";
413         if (!is_intree()) {
414                 pidl "#include <util/data_blob.h>\n";
415         }
416         pidl "#include <stdint.h>\n";
417         pidl "\n";
418         # FIXME: Include this only if NTSTATUS was actually used
419         pidl choose_header("libcli/util/ntstatus.h", "core/ntstatus.h") . "\n";
420         pidl "\n";
421
422         foreach (@{$ndr}) {
423                 ($_->{TYPE} eq "CPP_QUOTE") && HeaderQuote($_);
424                 ($_->{TYPE} eq "INTERFACE") && HeaderInterface($_);
425                 ($_->{TYPE} eq "IMPORT") && HeaderImport(@{$_->{PATHS}});
426                 ($_->{TYPE} eq "INCLUDE") && HeaderInclude(@{$_->{PATHS}});
427         }
428
429         return $res;
430 }
431
432 sub GenerateStructEnv($$)
433 {
434         my ($x, $v) = @_;
435         my %env;
436
437         foreach my $e (@{$x->{ELEMENTS}}) {
438                 $env{$e->{NAME}} = "$v->$e->{NAME}";
439         }
440
441         $env{"this"} = $v;
442
443         return \%env;
444 }
445
446 sub EnvSubstituteValue($$)
447 {
448         my ($env,$s) = @_;
449
450         # Substitute the value() values in the env
451         foreach my $e (@{$s->{ELEMENTS}}) {
452                 next unless (defined(my $v = has_property($e, "value")));
453                 
454                 $env->{$e->{NAME}} = ParseExpr($v, $env, $e);
455         }
456
457         return $env;
458 }
459
460 sub GenerateFunctionInEnv($;$)
461 {
462         my ($fn, $base) = @_;
463         my %env;
464
465         $base = "r->" unless defined($base);
466
467         foreach my $e (@{$fn->{ELEMENTS}}) {
468                 if (grep (/in/, @{$e->{DIRECTION}})) {
469                         $env{$e->{NAME}} = $base."in.$e->{NAME}";
470                 }
471         }
472
473         return \%env;
474 }
475
476 sub GenerateFunctionOutEnv($;$)
477 {
478         my ($fn, $base) = @_;
479         my %env;
480
481         $base = "r->" unless defined($base);
482
483         foreach my $e (@{$fn->{ELEMENTS}}) {
484                 if (grep (/out/, @{$e->{DIRECTION}})) {
485                         $env{$e->{NAME}} = $base."out.$e->{NAME}";
486                 } elsif (grep (/in/, @{$e->{DIRECTION}})) {
487                         $env{$e->{NAME}} = $base."in.$e->{NAME}";
488                 }
489         }
490
491         return \%env;
492 }
493
494 1;