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