2212044d3bc6e47724386549d2c4d426df7751f0
[nivanova/samba-autobuild/.git] / source4 / pidl / lib / Parse / Pidl / Samba4 / NDR / Parser.pm
1 ###################################################
2 # Samba4 NDR parser generator for IDL structures
3 # Copyright tridge@samba.org 2000-2003
4 # Copyright tpot@samba.org 2001
5 # Copyright jelmer@samba.org 2004-2006
6 # released under the GNU GPL
7
8 package Parse::Pidl::Samba4::NDR::Parser;
9
10 require Exporter;
11 @ISA = qw(Exporter);
12 @EXPORT = qw(is_charset_array);
13 @EXPORT_OK = qw(check_null_pointer);
14
15 use strict;
16 use Parse::Pidl::Typelist qw(hasType getType mapType);
17 use Parse::Pidl::Util qw(has_property ParseExpr ParseExprExt print_uuid);
18 use Parse::Pidl::NDR qw(GetPrevLevel GetNextLevel ContainsDeferred);
19 use Parse::Pidl::Samba4 qw(is_intree choose_header);
20 use Parse::Pidl qw(warning);
21
22 use vars qw($VERSION);
23 $VERSION = '0.01';
24
25 # list of known types
26 my %typefamily;
27
28 sub get_typefamily($)
29 {
30         my $n = shift;
31         return $typefamily{$n};
32 }
33
34 sub append_prefix($$)
35 {
36         my ($e, $var_name) = @_;
37         my $pointers = 0;
38
39         foreach my $l (@{$e->{LEVELS}}) {
40                 if ($l->{TYPE} eq "POINTER") {
41                         $pointers++;
42                 } elsif ($l->{TYPE} eq "ARRAY") {
43                         if (($pointers == 0) and 
44                             (not $l->{IS_FIXED}) and
45                             (not $l->{IS_INLINE})) {
46                                 return get_value_of($var_name); 
47                         }
48                 } elsif ($l->{TYPE} eq "DATA") {
49                         if (Parse::Pidl::Typelist::scalar_is_reference($l->{DATA_TYPE})) {
50                                 return get_value_of($var_name) unless ($pointers);
51                         }
52                 }
53         }
54         
55         return $var_name;
56 }
57
58 sub has_fast_array($$)
59 {
60         my ($e,$l) = @_;
61
62         return 0 if ($l->{TYPE} ne "ARRAY");
63
64         my $nl = GetNextLevel($e,$l);
65         return 0 unless ($nl->{TYPE} eq "DATA");
66         return 0 unless (hasType($nl->{DATA_TYPE}));
67
68         my $t = getType($nl->{DATA_TYPE});
69
70         # Only uint8 and string have fast array functions at the moment
71         return ($t->{NAME} eq "uint8") or ($t->{NAME} eq "string");
72 }
73
74 sub is_charset_array($$)
75 {
76         my ($e,$l) = @_;
77
78         return 0 if ($l->{TYPE} ne "ARRAY");
79
80         my $nl = GetNextLevel($e,$l);
81
82         return 0 unless ($nl->{TYPE} eq "DATA");
83
84         return has_property($e, "charset");
85 }
86
87 sub get_pointer_to($)
88 {
89         my $var_name = shift;
90         
91         if ($var_name =~ /^\*(.*)$/) {
92                 return $1;
93         } elsif ($var_name =~ /^\&(.*)$/) {
94                 return "&($var_name)";
95         } else {
96                 return "&$var_name";
97         }
98 }
99
100 sub get_value_of($)
101 {
102         my $var_name = shift;
103
104         if ($var_name =~ /^\&(.*)$/) {
105                 return $1;
106         } else {
107                 return "*$var_name";
108         }
109 }
110
111 my $res;
112 my $deferred = [];
113 my $tabs = "";
114
115 ####################################
116 # pidl() is our basic output routine
117 sub pidl($)
118 {
119         my $d = shift;
120         if ($d) {
121                 $res .= $tabs;
122                 $res .= $d;
123         }
124         $res .="\n";
125 }
126
127 my $res_hdr;
128
129 sub pidl_hdr ($) { my $d = shift; $res_hdr .= "$d\n"; }
130
131 ####################################
132 # defer() is like pidl(), but adds to 
133 # a deferred buffer which is then added to the 
134 # output buffer at the end of the structure/union/function
135 # This is needed to cope with code that must be pushed back
136 # to the end of a block of elements
137 my $defer_tabs = "";
138 sub defer_indent() { $defer_tabs.="\t"; }
139 sub defer_deindent() { $defer_tabs=substr($defer_tabs, 0, -1); }
140
141 sub defer($)
142 {
143         my $d = shift;
144         if ($d) {
145                 push(@$deferred, $defer_tabs.$d);
146         }
147 }
148
149 ########################################
150 # add the deferred content to the current
151 # output
152 sub add_deferred()
153 {
154         pidl $_ foreach (@$deferred);
155         $deferred = [];
156         $defer_tabs = "";
157 }
158
159 sub indent()
160 {
161         $tabs .= "\t";
162 }
163
164 sub deindent()
165 {
166         $tabs = substr($tabs, 0, -1);
167 }
168
169 #####################################################################
170 # declare a function public or static, depending on its attributes
171 sub fn_declare($$$)
172 {
173         my ($type,$fn,$decl) = @_;
174
175         if (has_property($fn, "no$type")) {
176                 pidl_hdr "$decl;";
177                 return 0;
178         }
179
180         if (has_property($fn, "public")) {
181                 pidl_hdr "$decl;";
182                 pidl "_PUBLIC_ $decl";
183         } else {
184                 pidl "static $decl";
185         }
186
187         return 1;
188 }
189
190 ###################################################################
191 # setup any special flags for an element or structure
192 sub start_flags($)
193 {
194         my $e = shift;
195         my $flags = has_property($e, "flag");
196         if (defined $flags) {
197                 pidl "{";
198                 indent;
199                 pidl "uint32_t _flags_save_$e->{TYPE} = ndr->flags;";
200                 pidl "ndr_set_flags(&ndr->flags, $flags);";
201         }
202 }
203
204 ###################################################################
205 # end any special flags for an element or structure
206 sub end_flags($)
207 {
208         my $e = shift;
209         my $flags = has_property($e, "flag");
210         if (defined $flags) {
211                 pidl "ndr->flags = _flags_save_$e->{TYPE};";
212                 deindent;
213                 pidl "}";
214         }
215 }
216
217 sub GenerateStructEnv($)
218 {
219         my $x = shift;
220         my %env;
221
222         foreach my $e (@{$x->{ELEMENTS}}) {
223                 $env{$e->{NAME}} = "r->$e->{NAME}";
224         }
225
226         $env{"this"} = "r";
227
228         return \%env;
229 }
230
231 sub EnvSubstituteValue($$)
232 {
233         my ($env,$s) = @_;
234
235         # Substitute the value() values in the env
236         foreach my $e (@{$s->{ELEMENTS}}) {
237                 next unless (my $v = has_property($e, "value"));
238                 
239                 $env->{$e->{NAME}} = ParseExpr($v, $env, $e);
240         }
241
242         return $env;
243 }
244
245 sub GenerateFunctionInEnv($)
246 {
247         my $fn = shift;
248         my %env;
249
250         foreach my $e (@{$fn->{ELEMENTS}}) {
251                 if (grep (/in/, @{$e->{DIRECTION}})) {
252                         $env{$e->{NAME}} = "r->in.$e->{NAME}";
253                 }
254         }
255
256         return \%env;
257 }
258
259 sub GenerateFunctionOutEnv($)
260 {
261         my $fn = shift;
262         my %env;
263
264         foreach my $e (@{$fn->{ELEMENTS}}) {
265                 if (grep (/out/, @{$e->{DIRECTION}})) {
266                         $env{$e->{NAME}} = "r->out.$e->{NAME}";
267                 } elsif (grep (/in/, @{$e->{DIRECTION}})) {
268                         $env{$e->{NAME}} = "r->in.$e->{NAME}";
269                 }
270         }
271
272         return \%env;
273 }
274
275 #####################################################################
276 # parse the data of an array - push side
277 sub ParseArrayPushHeader($$$$$)
278 {
279         my ($e,$l,$ndr,$var_name,$env) = @_;
280
281         my $size;
282         my $length;
283
284         if ($l->{IS_ZERO_TERMINATED}) {
285                 if (has_property($e, "charset")) {
286                         $size = $length = "ndr_charset_length($var_name, CH_$e->{PROPERTIES}->{charset})";
287                 } else {
288                         $size = $length = "ndr_string_length($var_name, sizeof(*$var_name))";
289                 }
290         } else {
291                 $size = ParseExpr($l->{SIZE_IS}, $env, $e);
292                 $length = ParseExpr($l->{LENGTH_IS}, $env, $e);
293         }
294
295         if ((!$l->{IS_SURROUNDING}) and $l->{IS_CONFORMANT}) {
296                 pidl "NDR_CHECK(ndr_push_uint32($ndr, NDR_SCALARS, $size));";
297         }
298         
299         if ($l->{IS_VARYING}) {
300                 pidl "NDR_CHECK(ndr_push_uint32($ndr, NDR_SCALARS, 0));";  # array offset
301                 pidl "NDR_CHECK(ndr_push_uint32($ndr, NDR_SCALARS, $length));";
302         } 
303
304         return $length;
305 }
306
307 sub check_null_pointer($$$$)
308 {
309         my ($element, $env, $print_fn, $return) = @_;
310
311         return sub ($) {
312                 my $expandedvar = shift;
313                 my $check = 0;
314
315                 # Figure out the number of pointers in $ptr
316                 $expandedvar =~ s/^(\**)//;
317                 my $ptr = $1;
318
319                 my $var = undef;
320                 foreach (keys %$env) {
321                         if ($env->{$_} eq $expandedvar) {
322                                 $var = $_;
323                                 last;
324                         }
325                 }
326                 
327                 if (defined($var)) {
328                         my $e;
329                         # lookup ptr in $e
330                         foreach (@{$element->{PARENT}->{ELEMENTS}}) {
331                                 if ($_->{NAME} eq $var) {
332                                         $e = $_;
333                                         last;
334                                 }
335                         }
336
337                         $e or die("Environment doesn't match siblings");
338
339                         # See if pointer at pointer level $level
340                         # needs to be checked.
341                         foreach my $l (@{$e->{LEVELS}}) {
342                                 if ($l->{TYPE} eq "POINTER" and 
343                                         $l->{POINTER_INDEX} == length($ptr)) {
344                                         # No need to check ref pointers
345                                         $check = ($l->{POINTER_TYPE} ne "ref");
346                                         last;
347                                 }
348
349                                 if ($l->{TYPE} eq "DATA") {
350                                         warning($element, "too much dereferences for `$var'");
351                                 }
352                         }
353                 } else {
354                         warning($element, "unknown dereferenced expression `$expandedvar'");
355                         $check = 1;
356                 }
357                 
358                 $print_fn->("if ($ptr$expandedvar == NULL) $return") if $check;
359         }
360 }
361
362 #####################################################################
363 # parse an array - pull side
364 sub ParseArrayPullHeader($$$$$)
365 {
366         my ($e,$l,$ndr,$var_name,$env) = @_;
367
368         my $length;
369         my $size;
370
371         if ($l->{IS_CONFORMANT}) {
372                 $length = $size = "ndr_get_array_size($ndr, " . get_pointer_to($var_name) . ")";
373         } elsif ($l->{IS_ZERO_TERMINATED}) { # Noheader arrays
374                 $length = $size = "ndr_get_string_size($ndr, sizeof(*$var_name))";
375         } else {
376                 $length = $size = ParseExprExt($l->{SIZE_IS}, $env, $e, 
377                     check_null_pointer($e, $env, \&pidl, "return NT_STATUS_INVALID_PARAMETER_MIX;"));
378         }
379
380         if ((!$l->{IS_SURROUNDING}) and $l->{IS_CONFORMANT}) {
381                 pidl "NDR_CHECK(ndr_pull_array_size(ndr, " . get_pointer_to($var_name) . "));";
382         }
383
384         if ($l->{IS_VARYING}) {
385                 pidl "NDR_CHECK(ndr_pull_array_length($ndr, " . get_pointer_to($var_name) . "));";
386                 $length = "ndr_get_array_length($ndr, " . get_pointer_to($var_name) .")";
387         }
388
389         if ($length ne $size) {
390                 pidl "if ($length > $size) {";
391                 indent;
392                 pidl "return ndr_pull_error($ndr, NDR_ERR_ARRAY_SIZE, \"Bad array size %u should exceed array length %u\", $size, $length);";
393                 deindent;
394                 pidl "}";
395         }
396
397         if ($l->{IS_CONFORMANT} and not $l->{IS_ZERO_TERMINATED}) {
398                 defer "if ($var_name) {";
399                 defer_indent;
400                 my $size = ParseExprExt($l->{SIZE_IS}, $env, $e, check_null_pointer($e, $env, \&defer, "return NT_STATUS_INVALID_PARAMETER_MIX;"));
401                 defer "NDR_CHECK(ndr_check_array_size(ndr, (void*)" . get_pointer_to($var_name) . ", $size));";
402                 defer_deindent;
403                 defer "}";
404         }
405
406         if ($l->{IS_VARYING} and not $l->{IS_ZERO_TERMINATED}) {
407                 defer "if ($var_name) {";
408                 defer_indent;
409                 my $length = ParseExprExt($l->{LENGTH_IS}, $env, $e, check_null_pointer($e, $env, \&defer, "return NT_STATUS_INVALID_PARAMETER_MIX;"));
410                 defer "NDR_CHECK(ndr_check_array_length(ndr, (void*)" . get_pointer_to($var_name) . ", $length));";
411                 defer_deindent;
412                 defer "}"
413         }
414
415         if (not $l->{IS_FIXED} and not is_charset_array($e, $l)) {
416                 AllocateArrayLevel($e,$l,$ndr,$env,$size);
417         }
418
419         return $length;
420 }
421
422 sub compression_alg($$)
423 {
424         my ($e, $l) = @_;
425         my ($alg, $clen, $dlen) = split(/ /, $l->{COMPRESSION});
426
427         return $alg;
428 }
429
430 sub compression_clen($$$)
431 {
432         my ($e, $l, $env) = @_;
433         my ($alg, $clen, $dlen) = split(/ /, $l->{COMPRESSION});
434
435         return ParseExpr($clen, $env, $e);
436 }
437
438 sub compression_dlen($$$)
439 {
440         my ($e,$l,$env) = @_;
441         my ($alg, $clen, $dlen) = split(/ /, $l->{COMPRESSION});
442
443         return ParseExpr($dlen, $env, $e);
444 }
445
446 sub ParseCompressionPushStart($$$$)
447 {
448         my ($e,$l,$ndr,$env) = @_;
449         my $comndr = "$ndr\_compressed";
450         my $alg = compression_alg($e, $l);
451         my $dlen = compression_dlen($e, $l, $env);
452
453         pidl "{";
454         indent;
455         pidl "struct ndr_push *$comndr;";
456         pidl "NDR_CHECK(ndr_push_compression_start($ndr, &$comndr, $alg, $dlen));";
457
458         return $comndr;
459 }
460
461 sub ParseCompressionPushEnd($$$$)
462 {
463         my ($e,$l,$ndr,$env) = @_;
464         my $comndr = "$ndr\_compressed";
465         my $alg = compression_alg($e, $l);
466         my $dlen = compression_dlen($e, $l, $env);
467
468         pidl "NDR_CHECK(ndr_push_compression_end($ndr, $comndr, $alg, $dlen));";
469         deindent;
470         pidl "}";
471 }
472
473 sub ParseCompressionPullStart($$$$)
474 {
475         my ($e,$l,$ndr,$env) = @_;
476         my $comndr = "$ndr\_compressed";
477         my $alg = compression_alg($e, $l);
478         my $dlen = compression_dlen($e, $l, $env);
479
480         pidl "{";
481         indent;
482         pidl "struct ndr_pull *$comndr;";
483         pidl "NDR_CHECK(ndr_pull_compression_start($ndr, &$comndr, $alg, $dlen));";
484
485         return $comndr;
486 }
487
488 sub ParseCompressionPullEnd($$$$)
489 {
490         my ($e,$l,$ndr,$env) = @_;
491         my $comndr = "$ndr\_compressed";
492         my $alg = compression_alg($e, $l);
493         my $dlen = compression_dlen($e, $l, $env);
494
495         pidl "NDR_CHECK(ndr_pull_compression_end($ndr, $comndr, $alg, $dlen));";
496         deindent;
497         pidl "}";
498 }
499
500 sub ParseSubcontextPushStart($$$$)
501 {
502         my ($e,$l,$ndr,$env) = @_;
503         my $subndr = "_ndr_$e->{NAME}";
504         my $subcontext_size = ParseExpr($l->{SUBCONTEXT_SIZE}, $env, $e);
505
506         pidl "{";
507         indent;
508         pidl "struct ndr_push *$subndr;";
509         pidl "NDR_CHECK(ndr_push_subcontext_start($ndr, &$subndr, $l->{HEADER_SIZE}, $subcontext_size));";
510
511         if (defined $l->{COMPRESSION}) {
512                 $subndr = ParseCompressionPushStart($e, $l, $subndr, $env);
513         }
514
515         return $subndr;
516 }
517
518 sub ParseSubcontextPushEnd($$$$)
519 {
520         my ($e,$l,$ndr,$env) = @_;
521         my $subndr = "_ndr_$e->{NAME}";
522         my $subcontext_size = ParseExpr($l->{SUBCONTEXT_SIZE}, $env, $e);
523
524         if (defined $l->{COMPRESSION}) {
525                 ParseCompressionPushEnd($e, $l, $subndr, $env);
526         }
527
528         pidl "NDR_CHECK(ndr_push_subcontext_end($ndr, $subndr, $l->{HEADER_SIZE}, $subcontext_size));";
529         deindent;
530         pidl "}";
531 }
532
533 sub ParseSubcontextPullStart($$$$)
534 {
535         my ($e,$l,$ndr,$env) = @_;
536         my $subndr = "_ndr_$e->{NAME}";
537         my $subcontext_size = ParseExpr($l->{SUBCONTEXT_SIZE}, $env, $e);
538
539         pidl "{";
540         indent;
541         pidl "struct ndr_pull *$subndr;";
542         pidl "NDR_CHECK(ndr_pull_subcontext_start($ndr, &$subndr, $l->{HEADER_SIZE}, $subcontext_size));";
543
544         if (defined $l->{COMPRESSION}) {
545                 $subndr = ParseCompressionPullStart($e, $l, $subndr, $env);
546         }
547
548         return $subndr;
549 }
550
551 sub ParseSubcontextPullEnd($$$$)
552 {
553         my ($e,$l,$ndr,$env) = @_;
554         my $subndr = "_ndr_$e->{NAME}";
555         my $subcontext_size = ParseExpr($l->{SUBCONTEXT_SIZE}, $env, $e);
556
557         if (defined $l->{COMPRESSION}) {
558                 ParseCompressionPullEnd($e, $l, $subndr, $env);
559         }
560
561         pidl "NDR_CHECK(ndr_pull_subcontext_end($ndr, $subndr, $l->{HEADER_SIZE}, $subcontext_size));";
562         deindent;
563         pidl "}";
564 }
565
566 sub ParseElementPushLevel
567 {
568         my ($e,$l,$ndr,$var_name,$env,$primitives,$deferred) = @_;
569
570         my $ndr_flags = CalcNdrFlags($l, $primitives, $deferred);
571
572         if ($l->{TYPE} eq "ARRAY" and ($l->{IS_CONFORMANT} or $l->{IS_VARYING})) {
573                 $var_name = get_pointer_to($var_name);
574         }
575
576         if (defined($ndr_flags)) {
577                 if ($l->{TYPE} eq "SUBCONTEXT") {
578                         my $subndr = ParseSubcontextPushStart($e, $l, $ndr, $env);
579                         ParseElementPushLevel($e, GetNextLevel($e, $l), $subndr, $var_name, $env, 1, 1);
580                         ParseSubcontextPushEnd($e, $l, $ndr, $env);
581                 } elsif ($l->{TYPE} eq "POINTER") {
582                         ParsePtrPush($e, $l, $var_name);
583                 } elsif ($l->{TYPE} eq "ARRAY") {
584                         my $length = ParseArrayPushHeader($e, $l, $ndr, $var_name, $env); 
585
586                         my $nl = GetNextLevel($e, $l);
587
588                         # Allow speedups for arrays of scalar types
589                         if (is_charset_array($e,$l)) {
590                                 pidl "NDR_CHECK(ndr_push_charset($ndr, $ndr_flags, $var_name, $length, sizeof(" . mapType($nl->{DATA_TYPE}) . "), CH_$e->{PROPERTIES}->{charset}));";
591                                 return;
592                         } elsif (has_fast_array($e,$l)) {
593                                 pidl "NDR_CHECK(ndr_push_array_$nl->{DATA_TYPE}($ndr, $ndr_flags, $var_name, $length));";
594                                 return;
595                         } 
596                 } elsif ($l->{TYPE} eq "SWITCH") {
597                         ParseSwitchPush($e, $l, $ndr, $var_name, $ndr_flags, $env);
598                 } elsif ($l->{TYPE} eq "DATA") {
599                         ParseDataPush($e, $l, $ndr, $var_name, $ndr_flags);
600                 }
601         }
602
603         if ($l->{TYPE} eq "POINTER" and $deferred) {
604                 if ($l->{POINTER_TYPE} ne "ref") {
605                         pidl "if ($var_name) {";
606                         indent;
607                         if ($l->{POINTER_TYPE} eq "relative") {
608                                 pidl "NDR_CHECK(ndr_push_relative_ptr2(ndr, $var_name));";
609                         }
610                 }
611                 $var_name = get_value_of($var_name);
612                 ParseElementPushLevel($e, GetNextLevel($e, $l), $ndr, $var_name, $env, 1, 1);
613
614                 if ($l->{POINTER_TYPE} ne "ref") {
615                         deindent;
616                         pidl "}";
617                 }
618         } elsif ($l->{TYPE} eq "ARRAY" and not has_fast_array($e,$l) and
619                 not is_charset_array($e, $l)) {
620                 my $length = ParseExpr($l->{LENGTH_IS}, $env, $e);
621                 my $counter = "cntr_$e->{NAME}_$l->{LEVEL_INDEX}";
622
623                 $var_name = $var_name . "[$counter]";
624
625                 if (($primitives and not $l->{IS_DEFERRED}) or ($deferred and $l->{IS_DEFERRED})) {
626                         pidl "for ($counter = 0; $counter < $length; $counter++) {";
627                         indent;
628                         ParseElementPushLevel($e, GetNextLevel($e, $l), $ndr, $var_name, $env, 1, 0);
629                         deindent;
630                         pidl "}";
631                 }
632
633                 if ($deferred and ContainsDeferred($e, $l)) {
634                         pidl "for ($counter = 0; $counter < $length; $counter++) {";
635                         indent;
636                         ParseElementPushLevel($e, GetNextLevel($e, $l), $ndr, $var_name, $env, 0, 1);
637                         deindent;
638                         pidl "}";
639                 }       
640         } elsif ($l->{TYPE} eq "SWITCH") {
641                 ParseElementPushLevel($e, GetNextLevel($e, $l), $ndr, $var_name, $env, $primitives, $deferred);
642         }
643 }
644
645 #####################################################################
646 # parse scalars in a structure element
647 sub ParseElementPush($$$$$$)
648 {
649         my ($e,$ndr,$var_prefix,$env,$primitives,$deferred) = @_;
650         my $subndr = undef;
651
652         my $var_name = $var_prefix.$e->{NAME};
653
654         return unless $primitives or ($deferred and ContainsDeferred($e, $e->{LEVELS}[0]));
655
656         # Representation type is different from transmit_as
657         if ($e->{REPRESENTATION_TYPE}) {
658                 pidl "{";
659                 indent;
660                 my $transmit_name = "_transmit_$e->{NAME}";
661                 pidl mapType($e->{TYPE}) ." $transmit_name;";
662                 pidl "NDR_CHECK(ndr_$e->{REPRESENTATION_TYPE}_to_$e->{TYPE}($var_name, " . get_pointer_to($transmit_name) . "));";
663                 $var_name = $transmit_name;
664         }
665
666         $var_name = append_prefix($e, $var_name);
667
668         start_flags($e);
669
670         if (my $value = has_property($e, "value")) {
671                 $var_name = ParseExpr($value, $env, $e);
672         }
673
674         ParseElementPushLevel($e, $e->{LEVELS}[0], $ndr, $var_name, $env, $primitives, $deferred);
675
676         end_flags($e);
677
678         if ($e->{REPRESENTATION_TYPE}) {
679                 deindent;
680                 pidl "}";
681         }
682 }
683
684 #####################################################################
685 # parse a pointer in a struct element or function
686 sub ParsePtrPush($$$)
687 {
688         my ($e,$l,$var_name) = @_;
689
690         if ($l->{POINTER_TYPE} eq "ref") {
691                 pidl "if ($var_name == NULL) return NT_STATUS_INVALID_PARAMETER_MIX;";
692                 if ($l->{LEVEL} eq "EMBEDDED") {
693                         pidl "NDR_CHECK(ndr_push_ref_ptr(ndr));";
694                 }
695         } elsif ($l->{POINTER_TYPE} eq "relative") {
696                 pidl "NDR_CHECK(ndr_push_relative_ptr1(ndr, $var_name));";
697         } elsif ($l->{POINTER_TYPE} eq "unique") {
698                 pidl "NDR_CHECK(ndr_push_unique_ptr(ndr, $var_name));";
699         } elsif ($l->{POINTER_TYPE} eq "full") {
700                 pidl "NDR_CHECK(ndr_push_full_ptr(ndr, $var_name));";
701         } else {
702                 die("Unhandled pointer type $l->{POINTER_TYPE}");
703         }
704 }
705
706 #####################################################################
707 # print scalars in a structure element
708 sub ParseElementPrint($$$)
709 {
710         my($e, $var_name, $env) = @_;
711
712         return if (has_property($e, "noprint"));
713
714         if ($e->{REPRESENTATION_TYPE}) {
715                 pidl "ndr_print_$e->{REPRESENTATION_TYPE}(ndr, \"$e->{NAME}\", $var_name);";
716                 return;
717         }
718
719         $var_name = append_prefix($e, $var_name);
720
721         if (my $value = has_property($e, "value")) {
722                 $var_name = "(ndr->flags & LIBNDR_PRINT_SET_VALUES)?" . ParseExpr($value,$env, $e) . ":$var_name";
723         }
724
725         foreach my $l (@{$e->{LEVELS}}) {
726                 if ($l->{TYPE} eq "POINTER") {
727                         pidl "ndr_print_ptr(ndr, \"$e->{NAME}\", $var_name);";
728                         pidl "ndr->depth++;";
729                         if ($l->{POINTER_TYPE} ne "ref") {
730                                 pidl "if ($var_name) {";
731                                 indent;
732                         }
733                         $var_name = get_value_of($var_name);
734                 } elsif ($l->{TYPE} eq "ARRAY") {
735                         my $length;
736
737                         if ($l->{IS_CONFORMANT} or $l->{IS_VARYING}) {
738                                 $var_name = get_pointer_to($var_name); 
739                         }
740                         
741                         if ($l->{IS_ZERO_TERMINATED}) {
742                                 $length = "ndr_string_length($var_name, sizeof(*$var_name))";
743                         } else {
744                                 $length = ParseExprExt($l->{LENGTH_IS}, $env, $e, 
745                                                         check_null_pointer($e, $env, \&pidl, "return;"));
746                         }
747
748                         if (is_charset_array($e,$l)) {
749                                 pidl "ndr_print_string(ndr, \"$e->{NAME}\", $var_name);";
750                                 last;
751                         } elsif (has_fast_array($e, $l)) {
752                                 my $nl = GetNextLevel($e, $l);
753                                 pidl "ndr_print_array_$nl->{DATA_TYPE}(ndr, \"$e->{NAME}\", $var_name, $length);";
754                                 last;
755                         } else {
756                                 my $counter = "cntr_$e->{NAME}_$l->{LEVEL_INDEX}";
757
758                                 pidl "ndr->print(ndr, \"\%s: ARRAY(\%d)\", \"$e->{NAME}\", $length);";
759                                 pidl 'ndr->depth++;';
760                                 pidl "for ($counter=0;$counter<$length;$counter++) {";
761                                 indent;
762                                 pidl "char *idx_$l->{LEVEL_INDEX}=NULL;";
763                                 pidl "asprintf(&idx_$l->{LEVEL_INDEX}, \"[\%d]\", $counter);";
764                                 pidl "if (idx_$l->{LEVEL_INDEX}) {";
765                                 indent;
766
767                                 $var_name = $var_name . "[$counter]";
768                         }
769                 } elsif ($l->{TYPE} eq "DATA") {
770                         if (not Parse::Pidl::Typelist::is_scalar($l->{DATA_TYPE}) or Parse::Pidl::Typelist::scalar_is_reference($l->{DATA_TYPE})) {
771                                 $var_name = get_pointer_to($var_name);
772                         }
773                         pidl "ndr_print_$l->{DATA_TYPE}(ndr, \"$e->{NAME}\", $var_name);";
774                 } elsif ($l->{TYPE} eq "SWITCH") {
775                         my $switch_var = ParseExprExt($l->{SWITCH_IS}, $env, $e, 
776                                                 check_null_pointer($e, $env, \&pidl, "return;"));
777                         pidl "ndr_print_set_switch_value(ndr, " . get_pointer_to($var_name) . ", $switch_var);";
778                 } 
779         }
780
781         foreach my $l (reverse @{$e->{LEVELS}}) {
782                 if ($l->{TYPE} eq "POINTER") {
783                         if ($l->{POINTER_TYPE} ne "ref") {
784                                 deindent;
785                                 pidl "}";
786                         }
787                         pidl "ndr->depth--;";
788                 } elsif (($l->{TYPE} eq "ARRAY")
789                         and not is_charset_array($e,$l)
790                         and not has_fast_array($e,$l)) {
791                         pidl "free(idx_$l->{LEVEL_INDEX});";
792                         deindent;
793                         pidl "}";
794                         deindent;
795                         pidl "}";
796                         pidl "ndr->depth--;";
797                 }
798         }
799 }
800
801 #####################################################################
802 # parse scalars in a structure element - pull size
803 sub ParseSwitchPull($$$$$$)
804 {
805         my($e,$l,$ndr,$var_name,$ndr_flags,$env) = @_;
806         my $switch_var = ParseExprExt($l->{SWITCH_IS}, $env, $e, 
807                 check_null_pointer($e, $env, \&pidl, "return NT_STATUS_INVALID_PARAMETER_MIX;"));
808
809         $var_name = get_pointer_to($var_name);
810         pidl "NDR_CHECK(ndr_pull_set_switch_value($ndr, $var_name, $switch_var));";
811 }
812
813 #####################################################################
814 # push switch element
815 sub ParseSwitchPush($$$$$$)
816 {
817         my($e,$l,$ndr,$var_name,$ndr_flags,$env) = @_;
818         my $switch_var = ParseExprExt($l->{SWITCH_IS}, $env, $e, 
819                 check_null_pointer($e, $env, \&pidl, "return NT_STATUS_INVALID_PARAMETER_MIX;"));
820
821         $var_name = get_pointer_to($var_name);
822         pidl "NDR_CHECK(ndr_push_set_switch_value($ndr, $var_name, $switch_var));";
823 }
824
825 sub ParseDataPull($$$$$)
826 {
827         my ($e,$l,$ndr,$var_name,$ndr_flags) = @_;
828
829         if (Parse::Pidl::Typelist::scalar_is_reference($l->{DATA_TYPE})) {
830                 $var_name = get_pointer_to($var_name);
831         }
832
833         $var_name = get_pointer_to($var_name);
834
835         pidl "NDR_CHECK(ndr_pull_$l->{DATA_TYPE}($ndr, $ndr_flags, $var_name));";
836
837         if (my $range = has_property($e, "range")) {
838                 $var_name = get_value_of($var_name);
839                 my ($low, $high) = split(/ /, $range, 2);
840                 pidl "if ($var_name < $low || $var_name > $high) {";
841                 pidl "\treturn ndr_pull_error($ndr, NDR_ERR_RANGE, \"value out of range\");";
842                 pidl "}";
843         }
844 }
845
846 sub ParseDataPush($$$$$)
847 {
848         my ($e,$l,$ndr,$var_name,$ndr_flags) = @_;
849
850         # strings are passed by value rather than reference
851         if (not Parse::Pidl::Typelist::is_scalar($l->{DATA_TYPE}) or Parse::Pidl::Typelist::scalar_is_reference($l->{DATA_TYPE})) {
852                 $var_name = get_pointer_to($var_name);
853         }
854
855         pidl "NDR_CHECK(ndr_push_$l->{DATA_TYPE}($ndr, $ndr_flags, $var_name));";
856 }
857
858 sub CalcNdrFlags($$$)
859 {
860         my ($l,$primitives,$deferred) = @_;
861
862         my $scalars = 0;
863         my $buffers = 0;
864
865         # Add NDR_SCALARS if this one is deferred 
866         # and deferreds may be pushed
867         $scalars = 1 if ($l->{IS_DEFERRED} and $deferred);
868
869         # Add NDR_SCALARS if this one is not deferred and 
870         # primitives may be pushed
871         $scalars = 1 if (!$l->{IS_DEFERRED} and $primitives);
872         
873         # Add NDR_BUFFERS if this one contains deferred stuff
874         # and deferreds may be pushed
875         $buffers = 1 if ($l->{CONTAINS_DEFERRED} and $deferred);
876
877         return "NDR_SCALARS|NDR_BUFFERS" if ($scalars and $buffers);
878         return "NDR_SCALARS" if ($scalars);
879         return "NDR_BUFFERS" if ($buffers);
880         return undef;
881 }
882
883 sub ParseMemCtxPullStart($$$)
884 {
885         my $e = shift;
886         my $l = shift;
887         my $ptr_name = shift;
888
889         my $mem_r_ctx = "_mem_save_$e->{NAME}_$l->{LEVEL_INDEX}";
890         my $mem_c_ctx = $ptr_name;
891         my $mem_c_flags = "0";
892
893         return if ($l->{TYPE} eq "ARRAY" and $l->{IS_FIXED});
894
895         if (($l->{TYPE} eq "POINTER") and ($l->{POINTER_TYPE} eq "ref")) {
896                 my $nl = GetNextLevel($e, $l);
897                 my $next_is_array = ($nl->{TYPE} eq "ARRAY");
898                 my $next_is_string = (($nl->{TYPE} eq "DATA") and 
899                                         ($nl->{DATA_TYPE} eq "string"));
900                 if ($next_is_array or $next_is_string) {
901                         return;
902                 } else {
903                         $mem_c_flags = "LIBNDR_FLAG_REF_ALLOC";
904                 }
905         }
906
907         pidl "$mem_r_ctx = NDR_PULL_GET_MEM_CTX(ndr);";
908         pidl "NDR_PULL_SET_MEM_CTX(ndr, $mem_c_ctx, $mem_c_flags);";
909 }
910
911 sub ParseMemCtxPullEnd($$)
912 {
913         my $e = shift;
914         my $l = shift;
915
916         my $mem_r_ctx = "_mem_save_$e->{NAME}_$l->{LEVEL_INDEX}";
917         my $mem_r_flags = "0";
918
919         return if ($l->{TYPE} eq "ARRAY" and $l->{IS_FIXED});
920
921         if (($l->{TYPE} eq "POINTER") and ($l->{POINTER_TYPE} eq "ref")) {
922                 my $nl = GetNextLevel($e, $l);
923                 my $next_is_array = ($nl->{TYPE} eq "ARRAY");
924                 my $next_is_string = (($nl->{TYPE} eq "DATA") and 
925                                         ($nl->{DATA_TYPE} eq "string"));
926                 if ($next_is_array or $next_is_string) {
927                         return;
928                 } else {
929                         $mem_r_flags = "LIBNDR_FLAG_REF_ALLOC";
930                 }
931         }
932
933         pidl "NDR_PULL_SET_MEM_CTX(ndr, $mem_r_ctx, $mem_r_flags);";
934 }
935
936 sub CheckStringTerminator($$$$)
937 {
938         my ($ndr,$e,$l,$length) = @_;
939         my $nl = GetNextLevel($e, $l);
940
941         # Make sure last element is zero!
942         pidl "NDR_CHECK(ndr_check_string_terminator($ndr, $length, sizeof($nl->{DATA_TYPE}_t)));";
943 }
944
945 sub ParseElementPullLevel
946 {
947         my($e,$l,$ndr,$var_name,$env,$primitives,$deferred) = @_;
948
949         my $ndr_flags = CalcNdrFlags($l, $primitives, $deferred);
950
951         if ($l->{TYPE} eq "ARRAY" and ($l->{IS_VARYING} or $l->{IS_CONFORMANT})) {
952                 $var_name = get_pointer_to($var_name);
953         }
954
955         # Only pull something if there's actually something to be pulled
956         if (defined($ndr_flags)) {
957                 if ($l->{TYPE} eq "SUBCONTEXT") {
958                         my $subndr = ParseSubcontextPullStart($e, $l, $ndr, $env);
959                         ParseElementPullLevel($e, GetNextLevel($e,$l), $subndr, $var_name, $env, 1, 1);
960                         ParseSubcontextPullEnd($e, $l, $ndr, $env);
961                 } elsif ($l->{TYPE} eq "ARRAY") {
962                         my $length = ParseArrayPullHeader($e, $l, $ndr, $var_name, $env);
963
964                         my $nl = GetNextLevel($e, $l);
965
966                         if (is_charset_array($e,$l)) {
967                                 if ($l->{IS_ZERO_TERMINATED}) {
968                                         CheckStringTerminator($ndr, $e, $l, $length);
969                                 }
970                                 pidl "NDR_CHECK(ndr_pull_charset($ndr, $ndr_flags, ".get_pointer_to($var_name).", $length, sizeof(" . mapType($nl->{DATA_TYPE}) . "), CH_$e->{PROPERTIES}->{charset}));";
971                                 return;
972                         } elsif (has_fast_array($e, $l)) {
973                                 if ($l->{IS_ZERO_TERMINATED}) {
974                                         CheckStringTerminator($ndr,$e,$l,$length);
975                                 }
976                                 pidl "NDR_CHECK(ndr_pull_array_$nl->{DATA_TYPE}($ndr, $ndr_flags, $var_name, $length));";
977                                 return;
978                         }
979                 } elsif ($l->{TYPE} eq "POINTER") {
980                         ParsePtrPull($e, $l, $ndr, $var_name);
981                 } elsif ($l->{TYPE} eq "SWITCH") {
982                         ParseSwitchPull($e, $l, $ndr, $var_name, $ndr_flags, $env);
983                 } elsif ($l->{TYPE} eq "DATA") {
984                         ParseDataPull($e, $l, $ndr, $var_name, $ndr_flags);
985                 }
986         }
987
988         # add additional constructions
989         if ($l->{TYPE} eq "POINTER" and $deferred) {
990                 if ($l->{POINTER_TYPE} ne "ref") {
991                         pidl "if ($var_name) {";
992                         indent;
993
994                         if ($l->{POINTER_TYPE} eq "relative") {
995                                 pidl "struct ndr_pull_save _relative_save;";
996                                 pidl "ndr_pull_save(ndr, &_relative_save);";
997                                 pidl "NDR_CHECK(ndr_pull_relative_ptr2(ndr, $var_name));";
998                         }
999                 }
1000
1001                 ParseMemCtxPullStart($e,$l, $var_name);
1002
1003                 $var_name = get_value_of($var_name);
1004                 ParseElementPullLevel($e, GetNextLevel($e,$l), $ndr, $var_name, $env, 1, 1);
1005
1006                 ParseMemCtxPullEnd($e,$l);
1007
1008                 if ($l->{POINTER_TYPE} ne "ref") {
1009                         if ($l->{POINTER_TYPE} eq "relative") {
1010                                 pidl "ndr_pull_restore(ndr, &_relative_save);";
1011                         }
1012                         deindent;
1013                         pidl "}";
1014                 }
1015         } elsif ($l->{TYPE} eq "ARRAY" and 
1016                         not has_fast_array($e,$l) and not is_charset_array($e, $l)) {
1017                 my $length = ParseExpr($l->{LENGTH_IS}, $env, $e);
1018                 my $counter = "cntr_$e->{NAME}_$l->{LEVEL_INDEX}";
1019                 my $array_name = $var_name;
1020
1021                 $var_name = $var_name . "[$counter]";
1022
1023                 ParseMemCtxPullStart($e,$l, $array_name);
1024
1025                 if (($primitives and not $l->{IS_DEFERRED}) or ($deferred and $l->{IS_DEFERRED})) {
1026                         my $nl = GetNextLevel($e,$l);
1027
1028                         if ($l->{IS_ZERO_TERMINATED}) {
1029                                 CheckStringTerminator($ndr,$e,$l,$length);
1030                         }
1031
1032                         pidl "for ($counter = 0; $counter < $length; $counter++) {";
1033                         indent;
1034                         ParseElementPullLevel($e, $nl, $ndr, $var_name, $env, 1, 0);
1035                         deindent;
1036                         pidl "}";
1037                 }
1038
1039                 if ($deferred and ContainsDeferred($e, $l)) {
1040                         pidl "for ($counter = 0; $counter < $length; $counter++) {";
1041                         indent;
1042                         ParseElementPullLevel($e,GetNextLevel($e,$l), $ndr, $var_name, $env, 0, 1);
1043                         deindent;
1044                         pidl "}";
1045                 }
1046
1047                 ParseMemCtxPullEnd($e,$l);
1048
1049         } elsif ($l->{TYPE} eq "SWITCH") {
1050                 ParseElementPullLevel($e,GetNextLevel($e,$l), $ndr, $var_name, $env, $primitives, $deferred);
1051         }
1052 }
1053
1054 #####################################################################
1055 # parse scalars in a structure element - pull size
1056 sub ParseElementPull($$$$$$)
1057 {
1058         my($e,$ndr,$var_prefix,$env,$primitives,$deferred) = @_;
1059
1060         my $var_name = $var_prefix.$e->{NAME};
1061         my $represent_name;
1062         my $transmit_name;
1063
1064         return unless $primitives or ($deferred and ContainsDeferred($e, $e->{LEVELS}[0]));
1065
1066         if ($e->{REPRESENTATION_TYPE}) {
1067                 pidl "{";
1068                 indent;
1069                 $represent_name = $var_name;
1070                 $transmit_name = "_transmit_$e->{NAME}";
1071                 $var_name = $transmit_name;
1072                 pidl mapType($e->{TYPE})." $var_name;";
1073         }
1074
1075         $var_name = append_prefix($e, $var_name);
1076
1077         start_flags($e);
1078
1079         ParseElementPullLevel($e,$e->{LEVELS}[0],$ndr,$var_name,$env,$primitives,$deferred);
1080
1081         end_flags($e);
1082
1083         # Representation type is different from transmit_as
1084         if ($e->{REPRESENTATION_TYPE}) {
1085                 pidl "NDR_CHECK(ndr_$e->{TYPE}_to_$e->{REPRESENTATION_TYPE}($transmit_name, ".get_pointer_to($represent_name)."));";
1086                 deindent;
1087                 pidl "}";
1088         }
1089 }
1090
1091 #####################################################################
1092 # parse a pointer in a struct element or function
1093 sub ParsePtrPull($$$$)
1094 {
1095         my($e,$l,$ndr,$var_name) = @_;
1096
1097         my $nl = GetNextLevel($e, $l);
1098         my $next_is_array = ($nl->{TYPE} eq "ARRAY");
1099         my $next_is_string = (($nl->{TYPE} eq "DATA") and 
1100                                                  ($nl->{DATA_TYPE} eq "string"));
1101
1102         if ($l->{POINTER_TYPE} eq "ref") {
1103                 if ($l->{LEVEL} eq "EMBEDDED") {
1104                         pidl "NDR_CHECK(ndr_pull_ref_ptr($ndr, &_ptr_$e->{NAME}));";
1105                 }
1106
1107                 if (!$next_is_array and !$next_is_string) {
1108                         pidl "if (ndr->flags & LIBNDR_FLAG_REF_ALLOC) {";
1109                         pidl "\tNDR_PULL_ALLOC($ndr, $var_name);"; 
1110                         pidl "}";
1111                 }
1112                 
1113                 return;
1114         } elsif (($l->{POINTER_TYPE} eq "unique") or 
1115                  ($l->{POINTER_TYPE} eq "relative") or
1116                  ($l->{POINTER_TYPE} eq "full")) {
1117                 pidl "NDR_CHECK(ndr_pull_generic_ptr($ndr, &_ptr_$e->{NAME}));";
1118                 pidl "if (_ptr_$e->{NAME}) {";
1119                 indent;
1120         } else {
1121                 die("Unhandled pointer type $l->{POINTER_TYPE}");
1122         }
1123
1124         # Don't do this for arrays, they're allocated at the actual level 
1125         # of the array
1126         unless ($next_is_array or $next_is_string) { 
1127                 pidl "NDR_PULL_ALLOC($ndr, $var_name);"; 
1128         } else {
1129                 # FIXME: Yes, this is nasty.
1130                 # We allocate an array twice
1131                 # - once just to indicate that it's there,
1132                 # - then the real allocation...
1133                 pidl "NDR_PULL_ALLOC($ndr, $var_name);";
1134         }
1135
1136         #pidl "memset($var_name, 0, sizeof($var_name));";
1137         if ($l->{POINTER_TYPE} eq "relative") {
1138                 pidl "NDR_CHECK(ndr_pull_relative_ptr1($ndr, $var_name, _ptr_$e->{NAME}));";
1139         }
1140         deindent;
1141         pidl "} else {";
1142         pidl "\t$var_name = NULL;";
1143         pidl "}";
1144 }
1145
1146 #####################################################################
1147 # parse a struct
1148 sub ParseStructPush($$)
1149 {
1150         my($struct,$name) = @_;
1151         
1152         return unless defined($struct->{ELEMENTS});
1153
1154         my $env = GenerateStructEnv($struct);
1155
1156         EnvSubstituteValue($env, $struct);
1157
1158         # save the old relative_base_offset
1159         pidl "uint32_t _save_relative_base_offset = ndr_push_get_relative_base_offset(ndr);" if defined($struct->{PROPERTIES}{relative_base});
1160
1161         foreach my $e (@{$struct->{ELEMENTS}}) { 
1162                 DeclareArrayVariables($e);
1163         }
1164
1165         start_flags($struct);
1166
1167         # see if the structure contains a conformant array. If it
1168         # does, then it must be the last element of the structure, and
1169         # we need to push the conformant length early, as it fits on
1170         # the wire before the structure (and even before the structure
1171         # alignment)
1172         if (defined($struct->{SURROUNDING_ELEMENT})) {
1173                 my $e = $struct->{SURROUNDING_ELEMENT};
1174
1175                 if (defined($e->{LEVELS}[0]) and 
1176                         $e->{LEVELS}[0]->{TYPE} eq "ARRAY") {
1177                         my $size;
1178                         
1179                         if ($e->{LEVELS}[0]->{IS_ZERO_TERMINATED}) {
1180                                 if (has_property($e, "charset")) {
1181                                         $size = "ndr_charset_length(r->$e->{NAME}, CH_$e->{PROPERTIES}->{charset})";
1182                                 } else {
1183                                         $size = "ndr_string_length(r->$e->{NAME}, sizeof(*r->$e->{NAME}))";
1184                                 }
1185                         } else {
1186                                 $size = ParseExpr($e->{LEVELS}[0]->{SIZE_IS}, $env, $e);
1187                         }
1188
1189                         pidl "NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, $size));";
1190                 } else {
1191                         pidl "NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, ndr_string_array_size(ndr, r->$e->{NAME})));";
1192                 }
1193         }
1194
1195         pidl "if (ndr_flags & NDR_SCALARS) {";
1196         indent;
1197
1198         pidl "NDR_CHECK(ndr_push_align(ndr, $struct->{ALIGN}));";
1199
1200         if (defined($struct->{PROPERTIES}{relative_base})) {
1201                 # set the current offset as base for relative pointers
1202                 # and store it based on the toplevel struct/union
1203                 pidl "NDR_CHECK(ndr_push_setup_relative_base_offset1(ndr, r, ndr->offset));";
1204         }
1205
1206         foreach my $e (@{$struct->{ELEMENTS}}) {
1207                 ParseElementPush($e, "ndr", "r->", $env, 1, 0);
1208         }       
1209
1210         deindent;
1211         pidl "}";
1212
1213         pidl "if (ndr_flags & NDR_BUFFERS) {";
1214         indent;
1215         if (defined($struct->{PROPERTIES}{relative_base})) {
1216                 # retrieve the current offset as base for relative pointers
1217                 # based on the toplevel struct/union
1218                 pidl "NDR_CHECK(ndr_push_setup_relative_base_offset2(ndr, r));";
1219         }
1220         foreach my $e (@{$struct->{ELEMENTS}}) {
1221                 ParseElementPush($e, "ndr", "r->", $env, 0, 1);
1222         }
1223
1224         deindent;
1225         pidl "}";
1226
1227         end_flags($struct);
1228         # restore the old relative_base_offset
1229         pidl "ndr_push_restore_relative_base_offset(ndr, _save_relative_base_offset);" if defined($struct->{PROPERTIES}{relative_base});
1230 }
1231
1232 #####################################################################
1233 # generate a push function for an enum
1234 sub ParseEnumPush($$)
1235 {
1236         my($enum,$name) = @_;
1237         my($type_fn) = $enum->{BASE_TYPE};
1238
1239         start_flags($enum);
1240         pidl "NDR_CHECK(ndr_push_$type_fn(ndr, NDR_SCALARS, r));";
1241         end_flags($enum);
1242 }
1243
1244 #####################################################################
1245 # generate a pull function for an enum
1246 sub ParseEnumPull($$)
1247 {
1248         my($enum,$name) = @_;
1249         my($type_fn) = $enum->{BASE_TYPE};
1250         my($type_v_decl) = mapType($type_fn);
1251
1252         pidl "$type_v_decl v;";
1253         start_flags($enum);
1254         pidl "NDR_CHECK(ndr_pull_$type_fn(ndr, NDR_SCALARS, &v));";
1255         pidl "*r = v;";
1256
1257         end_flags($enum);
1258 }
1259
1260 #####################################################################
1261 # generate a print function for an enum
1262 sub ParseEnumPrint($$)
1263 {
1264         my($enum,$name) = @_;
1265
1266         pidl "const char *val = NULL;";
1267         pidl "";
1268
1269         start_flags($enum);
1270
1271         pidl "switch (r) {";
1272         indent;
1273         my $els = \@{$enum->{ELEMENTS}};
1274         foreach my $i (0 .. $#{$els}) {
1275                 my $e = ${$els}[$i];
1276                 chomp $e;
1277                 if ($e =~ /^(.*)=/) {
1278                         $e = $1;
1279                 }
1280                 pidl "case $e: val = \"$e\"; break;";
1281         }
1282
1283         deindent;
1284         pidl "}";
1285         
1286         pidl "ndr_print_enum(ndr, name, \"$enum->{TYPE}\", val, r);";
1287
1288         end_flags($enum);
1289 }
1290
1291 sub DeclEnum($)
1292 {
1293         my ($e,$t) = @_;
1294         return "enum $e->{NAME} " . 
1295                 ($t eq "pull"?"*":"") . "r";
1296 }
1297
1298 $typefamily{ENUM} = {
1299         DECL => \&DeclEnum,
1300         PUSH_FN_BODY => \&ParseEnumPush,
1301         PULL_FN_BODY => \&ParseEnumPull,
1302         PRINT_FN_BODY => \&ParseEnumPrint,
1303 };
1304
1305 #####################################################################
1306 # generate a push function for a bitmap
1307 sub ParseBitmapPush($$)
1308 {
1309         my($bitmap,$name) = @_;
1310         my($type_fn) = $bitmap->{BASE_TYPE};
1311
1312         start_flags($bitmap);
1313
1314         pidl "NDR_CHECK(ndr_push_$type_fn(ndr, NDR_SCALARS, r));";
1315
1316         end_flags($bitmap);
1317 }
1318
1319 #####################################################################
1320 # generate a pull function for an bitmap
1321 sub ParseBitmapPull($$)
1322 {
1323         my($bitmap,$name) = @_;
1324         my $type_fn = $bitmap->{BASE_TYPE};
1325         my($type_decl) = mapType($bitmap->{BASE_TYPE});
1326
1327         pidl "$type_decl v;";
1328         start_flags($bitmap);
1329         pidl "NDR_CHECK(ndr_pull_$type_fn(ndr, NDR_SCALARS, &v));";
1330         pidl "*r = v;";
1331
1332         end_flags($bitmap);
1333 }
1334
1335 #####################################################################
1336 # generate a print function for an bitmap
1337 sub ParseBitmapPrintElement($$$)
1338 {
1339         my($e,$bitmap,$name) = @_;
1340         my($type_decl) = mapType($bitmap->{BASE_TYPE});
1341         my($type_fn) = $bitmap->{BASE_TYPE};
1342         my($flag);
1343
1344         if ($e =~ /^(\w+) .*$/) {
1345                 $flag = "$1";
1346         } else {
1347                 die "Bitmap: \"$name\" invalid Flag: \"$e\"";
1348         }
1349
1350         pidl "ndr_print_bitmap_flag(ndr, sizeof($type_decl), \"$flag\", $flag, r);";
1351 }
1352
1353 #####################################################################
1354 # generate a print function for an bitmap
1355 sub ParseBitmapPrint($$)
1356 {
1357         my($bitmap,$name) = @_;
1358         my($type_decl) = mapType($bitmap->{TYPE});
1359         my($type_fn) = $bitmap->{BASE_TYPE};
1360
1361         start_flags($bitmap);
1362
1363         pidl "ndr_print_$type_fn(ndr, name, r);";
1364
1365         pidl "ndr->depth++;";
1366         foreach my $e (@{$bitmap->{ELEMENTS}}) {
1367                 ParseBitmapPrintElement($e, $bitmap, $name);
1368         }
1369         pidl "ndr->depth--;";
1370
1371         end_flags($bitmap);
1372 }
1373
1374 sub DeclBitmap($$)
1375 {
1376         my ($e,$t) = @_;
1377         return mapType(Parse::Pidl::Typelist::bitmap_type_fn($e->{DATA})) . 
1378                 ($t eq "pull"?" *":" ") . "r";
1379 }
1380
1381 $typefamily{BITMAP} = {
1382         DECL => \&DeclBitmap,
1383         PUSH_FN_BODY => \&ParseBitmapPush,
1384         PULL_FN_BODY => \&ParseBitmapPull,
1385         PRINT_FN_BODY => \&ParseBitmapPrint,
1386 };
1387
1388 #####################################################################
1389 # generate a struct print function
1390 sub ParseStructPrint($$)
1391 {
1392         my($struct,$name) = @_;
1393
1394         return unless defined $struct->{ELEMENTS};
1395
1396         my $env = GenerateStructEnv($struct);
1397
1398         EnvSubstituteValue($env, $struct);
1399
1400         DeclareArrayVariables($_) foreach (@{$struct->{ELEMENTS}});
1401
1402         pidl "ndr_print_struct(ndr, name, \"$name\");";
1403
1404         start_flags($struct);
1405
1406         pidl "ndr->depth++;";
1407         
1408         ParseElementPrint($_, "r->$_->{NAME}", $env) foreach (@{$struct->{ELEMENTS}});
1409         pidl "ndr->depth--;";
1410
1411         end_flags($struct);
1412 }
1413
1414 sub DeclarePtrVariables($)
1415 {
1416         my $e = shift;
1417         foreach my $l (@{$e->{LEVELS}}) {
1418                 if ($l->{TYPE} eq "POINTER" and 
1419                         not ($l->{POINTER_TYPE} eq "ref" and $l->{LEVEL} eq "TOP")) {
1420                         pidl "uint32_t _ptr_$e->{NAME};";
1421                         last;
1422                 }
1423         }
1424 }
1425
1426 sub DeclareArrayVariables($)
1427 {
1428         my $e = shift;
1429
1430         foreach my $l (@{$e->{LEVELS}}) {
1431                 next if has_fast_array($e,$l);
1432                 next if is_charset_array($e,$l);
1433                 if ($l->{TYPE} eq "ARRAY") {
1434                         pidl "uint32_t cntr_$e->{NAME}_$l->{LEVEL_INDEX};";
1435                 }
1436         }
1437 }
1438
1439 sub need_decl_mem_ctx($$)
1440 {
1441         my ($e,$l) = @_;
1442
1443         return 0 if has_fast_array($e,$l);
1444         return 0 if is_charset_array($e,$l);
1445         return 1 if (($l->{TYPE} eq "ARRAY") and not $l->{IS_FIXED});
1446
1447         if (($l->{TYPE} eq "POINTER") and ($l->{POINTER_TYPE} eq "ref")) {
1448                 my $nl = GetNextLevel($e, $l);
1449                 my $next_is_array = ($nl->{TYPE} eq "ARRAY");
1450                 my $next_is_string = (($nl->{TYPE} eq "DATA") and 
1451                                         ($nl->{DATA_TYPE} eq "string"));
1452                 return 0 if ($next_is_array or $next_is_string);
1453         }
1454         return 1 if ($l->{TYPE} eq "POINTER");
1455
1456         return 0;
1457 }
1458
1459 sub DeclareMemCtxVariables($)
1460 {
1461         my $e = shift;
1462         foreach my $l (@{$e->{LEVELS}}) {
1463                 if (need_decl_mem_ctx($e, $l)) {
1464                         pidl "TALLOC_CTX *_mem_save_$e->{NAME}_$l->{LEVEL_INDEX};";
1465                 }
1466         }
1467 }
1468
1469 #####################################################################
1470 # parse a struct - pull side
1471 sub ParseStructPull($$)
1472 {
1473         my($struct,$name) = @_;
1474
1475         return unless defined $struct->{ELEMENTS};
1476
1477         my $env = GenerateStructEnv($struct);
1478
1479         # declare any internal pointers we need
1480         foreach my $e (@{$struct->{ELEMENTS}}) {
1481                 DeclarePtrVariables($e);
1482                 DeclareArrayVariables($e);
1483                 DeclareMemCtxVariables($e);
1484         }
1485
1486         # save the old relative_base_offset
1487         pidl "uint32_t _save_relative_base_offset = ndr_pull_get_relative_base_offset(ndr);" if defined($struct->{PROPERTIES}{relative_base});
1488
1489         start_flags($struct);
1490
1491         pidl "if (ndr_flags & NDR_SCALARS) {";
1492         indent;
1493
1494         if (defined $struct->{SURROUNDING_ELEMENT}) {
1495                 pidl "NDR_CHECK(ndr_pull_array_size(ndr, &r->$struct->{SURROUNDING_ELEMENT}->{NAME}));";
1496         }
1497
1498         pidl "NDR_CHECK(ndr_pull_align(ndr, $struct->{ALIGN}));";
1499
1500         if (defined($struct->{PROPERTIES}{relative_base})) {
1501                 # set the current offset as base for relative pointers
1502                 # and store it based on the toplevel struct/union
1503                 pidl "NDR_CHECK(ndr_pull_setup_relative_base_offset1(ndr, r, ndr->offset));";
1504         }
1505
1506         foreach my $e (@{$struct->{ELEMENTS}}) {
1507                 ParseElementPull($e, "ndr", "r->", $env, 1, 0);
1508         }       
1509
1510         add_deferred();
1511
1512         deindent;
1513         pidl "}";
1514         pidl "if (ndr_flags & NDR_BUFFERS) {";
1515         indent;
1516         if (defined($struct->{PROPERTIES}{relative_base})) {
1517                 # retrieve the current offset as base for relative pointers
1518                 # based on the toplevel struct/union
1519                 pidl "NDR_CHECK(ndr_pull_setup_relative_base_offset2(ndr, r));";
1520         }
1521         foreach my $e (@{$struct->{ELEMENTS}}) {
1522                 ParseElementPull($e, "ndr", "r->", $env, 0, 1);
1523         }
1524
1525         add_deferred();
1526
1527         deindent;
1528         pidl "}";
1529
1530         end_flags($struct);
1531         # restore the old relative_base_offset
1532         pidl "ndr_pull_restore_relative_base_offset(ndr, _save_relative_base_offset);" if defined($struct->{PROPERTIES}{relative_base});
1533 }
1534
1535 #####################################################################
1536 # calculate size of ndr struct
1537 sub ParseStructNdrSize($)
1538 {
1539         my $t = shift;
1540         my $sizevar;
1541
1542         if (my $flags = has_property($t, "flag")) {
1543                 pidl "flags |= $flags;";
1544         }
1545         pidl "return ndr_size_struct(r, flags, (ndr_push_flags_fn_t)ndr_push_$t->{NAME});";
1546 }
1547
1548 sub DeclStruct($)
1549 {
1550         my ($e,$t) = @_;
1551         return ($t ne "pull"?"const ":"") . "struct $e->{NAME} *r";
1552 }
1553
1554 sub ArgsStructNdrSize($)
1555 {
1556         my $d = shift;
1557         return "const struct $d->{NAME} *r, int flags";
1558 }
1559
1560 $typefamily{STRUCT} = {
1561         PUSH_FN_BODY => \&ParseStructPush,
1562         DECL => \&DeclStruct,
1563         PULL_FN_BODY => \&ParseStructPull,
1564         PRINT_FN_BODY => \&ParseStructPrint,
1565         SIZE_FN_BODY => \&ParseStructNdrSize,
1566         SIZE_FN_ARGS => \&ArgsStructNdrSize,
1567 };
1568
1569 #####################################################################
1570 # calculate size of ndr struct
1571 sub ParseUnionNdrSize($)
1572 {
1573         my $t = shift;
1574         my $sizevar;
1575
1576         if (my $flags = has_property($t, "flag")) {
1577                 pidl "flags |= $flags;";
1578         }
1579
1580         pidl "return ndr_size_union(r, flags, level, (ndr_push_flags_fn_t)ndr_push_$t->{NAME});";
1581 }
1582
1583 #####################################################################
1584 # parse a union - push side
1585 sub ParseUnionPush($$)
1586 {
1587         my ($e,$name) = @_;
1588         my $have_default = 0;
1589
1590         # save the old relative_base_offset
1591         pidl "uint32_t _save_relative_base_offset = ndr_push_get_relative_base_offset(ndr);" if defined($e->{PROPERTIES}{relative_base});
1592         pidl "int level;";
1593
1594         start_flags($e);
1595
1596         pidl "level = ndr_push_get_switch_value(ndr, r);";
1597
1598         pidl "if (ndr_flags & NDR_SCALARS) {";
1599         indent;
1600
1601         if (defined($e->{SWITCH_TYPE})) {
1602                 pidl "NDR_CHECK(ndr_push_$e->{SWITCH_TYPE}(ndr, NDR_SCALARS, level));";
1603         }
1604
1605         pidl "switch (level) {";
1606         indent;
1607         foreach my $el (@{$e->{ELEMENTS}}) {
1608                 if ($el->{CASE} eq "default") {
1609                         $have_default = 1;
1610                 }
1611                 pidl "$el->{CASE}:";
1612
1613                 if ($el->{TYPE} ne "EMPTY") {
1614                         indent;
1615                         if (defined($e->{PROPERTIES}{relative_base})) {
1616                                 pidl "NDR_CHECK(ndr_push_align(ndr, $el->{ALIGN}));";
1617                                 # set the current offset as base for relative pointers
1618                                 # and store it based on the toplevel struct/union
1619                                 pidl "NDR_CHECK(ndr_push_setup_relative_base_offset1(ndr, r, ndr->offset));";
1620                         }
1621                         DeclareArrayVariables($el);
1622                         ParseElementPush($el, "ndr", "r->", {}, 1, 0);
1623                         deindent;
1624                 }
1625                 pidl "break;";
1626                 pidl "";
1627         }
1628         if (! $have_default) {
1629                 pidl "default:";
1630                 pidl "\treturn ndr_push_error(ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value \%u\", level);";
1631         }
1632         deindent;
1633         pidl "}";
1634         deindent;
1635         pidl "}";
1636         pidl "if (ndr_flags & NDR_BUFFERS) {";
1637         indent;
1638         if (defined($e->{PROPERTIES}{relative_base})) {
1639                 # retrieve the current offset as base for relative pointers
1640                 # based on the toplevel struct/union
1641                 pidl "NDR_CHECK(ndr_push_setup_relative_base_offset2(ndr, r));";
1642         }
1643         pidl "switch (level) {";
1644         indent;
1645         foreach my $el (@{$e->{ELEMENTS}}) {
1646                 pidl "$el->{CASE}:";
1647                 if ($el->{TYPE} ne "EMPTY") {
1648                         indent;
1649                         ParseElementPush($el, "ndr", "r->", {}, 0, 1);
1650                         deindent;
1651                 }
1652                 pidl "break;";
1653                 pidl "";
1654         }
1655         if (! $have_default) {
1656                 pidl "default:";
1657                 pidl "\treturn ndr_push_error(ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value \%u\", level);";
1658         }
1659         deindent;
1660         pidl "}";
1661
1662         deindent;
1663         pidl "}";
1664         end_flags($e);
1665         # restore the old relative_base_offset
1666         pidl "ndr_push_restore_relative_base_offset(ndr, _save_relative_base_offset);" if defined($e->{PROPERTIES}{relative_base});
1667 }
1668
1669 #####################################################################
1670 # print a union
1671 sub ParseUnionPrint($$)
1672 {
1673         my ($e,$name) = @_;
1674         my $have_default = 0;
1675
1676         pidl "int level;";
1677         foreach my $el (@{$e->{ELEMENTS}}) {
1678                 DeclareArrayVariables($el);
1679         }
1680
1681         start_flags($e);
1682
1683         pidl "level = ndr_print_get_switch_value(ndr, r);";
1684
1685         pidl "ndr_print_union(ndr, name, level, \"$name\");";
1686
1687         pidl "switch (level) {";
1688         indent;
1689         foreach my $el (@{$e->{ELEMENTS}}) {
1690                 if ($el->{CASE} eq "default") {
1691                         $have_default = 1;
1692                 }
1693                 pidl "$el->{CASE}:";
1694                 if ($el->{TYPE} ne "EMPTY") {
1695                         indent;
1696                         ParseElementPrint($el, "r->$el->{NAME}", {});
1697                         deindent;
1698                 }
1699                 pidl "break;";
1700                 pidl "";
1701         }
1702         if (! $have_default) {
1703                 pidl "default:";
1704                 pidl "\tndr_print_bad_level(ndr, name, level);";
1705         }
1706         deindent;
1707         pidl "}";
1708
1709         end_flags($e);
1710 }
1711
1712 #####################################################################
1713 # parse a union - pull side
1714 sub ParseUnionPull($$)
1715 {
1716         my ($e,$name) = @_;
1717         my $have_default = 0;
1718         my $switch_type = $e->{SWITCH_TYPE};
1719
1720         # save the old relative_base_offset
1721         pidl "uint32_t _save_relative_base_offset = ndr_pull_get_relative_base_offset(ndr);" if defined($e->{PROPERTIES}{relative_base});
1722         pidl "int level;";
1723         if (defined($switch_type)) {
1724                 if (Parse::Pidl::Typelist::typeIs($switch_type, "ENUM")) {
1725                         $switch_type = Parse::Pidl::Typelist::enum_type_fn(getType($switch_type)->{DATA});
1726                 }
1727                 pidl mapType($switch_type) . " _level;";
1728         }
1729
1730         my %double_cases = ();
1731         foreach my $el (@{$e->{ELEMENTS}}) {
1732                 next if ($el->{TYPE} eq "EMPTY");
1733                 next if ($double_cases{"$el->{NAME}"});
1734                 DeclareMemCtxVariables($el);
1735                 $double_cases{"$el->{NAME}"} = 1;
1736         }
1737
1738         start_flags($e);
1739
1740         pidl "level = ndr_pull_get_switch_value(ndr, r);";
1741
1742         pidl "if (ndr_flags & NDR_SCALARS) {";
1743         indent;
1744
1745         if (defined($switch_type)) {
1746                 pidl "NDR_CHECK(ndr_pull_$switch_type(ndr, NDR_SCALARS, &_level));";
1747                 pidl "if (_level != level) {"; 
1748                 pidl "\treturn ndr_pull_error(ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value %u for $name\", _level);";
1749                 pidl "}";
1750         }
1751
1752         pidl "switch (level) {";
1753         indent;
1754         foreach my $el (@{$e->{ELEMENTS}}) {
1755                 if ($el->{CASE} eq "default") {
1756                         $have_default = 1;
1757                 } 
1758                 pidl "$el->{CASE}: {";
1759
1760                 if ($el->{TYPE} ne "EMPTY") {
1761                         indent;
1762                         DeclarePtrVariables($el);
1763                         DeclareArrayVariables($el);
1764                         if (defined($e->{PROPERTIES}{relative_base})) {
1765                                 pidl "NDR_CHECK(ndr_pull_align(ndr, $el->{ALIGN}));";
1766                                 # set the current offset as base for relative pointers
1767                                 # and store it based on the toplevel struct/union
1768                                 pidl "NDR_CHECK(ndr_pull_setup_relative_base_offset1(ndr, r, ndr->offset));";
1769                         }
1770                         ParseElementPull($el, "ndr", "r->", {}, 1, 0);
1771                         deindent;
1772                 }
1773                 pidl "break; }";
1774                 pidl "";
1775         }
1776         if (! $have_default) {
1777                 pidl "default:";
1778                 pidl "\treturn ndr_pull_error(ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value \%u\", level);";
1779         }
1780         deindent;
1781         pidl "}";
1782         deindent;
1783         pidl "}";
1784         pidl "if (ndr_flags & NDR_BUFFERS) {";
1785         indent;
1786         if (defined($e->{PROPERTIES}{relative_base})) {
1787                 # retrieve the current offset as base for relative pointers
1788                 # based on the toplevel struct/union
1789                 pidl "NDR_CHECK(ndr_pull_setup_relative_base_offset2(ndr, r));";
1790         }
1791         pidl "switch (level) {";
1792         indent;
1793         foreach my $el (@{$e->{ELEMENTS}}) {
1794                 pidl "$el->{CASE}:";
1795                 if ($el->{TYPE} ne "EMPTY") {
1796                         indent;
1797                         ParseElementPull($el, "ndr", "r->", {}, 0, 1);
1798                         deindent;
1799                 }
1800                 pidl "break;";
1801                 pidl "";
1802         }
1803         if (! $have_default) {
1804                 pidl "default:";
1805                 pidl "\treturn ndr_pull_error(ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value \%u\", level);";
1806         }
1807         deindent;
1808         pidl "}";
1809
1810         deindent;
1811         pidl "}";
1812
1813         add_deferred();
1814
1815         end_flags($e);
1816         # restore the old relative_base_offset
1817         pidl "ndr_pull_restore_relative_base_offset(ndr, _save_relative_base_offset);" if defined($e->{PROPERTIES}{relative_base});
1818 }
1819
1820 sub DeclUnion($$)
1821 {
1822         my ($e,$t) = @_;
1823         return ($t ne "pull"?"const ":"") . "union $e->{NAME} *r";
1824 }
1825
1826 sub ArgsUnionNdrSize($)
1827 {
1828         my $d = shift;
1829         return "const union $d->{NAME} *r, uint32_t level, int flags";
1830 }
1831
1832 $typefamily{UNION} = {
1833         PUSH_FN_BODY => \&ParseUnionPush,
1834         DECL => \&DeclUnion,
1835         PULL_FN_BODY => \&ParseUnionPull,
1836         PRINT_FN_BODY => \&ParseUnionPrint,
1837         SIZE_FN_ARGS => \&ArgsUnionNdrSize,
1838         SIZE_FN_BODY => \&ParseUnionNdrSize,
1839 };
1840         
1841 #####################################################################
1842 # parse a typedef - push side
1843 sub ParseTypedefPush($)
1844 {
1845         my($e) = shift;
1846
1847         my $args = $typefamily{$e->{DATA}->{TYPE}}->{DECL}->($e,"push");
1848         fn_declare("push", $e, "NTSTATUS ndr_push_$e->{NAME}(struct ndr_push *ndr, int ndr_flags, $args)") or return;
1849
1850         pidl "{";
1851         indent;
1852         $typefamily{$e->{DATA}->{TYPE}}->{PUSH_FN_BODY}->($e->{DATA}, $e->{NAME});
1853         pidl "return NT_STATUS_OK;";
1854         deindent;
1855         pidl "}";
1856         pidl "";;
1857 }
1858
1859 #####################################################################
1860 # parse a typedef - pull side
1861 sub ParseTypedefPull($)
1862 {
1863         my($e) = shift;
1864
1865         my $args = $typefamily{$e->{DATA}->{TYPE}}->{DECL}->($e,"pull");
1866
1867         fn_declare("pull", $e, "NTSTATUS ndr_pull_$e->{NAME}(struct ndr_pull *ndr, int ndr_flags, $args)") or return;
1868
1869         pidl "{";
1870         indent;
1871         $typefamily{$e->{DATA}->{TYPE}}->{PULL_FN_BODY}->($e->{DATA}, $e->{NAME});
1872         pidl "return NT_STATUS_OK;";
1873         deindent;
1874         pidl "}";
1875         pidl "";
1876 }
1877
1878 #####################################################################
1879 # parse a typedef - print side
1880 sub ParseTypedefPrint($)
1881 {
1882         my($e) = shift;
1883
1884         my $args = $typefamily{$e->{DATA}->{TYPE}}->{DECL}->($e,"print");
1885
1886         pidl_hdr "void ndr_print_$e->{NAME}(struct ndr_print *ndr, const char *name, $args);";
1887
1888         return if (has_property($e, "noprint"));
1889
1890         pidl "_PUBLIC_ void ndr_print_$e->{NAME}(struct ndr_print *ndr, const char *name, $args)";
1891         pidl "{";
1892         indent;
1893         $typefamily{$e->{DATA}->{TYPE}}->{PRINT_FN_BODY}->($e->{DATA}, $e->{NAME});
1894         deindent;
1895         pidl "}";
1896         pidl "";
1897 }
1898
1899 #####################################################################
1900 ## calculate the size of a structure
1901 sub ParseTypedefNdrSize($)
1902 {
1903         my($t) = shift;
1904
1905         my $tf = $typefamily{$t->{DATA}->{TYPE}};
1906         my $args = $tf->{SIZE_FN_ARGS}->($t);
1907
1908         fn_declare("size", $t, "size_t ndr_size_$t->{NAME}($args)") or return;
1909
1910         pidl "{";
1911         indent;
1912         $typefamily{$t->{DATA}->{TYPE}}->{SIZE_FN_BODY}->($t);
1913         deindent;
1914         pidl "}";
1915         pidl "";
1916 }
1917
1918 #####################################################################
1919 # parse a function - print side
1920 sub ParseFunctionPrint($)
1921 {
1922         my($fn) = shift;
1923
1924         pidl_hdr "void ndr_print_$fn->{NAME}(struct ndr_print *ndr, const char *name, int flags, const struct $fn->{NAME} *r);";
1925
1926         return if has_property($fn, "noprint");
1927
1928         pidl "_PUBLIC_ void ndr_print_$fn->{NAME}(struct ndr_print *ndr, const char *name, int flags, const struct $fn->{NAME} *r)";
1929         pidl "{";
1930         indent;
1931
1932         foreach my $e (@{$fn->{ELEMENTS}}) {
1933                 DeclareArrayVariables($e);
1934         }
1935
1936         pidl "ndr_print_struct(ndr, name, \"$fn->{NAME}\");";
1937         pidl "ndr->depth++;";
1938
1939         pidl "if (flags & NDR_SET_VALUES) {";
1940         pidl "\tndr->flags |= LIBNDR_PRINT_SET_VALUES;";
1941         pidl "}";
1942
1943         pidl "if (flags & NDR_IN) {";
1944         indent;
1945         pidl "ndr_print_struct(ndr, \"in\", \"$fn->{NAME}\");";
1946         pidl "ndr->depth++;";
1947
1948         my $env = GenerateFunctionInEnv($fn);
1949         EnvSubstituteValue($env, $fn);
1950
1951         foreach my $e (@{$fn->{ELEMENTS}}) {
1952                 if (grep(/in/,@{$e->{DIRECTION}})) {
1953                         ParseElementPrint($e, "r->in.$e->{NAME}", $env);
1954                 }
1955         }
1956         pidl "ndr->depth--;";
1957         deindent;
1958         pidl "}";
1959         
1960         pidl "if (flags & NDR_OUT) {";
1961         indent;
1962         pidl "ndr_print_struct(ndr, \"out\", \"$fn->{NAME}\");";
1963         pidl "ndr->depth++;";
1964
1965         $env = GenerateFunctionOutEnv($fn);
1966         foreach my $e (@{$fn->{ELEMENTS}}) {
1967                 if (grep(/out/,@{$e->{DIRECTION}})) {
1968                         ParseElementPrint($e, "r->out.$e->{NAME}", $env);
1969                 }
1970         }
1971         if ($fn->{RETURN_TYPE}) {
1972                 pidl "ndr_print_$fn->{RETURN_TYPE}(ndr, \"result\", r->out.result);";
1973         }
1974         pidl "ndr->depth--;";
1975         deindent;
1976         pidl "}";
1977         
1978         pidl "ndr->depth--;";
1979         deindent;
1980         pidl "}";
1981         pidl "";
1982 }
1983
1984 #####################################################################
1985 # parse a function
1986 sub ParseFunctionPush($)
1987
1988         my($fn) = shift;
1989
1990         fn_declare("push", $fn, "NTSTATUS ndr_push_$fn->{NAME}(struct ndr_push *ndr, int flags, const struct $fn->{NAME} *r)") or return;
1991
1992         return if has_property($fn, "nopush");
1993
1994         pidl "{";
1995         indent;
1996
1997         foreach my $e (@{$fn->{ELEMENTS}}) { 
1998                 DeclareArrayVariables($e);
1999         }
2000
2001         pidl "if (flags & NDR_IN) {";
2002         indent;
2003
2004         my $env = GenerateFunctionInEnv($fn);
2005
2006         EnvSubstituteValue($env, $fn);
2007
2008         foreach my $e (@{$fn->{ELEMENTS}}) {
2009                 if (grep(/in/,@{$e->{DIRECTION}})) {
2010                         ParseElementPush($e, "ndr", "r->in.", $env, 1, 1);
2011                 }
2012         }
2013
2014         deindent;
2015         pidl "}";
2016
2017         pidl "if (flags & NDR_OUT) {";
2018         indent;
2019
2020         $env = GenerateFunctionOutEnv($fn);
2021         foreach my $e (@{$fn->{ELEMENTS}}) {
2022                 if (grep(/out/,@{$e->{DIRECTION}})) {
2023                         ParseElementPush($e, "ndr", "r->out.", $env, 1, 1);
2024                 }
2025         }
2026
2027         if ($fn->{RETURN_TYPE}) {
2028                 pidl "NDR_CHECK(ndr_push_$fn->{RETURN_TYPE}(ndr, NDR_SCALARS, r->out.result));";
2029         }
2030     
2031         deindent;
2032         pidl "}";
2033         pidl "return NT_STATUS_OK;";
2034         deindent;
2035         pidl "}";
2036         pidl "";
2037 }
2038
2039 sub AllocateArrayLevel($$$$$)
2040 {
2041         my ($e,$l,$ndr,$env,$size) = @_;
2042
2043         my $var = ParseExpr($e->{NAME}, $env, $e);
2044
2045         my $pl = GetPrevLevel($e, $l);
2046         if (defined($pl) and 
2047             $pl->{TYPE} eq "POINTER" and 
2048             $pl->{POINTER_TYPE} eq "ref"
2049             and not $l->{IS_ZERO_TERMINATED}) {
2050                 pidl "if (ndr->flags & LIBNDR_FLAG_REF_ALLOC) {";
2051                 pidl "\tNDR_PULL_ALLOC_N($ndr, $var, $size);";
2052                 pidl "}";
2053                 if (grep(/in/,@{$e->{DIRECTION}}) and
2054                     grep(/out/,@{$e->{DIRECTION}})) {
2055                         pidl "memcpy(r->out.$e->{NAME},r->in.$e->{NAME},$size * sizeof(*r->in.$e->{NAME}));";
2056                 }
2057                 return;
2058         }
2059
2060         pidl "NDR_PULL_ALLOC_N($ndr, $var, $size);";
2061 }
2062
2063 #####################################################################
2064 # parse a function
2065 sub ParseFunctionPull($)
2066
2067         my($fn) = shift;
2068
2069         # pull function args
2070         fn_declare("pull", $fn, "NTSTATUS ndr_pull_$fn->{NAME}(struct ndr_pull *ndr, int flags, struct $fn->{NAME} *r)") or return;
2071
2072         pidl "{";
2073         indent;
2074
2075         # declare any internal pointers we need
2076         foreach my $e (@{$fn->{ELEMENTS}}) { 
2077                 DeclarePtrVariables($e);
2078                 DeclareArrayVariables($e);
2079         }
2080
2081         my %double_cases = ();
2082         foreach my $e (@{$fn->{ELEMENTS}}) {
2083                 next if ($e->{TYPE} eq "EMPTY");
2084                 next if ($double_cases{"$e->{NAME}"});
2085                 DeclareMemCtxVariables($e);
2086                 $double_cases{"$e->{NAME}"} = 1;
2087         }
2088
2089         pidl "if (flags & NDR_IN) {";
2090         indent;
2091
2092         # auto-init the out section of a structure. I originally argued that
2093         # this was a bad idea as it hides bugs, but coping correctly
2094         # with initialisation and not wiping ref vars is turning
2095         # out to be too tricky (tridge)
2096         foreach my $e (@{$fn->{ELEMENTS}}) {
2097                 next unless grep(/out/, @{$e->{DIRECTION}});
2098                 pidl "ZERO_STRUCT(r->out);";
2099                 pidl "";
2100                 last;
2101         }
2102
2103         my $env = GenerateFunctionInEnv($fn);
2104
2105         foreach my $e (@{$fn->{ELEMENTS}}) {
2106                 next unless (grep(/in/, @{$e->{DIRECTION}}));
2107                 ParseElementPull($e, "ndr", "r->in.", $env, 1, 1);
2108         }
2109
2110         # allocate the "simple" out ref variables. FIXME: Shouldn't this have it's
2111         # own flag rather than be in NDR_IN ?
2112
2113         foreach my $e (@{$fn->{ELEMENTS}}) {
2114                 next unless (grep(/out/, @{$e->{DIRECTION}}));
2115                 next unless ($e->{LEVELS}[0]->{TYPE} eq "POINTER" and 
2116                              $e->{LEVELS}[0]->{POINTER_TYPE} eq "ref");
2117                 next if (($e->{LEVELS}[1]->{TYPE} eq "DATA") and 
2118                                  ($e->{LEVELS}[1]->{DATA_TYPE} eq "string"));
2119                 next if (($e->{LEVELS}[1]->{TYPE} eq "ARRAY") 
2120                         and   $e->{LEVELS}[1]->{IS_ZERO_TERMINATED});
2121
2122                 if ($e->{LEVELS}[1]->{TYPE} eq "ARRAY") {
2123                         my $size = ParseExprExt($e->{LEVELS}[1]->{SIZE_IS}, $env, $e, check_null_pointer($e, $env, \&pidl, "return NT_STATUS_INVALID_PARAMETER_MIX;"));
2124                         
2125                         pidl "NDR_PULL_ALLOC_N(ndr, r->out.$e->{NAME}, $size);";
2126
2127                         if (grep(/in/, @{$e->{DIRECTION}})) {
2128                                 pidl "memcpy(r->out.$e->{NAME}, r->in.$e->{NAME}, $size * sizeof(*r->in.$e->{NAME}));";
2129                         } else {
2130                                 pidl "memset(r->out.$e->{NAME}, 0, $size * sizeof(*r->out.$e->{NAME}));";
2131                         }
2132                 } else {
2133                         pidl "NDR_PULL_ALLOC(ndr, r->out.$e->{NAME});";
2134                 
2135                         if (grep(/in/, @{$e->{DIRECTION}})) {
2136                                 pidl "*r->out.$e->{NAME} = *r->in.$e->{NAME};";
2137                         } else {
2138                                 pidl "ZERO_STRUCTP(r->out.$e->{NAME});";
2139                         }
2140                 }
2141         }
2142
2143         add_deferred();
2144         deindent;
2145         pidl "}";
2146         
2147         pidl "if (flags & NDR_OUT) {";
2148         indent;
2149
2150         $env = GenerateFunctionOutEnv($fn);
2151         foreach my $e (@{$fn->{ELEMENTS}}) {
2152                 next unless grep(/out/, @{$e->{DIRECTION}});
2153                 ParseElementPull($e, "ndr", "r->out.", $env, 1, 1);
2154         }
2155
2156         if ($fn->{RETURN_TYPE}) {
2157                 pidl "NDR_CHECK(ndr_pull_$fn->{RETURN_TYPE}(ndr, NDR_SCALARS, &r->out.result));";
2158         }
2159
2160         add_deferred();
2161         deindent;
2162         pidl "}";
2163
2164         pidl "return NT_STATUS_OK;";
2165         deindent;
2166         pidl "}";
2167         pidl "";
2168 }
2169
2170 #####################################################################
2171 # produce a function call table
2172 sub FunctionTable($)
2173 {
2174         my($interface) = shift;
2175         my $count = 0;
2176         my $uname = uc $interface->{NAME};
2177
2178         return if ($#{$interface->{FUNCTIONS}}+1 == 0);
2179         return unless defined ($interface->{PROPERTIES}->{uuid});
2180
2181         pidl "static const struct dcerpc_interface_call $interface->{NAME}\_calls[] = {";
2182         foreach my $d (@{$interface->{FUNCTIONS}}) {
2183                 next if not defined($d->{OPNUM});
2184                 pidl "\t{";
2185                 pidl "\t\t\"$d->{NAME}\",";
2186                 pidl "\t\tsizeof(struct $d->{NAME}),";
2187                 pidl "\t\t(ndr_push_flags_fn_t) ndr_push_$d->{NAME},";
2188                 pidl "\t\t(ndr_pull_flags_fn_t) ndr_pull_$d->{NAME},";
2189                 pidl "\t\t(ndr_print_function_t) ndr_print_$d->{NAME},";
2190                 pidl "\t\t".($d->{ASYNC}?"True":"False").",";
2191                 pidl "\t},";
2192                 $count++;
2193         }
2194         pidl "\t{ NULL, 0, NULL, NULL, NULL, False }";
2195         pidl "};";
2196         pidl "";
2197
2198         pidl "static const char * const $interface->{NAME}\_endpoint_strings[] = {";
2199         foreach my $ep (@{$interface->{ENDPOINTS}}) {
2200                 pidl "\t$ep, ";
2201         }
2202         my $endpoint_count = $#{$interface->{ENDPOINTS}}+1;
2203         
2204         pidl "};";
2205         pidl "";
2206
2207         pidl "static const struct dcerpc_endpoint_list $interface->{NAME}\_endpoints = {";
2208         pidl "\t.count\t= $endpoint_count,";
2209         pidl "\t.names\t= $interface->{NAME}\_endpoint_strings";
2210         pidl "};";
2211         pidl "";
2212
2213         if (! defined $interface->{PROPERTIES}->{authservice}) {
2214                 $interface->{PROPERTIES}->{authservice} = "\"host\"";
2215         }
2216
2217         my @a = split / /, $interface->{PROPERTIES}->{authservice};
2218         my $authservice_count = $#a + 1;
2219
2220         pidl "static const char * const $interface->{NAME}\_authservice_strings[] = {";
2221         foreach my $ap (@a) {
2222                 pidl "\t$ap, ";
2223         }
2224         pidl "};";
2225         pidl "";
2226
2227         pidl "static const struct dcerpc_authservice_list $interface->{NAME}\_authservices = {";
2228         pidl "\t.count\t= $endpoint_count,";
2229         pidl "\t.names\t= $interface->{NAME}\_authservice_strings";
2230         pidl "};";
2231         pidl "";
2232
2233         pidl "\nconst struct dcerpc_interface_table dcerpc_table_$interface->{NAME} = {";
2234         pidl "\t.name\t\t= \"$interface->{NAME}\",";
2235         pidl "\t.syntax_id\t= {";
2236         pidl "\t\t" . print_uuid($interface->{UUID}) .",";
2237         pidl "\t\tDCERPC_$uname\_VERSION";
2238         pidl "\t},";
2239         pidl "\t.helpstring\t= DCERPC_$uname\_HELPSTRING,";
2240         pidl "\t.num_calls\t= $count,";
2241         pidl "\t.calls\t\t= $interface->{NAME}\_calls,";
2242         pidl "\t.endpoints\t= &$interface->{NAME}\_endpoints,";
2243         pidl "\t.authservices\t= &$interface->{NAME}\_authservices";
2244         pidl "};";
2245         pidl "";
2246
2247 }
2248
2249 #####################################################################
2250 # generate include statements for imported idl files
2251 sub HeaderImport
2252 {
2253         my @imports = @_;
2254         foreach (@imports) {
2255                 s/\.idl\"$//;
2256                 s/^\"//;
2257                 pidl choose_header("librpc/gen_ndr/ndr_$_\.h", "gen_ndr/ndr_$_.h");
2258         }
2259 }
2260
2261 #####################################################################
2262 # generate include statements for included header files
2263 sub HeaderInclude
2264 {
2265         my @includes = @_;
2266         foreach (@includes) {
2267                 pidl_hdr "#include $_";
2268         }
2269 }
2270
2271 #####################################################################
2272 # generate prototypes and defines for the interface definitions
2273 # FIXME: these prototypes are for the DCE/RPC client functions, not the 
2274 # NDR parser and so do not belong here, technically speaking
2275 sub HeaderInterface($)
2276 {
2277         my($interface) = shift;
2278
2279         my $count = 0;
2280
2281         pidl_hdr choose_header("librpc/ndr/libndr.h", "ndr.h");
2282
2283         if (has_property($interface, "object")) {
2284                 pidl choose_header("librpc/gen_ndr/ndr_orpc.h", "ndr/orpc.h");
2285         }
2286
2287         if (defined $interface->{PROPERTIES}->{helper}) {
2288                 HeaderInclude(split / /, $interface->{PROPERTIES}->{helper});
2289         }
2290
2291         if (defined $interface->{PROPERTIES}->{uuid}) {
2292                 my $name = uc $interface->{NAME};
2293                 pidl_hdr "#define DCERPC_$name\_UUID " . 
2294                 Parse::Pidl::Util::make_str(lc($interface->{PROPERTIES}->{uuid}));
2295
2296                 if(!defined $interface->{PROPERTIES}->{version}) { $interface->{PROPERTIES}->{version} = "0.0"; }
2297                 pidl_hdr "#define DCERPC_$name\_VERSION $interface->{PROPERTIES}->{version}";
2298
2299                 pidl_hdr "#define DCERPC_$name\_NAME \"$interface->{NAME}\"";
2300
2301                 if(!defined $interface->{PROPERTIES}->{helpstring}) { $interface->{PROPERTIES}->{helpstring} = "NULL"; }
2302                 pidl_hdr "#define DCERPC_$name\_HELPSTRING $interface->{PROPERTIES}->{helpstring}";
2303
2304                 pidl_hdr "extern const struct dcerpc_interface_table dcerpc_table_$interface->{NAME};";
2305                 pidl_hdr "NTSTATUS dcerpc_server_$interface->{NAME}_init(void);";
2306         }
2307
2308         foreach (@{$interface->{FUNCTIONS}}) {
2309                 next if has_property($_, "noopnum");
2310                 next if grep(/$_->{NAME}/,@{$interface->{INHERITED_FUNCTIONS}});
2311                 my $u_name = uc $_->{NAME};
2312         
2313                 my $val = sprintf("0x%02x", $count);
2314                 if (defined($interface->{BASE})) {
2315                         $val .= " + DCERPC_" . uc $interface->{BASE} . "_CALL_COUNT";
2316                 }
2317                 
2318                 pidl_hdr "#define DCERPC_$u_name ($val)";
2319
2320                 pidl_hdr "";
2321                 $count++;
2322         }
2323
2324         my $val = $count;
2325
2326         if (defined($interface->{BASE})) {
2327                 $val .= " + DCERPC_" . uc $interface->{BASE} . "_CALL_COUNT";
2328         }
2329
2330         pidl_hdr "#define DCERPC_" . uc $interface->{NAME} . "_CALL_COUNT ($val)";
2331
2332 }
2333
2334 #####################################################################
2335 # parse the interface definitions
2336 sub ParseInterface($$)
2337 {
2338         my($interface,$needed) = @_;
2339
2340         pidl_hdr "#ifndef _HEADER_NDR_$interface->{NAME}";
2341         pidl_hdr "#define _HEADER_NDR_$interface->{NAME}";
2342
2343         pidl_hdr "";
2344
2345         if ($needed->{"compression"}) {
2346                 pidl choose_header("librpc/ndr/ndr_compression.h", "ndr/compression.h");
2347         }
2348
2349         HeaderInterface($interface);
2350
2351         # Typedefs
2352         foreach my $d (@{$interface->{TYPES}}) {
2353                 ($needed->{"push_$d->{NAME}"}) && ParseTypedefPush($d);
2354                 ($needed->{"pull_$d->{NAME}"}) && ParseTypedefPull($d);
2355                 ($needed->{"print_$d->{NAME}"}) && ParseTypedefPrint($d);
2356
2357                 # Make sure we don't generate a function twice...
2358                 $needed->{"push_$d->{NAME}"} = $needed->{"pull_$d->{NAME}"} = 
2359                         $needed->{"print_$d->{NAME}"} = 0;
2360
2361                 ($needed->{"ndr_size_$d->{NAME}"}) && ParseTypedefNdrSize($d);
2362         }
2363
2364         # Functions
2365         foreach my $d (@{$interface->{FUNCTIONS}}) {
2366                 ($needed->{"push_$d->{NAME}"}) && ParseFunctionPush($d);
2367                 ($needed->{"pull_$d->{NAME}"}) && ParseFunctionPull($d);
2368                 ($needed->{"print_$d->{NAME}"}) && ParseFunctionPrint($d);
2369
2370                 # Make sure we don't generate a function twice...
2371                 $needed->{"push_$d->{NAME}"} = $needed->{"pull_$d->{NAME}"} = 
2372                         $needed->{"print_$d->{NAME}"} = 0;
2373         }
2374
2375         FunctionTable($interface);
2376
2377         pidl_hdr "#endif /* _HEADER_NDR_$interface->{NAME} */";
2378 }
2379
2380 sub GenerateIncludes()
2381 {
2382         if (is_intree()) {
2383                 pidl "#include \"includes.h\"";
2384         } else {
2385                 pidl "#define _GNU_SOURCE";
2386                 pidl "#include <stdint.h>";
2387                 pidl "#include <stdlib.h>";
2388                 pidl "#include <stdio.h>";
2389                 pidl "#include <stdbool.h>";
2390                 pidl "#include <stdarg.h>";
2391                 pidl "#include <string.h>";
2392         }
2393
2394         # Samba3 has everything in include/includes.h
2395         if (is_intree() != 3) {
2396                 pidl choose_header("libcli/util/nterr.h", "core/nterr.h");
2397                 pidl choose_header("librpc/gen_ndr/ndr_misc.h", "gen_ndr/ndr_misc.h");
2398                 pidl choose_header("librpc/gen_ndr/ndr_dcerpc.h", "gen_ndr/ndr_dcerpc.h");
2399                 pidl choose_header("librpc/rpc/dcerpc.h", "dcerpc.h"); #FIXME: This shouldn't be here!
2400         }
2401 }
2402
2403 #####################################################################
2404 # parse a parsed IDL structure back into an IDL file
2405 sub Parse($$$)
2406 {
2407         my($ndr,$gen_header,$ndr_header) = @_;
2408
2409         $tabs = "";
2410         $res = "";
2411
2412         $res_hdr = "";
2413         pidl_hdr "/* header auto-generated by pidl */";
2414         pidl_hdr "";
2415         pidl_hdr "#include \"$gen_header\"" if ($gen_header);
2416         pidl_hdr "";
2417
2418         pidl "/* parser auto-generated by pidl */";
2419         pidl "";
2420         GenerateIncludes();
2421         pidl "#include \"$ndr_header\"" if ($ndr_header);
2422         pidl "";
2423
2424         my %needed = ();
2425
2426         foreach (@{$ndr}) {
2427                 ($_->{TYPE} eq "INTERFACE") && NeededInterface($_, \%needed);
2428         }
2429
2430         foreach (@{$ndr}) {
2431                 ($_->{TYPE} eq "INTERFACE") && ParseInterface($_, \%needed);
2432                 ($_->{TYPE} eq "IMPORT") && HeaderImport(@{$_->{PATHS}});
2433                 ($_->{TYPE} eq "INCLUDE") && HeaderInclude(@{$_->{PATHS}});
2434         }
2435
2436         return ($res_hdr, $res);
2437 }
2438
2439 sub NeededFunction($$)
2440 {
2441         my ($fn,$needed) = @_;
2442         $needed->{"pull_$fn->{NAME}"} = 1;
2443         $needed->{"push_$fn->{NAME}"} = 1;
2444         $needed->{"print_$fn->{NAME}"} = 1;
2445         foreach my $e (@{$fn->{ELEMENTS}}) {
2446                 $e->{PARENT} = $fn;
2447                 unless(defined($needed->{"pull_$e->{TYPE}"})) {
2448                         $needed->{"pull_$e->{TYPE}"} = 1;
2449                 }
2450                 unless(defined($needed->{"push_$e->{TYPE}"})) {
2451                         $needed->{"push_$e->{TYPE}"} = 1;
2452                 }
2453                 unless(defined($needed->{"print_$e->{TYPE}"})) {
2454                         $needed->{"print_$e->{TYPE}"} = 1;
2455                 }
2456         }
2457 }
2458
2459 sub NeededTypedef($$)
2460 {
2461         my ($t,$needed) = @_;
2462         if (has_property($t, "public")) {
2463                 $needed->{"pull_$t->{NAME}"} = 1;
2464                 $needed->{"push_$t->{NAME}"} = 1;
2465                 $needed->{"print_$t->{NAME}"} = 1;
2466         }
2467
2468         if ($t->{DATA}->{TYPE} eq "STRUCT" or $t->{DATA}->{TYPE} eq "UNION") {
2469                 if (has_property($t, "gensize")) {
2470                         $needed->{"ndr_size_$t->{NAME}"} = 1;
2471                 }
2472
2473                 for my $e (@{$t->{DATA}->{ELEMENTS}}) {
2474                         $e->{PARENT} = $t->{DATA};
2475                         if (has_property($e, "compression")) { 
2476                                 $needed->{"compression"} = 1;
2477                         }
2478                         if ($needed->{"pull_$t->{NAME}"} and
2479                                 not defined($needed->{"pull_$e->{TYPE}"})) {
2480                                 $needed->{"pull_$e->{TYPE}"} = 1;
2481                         }
2482                         if ($needed->{"push_$t->{NAME}"} and
2483                                 not defined($needed->{"push_$e->{TYPE}"})) {
2484                                 $needed->{"push_$e->{TYPE}"} = 1;
2485                         }
2486                         if ($needed->{"print_$t->{NAME}"} and 
2487                                 not defined($needed->{"print_$e->{TYPE}"})) {
2488                                 $needed->{"print_$e->{TYPE}"} = 1;
2489                         }
2490                 }
2491         }
2492 }
2493
2494 #####################################################################
2495 # work out what parse functions are needed
2496 sub NeededInterface($$)
2497 {
2498         my ($interface,$needed) = @_;
2499         NeededFunction($_, $needed) foreach (@{$interface->{FUNCTIONS}});
2500         NeededTypedef($_, $needed) foreach (reverse @{$interface->{TYPES}});
2501 }
2502
2503 1;