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