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