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