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