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