r8612: Parse::Pidl::NDR:
[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);
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 ArgsEnumPush($)
1104 {
1105         my $e = shift;
1106         return "struct ndr_push *ndr, int ndr_flags, enum $e->{NAME} r";
1107 }
1108
1109 sub ArgsEnumPrint($)
1110 {
1111         my $e = shift;
1112         return "struct ndr_print *ndr, const char *name, enum $e->{NAME} r";
1113 }
1114
1115 sub ArgsEnumPull($)
1116 {
1117         my $e = shift;
1118         return "struct ndr_pull *ndr, int ndr_flags, enum $e->{NAME} *r";
1119 }
1120
1121 $typefamily{ENUM} = {
1122         PUSH_FN_BODY => \&ParseEnumPush,
1123         PUSH_FN_ARGS => \&ArgsEnumPush,
1124         PULL_FN_BODY => \&ParseEnumPull,
1125         PULL_FN_ARGS => \&ArgsEnumPull,
1126         PRINT_FN_BODY => \&ParseEnumPrint,
1127         PRINT_FN_ARGS => \&ArgsEnumPrint,
1128 };
1129
1130 #####################################################################
1131 # generate a push function for a bitmap
1132 sub ParseBitmapPush($$)
1133 {
1134         my($bitmap,$name) = @_;
1135         my($type_fn) = $bitmap->{BASE_TYPE};
1136
1137         start_flags($bitmap);
1138
1139         pidl "NDR_CHECK(ndr_push_$type_fn(ndr, NDR_SCALARS, r));";
1140
1141         end_flags($bitmap);
1142 }
1143
1144 #####################################################################
1145 # generate a pull function for an bitmap
1146 sub ParseBitmapPull($$)
1147 {
1148         my($bitmap,$name) = @_;
1149         my $type_fn = $bitmap->{BASE_TYPE};
1150         my($type_decl) = Parse::Pidl::Typelist::mapType($bitmap->{BASE_TYPE});
1151
1152         pidl "$type_decl v;";
1153         start_flags($bitmap);
1154         pidl "NDR_CHECK(ndr_pull_$type_fn(ndr, NDR_SCALARS, &v));";
1155         pidl "*r = v;";
1156
1157         end_flags($bitmap);
1158 }
1159
1160 #####################################################################
1161 # generate a print function for an bitmap
1162 sub ParseBitmapPrintElement($$$)
1163 {
1164         my($e,$bitmap,$name) = @_;
1165         my($type_decl) = Parse::Pidl::Typelist::mapType($bitmap->{BASE_TYPE});
1166         my($type_fn) = $bitmap->{BASE_TYPE};
1167         my($flag);
1168
1169         if ($e =~ /^(\w+) .*$/) {
1170                 $flag = "$1";
1171         } else {
1172                 die "Bitmap: \"$name\" invalid Flag: \"$e\"";
1173         }
1174
1175         pidl "ndr_print_bitmap_flag(ndr, sizeof($type_decl), \"$flag\", $flag, r);";
1176 }
1177
1178 #####################################################################
1179 # generate a print function for an bitmap
1180 sub ParseBitmapPrint($$)
1181 {
1182         my($bitmap,$name) = @_;
1183         my($type_decl) = Parse::Pidl::Typelist::mapType($bitmap->{TYPE});
1184         my($type_fn) = $bitmap->{BASE_TYPE};
1185
1186         start_flags($bitmap);
1187
1188         pidl "ndr_print_$type_fn(ndr, name, r);";
1189
1190         pidl "ndr->depth++;";
1191         foreach my $e (@{$bitmap->{ELEMENTS}}) {
1192                 ParseBitmapPrintElement($e, $bitmap, $name);
1193         }
1194         pidl "ndr->depth--;";
1195
1196         end_flags($bitmap);
1197 }
1198
1199 sub ArgsBitmapPush($)
1200 {
1201         my $e = shift;
1202         my $type_decl = Parse::Pidl::Typelist::mapType($e->{DATA}->{BASE_TYPE});
1203         return "struct ndr_push *ndr, int ndr_flags, $type_decl r";
1204 }
1205
1206 sub ArgsBitmapPrint($)
1207 {
1208         my $e = shift;
1209         my $type_decl = Parse::Pidl::Typelist::mapType($e->{DATA}->{BASE_TYPE});
1210         return "struct ndr_print *ndr, const char *name, $type_decl r";
1211 }
1212
1213 sub ArgsBitmapPull($)
1214 {
1215         my $e = shift;
1216         my $type_decl = Parse::Pidl::Typelist::mapType($e->{DATA}->{BASE_TYPE});
1217         return "struct ndr_pull *ndr, int ndr_flags, $type_decl *r";
1218 }
1219
1220 $typefamily{BITMAP} = {
1221         PUSH_FN_BODY => \&ParseBitmapPush,
1222         PUSH_FN_ARGS => \&ArgsBitmapPush,
1223         PULL_FN_BODY => \&ParseBitmapPull,
1224         PULL_FN_ARGS => \&ArgsBitmapPull,
1225         PRINT_FN_BODY => \&ParseBitmapPrint,
1226         PRINT_FN_ARGS => \&ArgsBitmapPrint,
1227 };
1228
1229 #####################################################################
1230 # generate a struct print function
1231 sub ParseStructPrint($$)
1232 {
1233         my($struct,$name) = @_;
1234
1235         return unless defined $struct->{ELEMENTS};
1236
1237         my $env = GenerateStructEnv($struct);
1238
1239         foreach my $e (@{$struct->{ELEMENTS}}) {
1240                 DeclareArrayVariables($e);
1241         }
1242
1243         pidl "ndr_print_struct(ndr, name, \"$name\");";
1244
1245         start_flags($struct);
1246
1247         pidl "ndr->depth++;";
1248         foreach my $e (@{$struct->{ELEMENTS}}) {
1249                 ParseElementPrint($e, "r->$e->{NAME}", $env);
1250         }
1251         pidl "ndr->depth--;";
1252
1253         end_flags($struct);
1254 }
1255
1256 sub DeclarePtrVariables($)
1257 {
1258         my $e = shift;
1259         foreach my $l (@{$e->{LEVELS}}) {
1260                 if ($l->{TYPE} eq "POINTER" and 
1261                         not ($l->{POINTER_TYPE} eq "ref" and $l->{LEVEL} eq "TOP")) {
1262                         pidl "uint32_t _ptr_$e->{NAME};";
1263                         last;
1264                 }
1265         }
1266 }
1267
1268 sub DeclareArrayVariables($)
1269 {
1270         my $e = shift;
1271
1272         foreach my $l (@{$e->{LEVELS}}) {
1273                 next if has_fast_array($e,$l);
1274                 next if has_property($e, "charset");
1275                 if ($l->{TYPE} eq "ARRAY") {
1276                         pidl "uint32_t cntr_$e->{NAME}_$l->{LEVEL_INDEX};";
1277                 }
1278         }
1279 }
1280
1281 #####################################################################
1282 # parse a struct - pull side
1283 sub ParseStructPull($$)
1284 {
1285         my($struct,$name) = @_;
1286
1287         return unless defined $struct->{ELEMENTS};
1288
1289         my $env = GenerateStructEnv($struct);
1290
1291         # declare any internal pointers we need
1292         foreach my $e (@{$struct->{ELEMENTS}}) {
1293                 DeclarePtrVariables($e);
1294                 DeclareArrayVariables($e);
1295         }
1296
1297         # save the old relative_base_offset
1298         pidl "uint32_t _save_relative_base_offset = ndr_pull_get_relative_base_offset(ndr);" if defined($struct->{PROPERTIES}{relative_base});
1299
1300         start_flags($struct);
1301
1302         pidl "if (ndr_flags & NDR_SCALARS) {";
1303         indent;
1304
1305         if (defined $struct->{SURROUNDING_ELEMENT}) {
1306                 pidl "NDR_CHECK(ndr_pull_array_size(ndr, &r->$struct->{SURROUNDING_ELEMENT}->{NAME}));";
1307         }
1308
1309         pidl "NDR_CHECK(ndr_pull_align(ndr, $struct->{ALIGN}));";
1310
1311         if (defined($struct->{PROPERTIES}{relative_base})) {
1312                 # set the current offset as base for relative pointers
1313                 # and store it based on the toplevel struct/union
1314                 pidl "NDR_CHECK(ndr_pull_setup_relative_base_offset1(ndr, r, ndr->offset));";
1315         }
1316
1317         foreach my $e (@{$struct->{ELEMENTS}}) {
1318                 ParseElementPull($e, "ndr", "r->", $env, 1, 0);
1319         }       
1320
1321         deindent;
1322         pidl "}";
1323         pidl "if (ndr_flags & NDR_BUFFERS) {";
1324         indent;
1325         if (defined($struct->{PROPERTIES}{relative_base})) {
1326                 # retrieve the current offset as base for relative pointers
1327                 # based on the toplevel struct/union
1328                 pidl "NDR_CHECK(ndr_pull_setup_relative_base_offset2(ndr, r));";
1329         }
1330         foreach my $e (@{$struct->{ELEMENTS}}) {
1331                 ParseElementPull($e, "ndr", "r->", $env, 0, 1);
1332         }
1333
1334         deindent;
1335         pidl "}";
1336
1337         end_flags($struct);
1338         # restore the old relative_base_offset
1339         pidl "ndr_pull_restore_relative_base_offset(ndr, _save_relative_base_offset);" if defined($struct->{PROPERTIES}{relative_base});
1340 }
1341
1342 #####################################################################
1343 # calculate size of ndr struct
1344 sub ParseStructNdrSize($)
1345 {
1346         my $t = shift;
1347         my $sizevar;
1348
1349         if (my $flags = has_property($t, "flag")) {
1350                 pidl "flags |= $flags;";
1351         }
1352         pidl "return ndr_size_struct(r, flags, (ndr_push_flags_fn_t)ndr_push_$t->{NAME});";
1353 }
1354
1355 sub ArgsStructPush($)
1356 {
1357         my $e = shift;
1358         return "struct ndr_push *ndr, int ndr_flags, const struct $e->{NAME} *r";
1359 }
1360
1361 sub ArgsStructPrint($)
1362 {
1363         my $e = shift;
1364         return "struct ndr_print *ndr, const char *name, const struct $e->{NAME} *r";
1365 }
1366
1367 sub ArgsStructPull($)
1368 {
1369         my $e = shift;
1370         return "struct ndr_pull *ndr, int ndr_flags, struct $e->{NAME} *r";
1371 }
1372
1373 sub ArgsStructNdrSize($)
1374 {
1375         my $d = shift;
1376         return "const struct $d->{NAME} *r, int flags";
1377 }
1378
1379 $typefamily{STRUCT} = {
1380         PUSH_FN_BODY => \&ParseStructPush,
1381         PUSH_FN_ARGS => \&ArgsStructPush,
1382         PULL_FN_BODY => \&ParseStructPull,
1383         PULL_FN_ARGS => \&ArgsStructPull,
1384         PRINT_FN_BODY => \&ParseStructPrint,
1385         PRINT_FN_ARGS => \&ArgsStructPrint,
1386         SIZE_FN_BODY => \&ParseStructNdrSize,
1387         SIZE_FN_ARGS => \&ArgsStructNdrSize,
1388 };
1389
1390 #####################################################################
1391 # calculate size of ndr struct
1392 sub ParseUnionNdrSize($)
1393 {
1394         my $t = shift;
1395         my $sizevar;
1396
1397         if (my $flags = has_property($t, "flag")) {
1398                 pidl "flags |= $flags;";
1399         }
1400
1401         pidl "return ndr_size_union(r, flags, level, (ndr_push_flags_fn_t)ndr_push_$t->{NAME});";
1402 }
1403
1404 #####################################################################
1405 # parse a union - push side
1406 sub ParseUnionPush($$)
1407 {
1408         my ($e,$name) = @_;
1409         my $have_default = 0;
1410
1411         # save the old relative_base_offset
1412         pidl "uint32_t _save_relative_base_offset = ndr_push_get_relative_base_offset(ndr);" if defined($e->{PROPERTIES}{relative_base});
1413         pidl "int level;";
1414
1415         start_flags($e);
1416
1417         pidl "level = ndr_push_get_switch_value(ndr, r);";
1418
1419         pidl "if (ndr_flags & NDR_SCALARS) {";
1420         indent;
1421
1422         if (defined($e->{SWITCH_TYPE})) {
1423                 pidl "NDR_CHECK(ndr_push_$e->{SWITCH_TYPE}(ndr, NDR_SCALARS, level));";
1424         }
1425
1426         pidl "switch (level) {";
1427         indent;
1428         foreach my $el (@{$e->{ELEMENTS}}) {
1429                 if ($el->{CASE} eq "default") {
1430                         $have_default = 1;
1431                 }
1432                 pidl "$el->{CASE}:";
1433
1434                 if ($el->{TYPE} ne "EMPTY") {
1435                         indent;
1436                         if (defined($e->{PROPERTIES}{relative_base})) {
1437                                 pidl "NDR_CHECK(ndr_push_align(ndr, $el->{ALIGN}));";
1438                                 # set the current offset as base for relative pointers
1439                                 # and store it based on the toplevel struct/union
1440                                 pidl "NDR_CHECK(ndr_push_setup_relative_base_offset1(ndr, r, ndr->offset));";
1441                         }
1442                         DeclareArrayVariables($el);
1443                         ParseElementPush($el, "ndr", "r->", {}, 1, 0);
1444                         deindent;
1445                 }
1446                 pidl "break;";
1447                 pidl "";
1448         }
1449         if (! $have_default) {
1450                 pidl "default:";
1451                 pidl "\treturn ndr_push_error(ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value \%u\", level);";
1452         }
1453         deindent;
1454         pidl "}";
1455         deindent;
1456         pidl "}";
1457         pidl "if (ndr_flags & NDR_BUFFERS) {";
1458         indent;
1459         if (defined($e->{PROPERTIES}{relative_base})) {
1460                 # retrieve the current offset as base for relative pointers
1461                 # based on the toplevel struct/union
1462                 pidl "NDR_CHECK(ndr_push_setup_relative_base_offset2(ndr, r));";
1463         }
1464         pidl "switch (level) {";
1465         indent;
1466         foreach my $el (@{$e->{ELEMENTS}}) {
1467                 pidl "$el->{CASE}:";
1468                 if ($el->{TYPE} ne "EMPTY") {
1469                         indent;
1470                         ParseElementPush($el, "ndr", "r->", {}, 0, 1);
1471                         deindent;
1472                 }
1473                 pidl "break;";
1474                 pidl "";
1475         }
1476         if (! $have_default) {
1477                 pidl "default:";
1478                 pidl "\treturn ndr_push_error(ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value \%u\", level);";
1479         }
1480         deindent;
1481         pidl "}";
1482
1483         deindent;
1484         pidl "}";
1485         end_flags($e);
1486         # restore the old relative_base_offset
1487         pidl "ndr_push_restore_relative_base_offset(ndr, _save_relative_base_offset);" if defined($e->{PROPERTIES}{relative_base});
1488 }
1489
1490 #####################################################################
1491 # print a union
1492 sub ParseUnionPrint($$)
1493 {
1494         my ($e,$name) = @_;
1495         my $have_default = 0;
1496
1497         pidl "int level = ndr_print_get_switch_value(ndr, r);";
1498
1499         foreach my $el (@{$e->{ELEMENTS}}) {
1500                 DeclareArrayVariables($el);
1501         }
1502
1503         pidl "ndr_print_union(ndr, name, level, \"$name\");";
1504         start_flags($e);
1505
1506         pidl "switch (level) {";
1507         indent;
1508         foreach my $el (@{$e->{ELEMENTS}}) {
1509                 if ($el->{CASE} eq "default") {
1510                         $have_default = 1;
1511                 }
1512                 pidl "$el->{CASE}:";
1513                 if ($el->{TYPE} ne "EMPTY") {
1514                         indent;
1515                         ParseElementPrint($el, "r->$el->{NAME}", {});
1516                         deindent;
1517                 }
1518                 pidl "break;";
1519                 pidl "";
1520         }
1521         if (! $have_default) {
1522                 pidl "default:";
1523                 pidl "\tndr_print_bad_level(ndr, name, level);";
1524         }
1525         deindent;
1526         pidl "}";
1527
1528         end_flags($e);
1529 }
1530
1531 #####################################################################
1532 # parse a union - pull side
1533 sub ParseUnionPull($$)
1534 {
1535         my ($e,$name) = @_;
1536         my $have_default = 0;
1537         my $switch_type = $e->{SWITCH_TYPE};
1538
1539         # save the old relative_base_offset
1540         pidl "uint32_t _save_relative_base_offset = ndr_pull_get_relative_base_offset(ndr);" if defined($e->{PROPERTIES}{relative_base});
1541         pidl "int level;";
1542         if (defined($switch_type)) {
1543                 if (Parse::Pidl::Typelist::typeIs($switch_type, "ENUM")) {
1544                         $switch_type = Parse::Pidl::Typelist::enum_type_fn(getType($switch_type));
1545                 }
1546                 pidl Parse::Pidl::Typelist::mapType($switch_type) . " _level;";
1547         }
1548
1549         start_flags($e);
1550
1551         pidl "level = ndr_pull_get_switch_value(ndr, r);";
1552
1553         pidl "if (ndr_flags & NDR_SCALARS) {";
1554         indent;
1555
1556         if (defined($switch_type)) {
1557                 pidl "NDR_CHECK(ndr_pull_$switch_type(ndr, NDR_SCALARS, &_level));";
1558                 pidl "if (_level != level) {"; 
1559                 pidl "\treturn ndr_pull_error(ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value %u for $name\", _level);";
1560                 pidl "}";
1561         }
1562
1563         pidl "switch (level) {";
1564         indent;
1565         foreach my $el (@{$e->{ELEMENTS}}) {
1566                 if ($el->{CASE} eq "default") {
1567                         $have_default = 1;
1568                 } 
1569                 pidl "$el->{CASE}: {";
1570
1571                 if ($el->{TYPE} ne "EMPTY") {
1572                         indent;
1573                         DeclarePtrVariables($el);
1574                         DeclareArrayVariables($el);
1575                         if (defined($e->{PROPERTIES}{relative_base})) {
1576                                 pidl "NDR_CHECK(ndr_pull_align(ndr, $el->{ALIGN}));";
1577                                 # set the current offset as base for relative pointers
1578                                 # and store it based on the toplevel struct/union
1579                                 pidl "NDR_CHECK(ndr_pull_setup_relative_base_offset1(ndr, r, ndr->offset));";
1580                         }
1581                         ParseElementPull($el, "ndr", "r->", {}, 1, 0);
1582                         deindent;
1583                 }
1584                 pidl "break; }";
1585                 pidl "";
1586         }
1587         if (! $have_default) {
1588                 pidl "default:";
1589                 pidl "\treturn ndr_pull_error(ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value \%u\", level);";
1590         }
1591         deindent;
1592         pidl "}";
1593         deindent;
1594         pidl "}";
1595         pidl "if (ndr_flags & NDR_BUFFERS) {";
1596         indent;
1597         if (defined($e->{PROPERTIES}{relative_base})) {
1598                 # retrieve the current offset as base for relative pointers
1599                 # based on the toplevel struct/union
1600                 pidl "NDR_CHECK(ndr_pull_setup_relative_base_offset2(ndr, r));";
1601         }
1602         pidl "switch (level) {";
1603         indent;
1604         foreach my $el (@{$e->{ELEMENTS}}) {
1605                 pidl "$el->{CASE}:";
1606                 if ($el->{TYPE} ne "EMPTY") {
1607                         indent;
1608                         ParseElementPull($el, "ndr", "r->", {}, 0, 1);
1609                         deindent;
1610                 }
1611                 pidl "break;";
1612                 pidl "";
1613         }
1614         if (! $have_default) {
1615                 pidl "default:";
1616                 pidl "\treturn ndr_pull_error(ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value \%u\", level);";
1617         }
1618         deindent;
1619         pidl "}";
1620
1621         deindent;
1622         pidl "}";
1623         end_flags($e);
1624         # restore the old relative_base_offset
1625         pidl "ndr_pull_restore_relative_base_offset(ndr, _save_relative_base_offset);" if defined($e->{PROPERTIES}{relative_base});
1626 }
1627
1628 sub ArgsUnionPush($)
1629 {
1630         my $e = shift;
1631         return "struct ndr_push *ndr, int ndr_flags, const union $e->{NAME} *r";
1632 }
1633
1634 sub ArgsUnionPrint($)
1635 {
1636         my $e = shift;
1637         return "struct ndr_print *ndr, const char *name, const union $e->{NAME} *r";
1638 }
1639
1640 sub ArgsUnionPull($)
1641 {
1642         my $e = shift;
1643         return "struct ndr_pull *ndr, int ndr_flags, union $e->{NAME} *r";
1644 }
1645
1646 sub ArgsUnionNdrSize($)
1647 {
1648         my $d = shift;
1649         return "const union $d->{NAME} *r, uint32_t level, int flags";
1650 }
1651
1652 $typefamily{UNION} = {
1653         PUSH_FN_BODY => \&ParseUnionPush,
1654         PUSH_FN_ARGS => \&ArgsUnionPush,
1655         PULL_FN_BODY => \&ParseUnionPull,
1656         PULL_FN_ARGS => \&ArgsUnionPull,
1657         PRINT_FN_BODY => \&ParseUnionPrint,
1658         PRINT_FN_ARGS => \&ArgsUnionPrint,
1659         SIZE_FN_ARGS => \&ArgsUnionNdrSize,
1660         SIZE_FN_BODY => \&ParseUnionNdrSize,
1661 };
1662         
1663 #####################################################################
1664 # parse a typedef - push side
1665 sub ParseTypedefPush($)
1666 {
1667         my($e) = shift;
1668
1669         my $args = $typefamily{$e->{DATA}->{TYPE}}->{PUSH_FN_ARGS}->($e);
1670         pidl fn_prefix($e) . "NTSTATUS ndr_push_$e->{NAME}($args)";
1671
1672         pidl "{";
1673         indent;
1674         $typefamily{$e->{DATA}->{TYPE}}->{PUSH_FN_BODY}->($e->{DATA}, $e->{NAME});
1675         pidl "return NT_STATUS_OK;";
1676         deindent;
1677         pidl "}";
1678         pidl "";;
1679 }
1680
1681 #####################################################################
1682 # parse a typedef - pull side
1683 sub ParseTypedefPull($)
1684 {
1685         my($e) = shift;
1686
1687         my $args = $typefamily{$e->{DATA}->{TYPE}}->{PULL_FN_ARGS}->($e);
1688
1689         pidl fn_prefix($e) . "NTSTATUS ndr_pull_$e->{NAME}($args)";
1690
1691         pidl "{";
1692         indent;
1693         $typefamily{$e->{DATA}->{TYPE}}->{PULL_FN_BODY}->($e->{DATA}, $e->{NAME});
1694         pidl "return NT_STATUS_OK;";
1695         deindent;
1696         pidl "}";
1697         pidl "";
1698 }
1699
1700 #####################################################################
1701 # parse a typedef - print side
1702 sub ParseTypedefPrint($)
1703 {
1704         my($e) = shift;
1705
1706         my $args = $typefamily{$e->{DATA}->{TYPE}}->{PRINT_FN_ARGS}->($e);
1707
1708         pidl "void ndr_print_$e->{NAME}($args)";
1709         pidl "{";
1710         indent;
1711         $typefamily{$e->{DATA}->{TYPE}}->{PRINT_FN_BODY}->($e->{DATA}, $e->{NAME});
1712         deindent;
1713         pidl "}";
1714         pidl "";
1715 }
1716
1717 #####################################################################
1718 ## calculate the size of a structure
1719 sub ParseTypedefNdrSize($)
1720 {
1721         my($t) = shift;
1722
1723         my $tf = $typefamily{$t->{DATA}->{TYPE}};
1724         my $args = $tf->{SIZE_FN_ARGS}->($t);
1725
1726         pidl "size_t ndr_size_$t->{NAME}($args)";
1727         pidl "{";
1728         indent;
1729         $typefamily{$t->{DATA}->{TYPE}}->{SIZE_FN_BODY}->($t);
1730         deindent;
1731         pidl "}";
1732         pidl "";
1733 }
1734
1735 #####################################################################
1736 # parse a function - print side
1737 sub ParseFunctionPrint($)
1738 {
1739         my($fn) = shift;
1740
1741         return if has_property($fn, "noprint");
1742
1743         pidl "void ndr_print_$fn->{NAME}(struct ndr_print *ndr, const char *name, int flags, const struct $fn->{NAME} *r)";
1744         pidl "{";
1745         indent;
1746
1747         foreach my $e (@{$fn->{ELEMENTS}}) {
1748                 DeclareArrayVariables($e);
1749         }
1750
1751         pidl "ndr_print_struct(ndr, name, \"$fn->{NAME}\");";
1752         pidl "ndr->depth++;";
1753
1754         pidl "if (flags & NDR_SET_VALUES) {";
1755         pidl "\tndr->flags |= LIBNDR_PRINT_SET_VALUES;";
1756         pidl "}";
1757
1758         pidl "if (flags & NDR_IN) {";
1759         indent;
1760         pidl "ndr_print_struct(ndr, \"in\", \"$fn->{NAME}\");";
1761         pidl "ndr->depth++;";
1762
1763         my $env = GenerateFunctionInEnv($fn);
1764
1765         foreach my $e (@{$fn->{ELEMENTS}}) {
1766                 if (grep(/in/,@{$e->{DIRECTION}})) {
1767                         ParseElementPrint($e, "r->in.$e->{NAME}", $env);
1768                 }
1769         }
1770         pidl "ndr->depth--;";
1771         deindent;
1772         pidl "}";
1773         
1774         pidl "if (flags & NDR_OUT) {";
1775         indent;
1776         pidl "ndr_print_struct(ndr, \"out\", \"$fn->{NAME}\");";
1777         pidl "ndr->depth++;";
1778
1779         $env = GenerateFunctionOutEnv($fn);
1780         foreach my $e (@{$fn->{ELEMENTS}}) {
1781                 if (grep(/out/,@{$e->{DIRECTION}})) {
1782                         ParseElementPrint($e, "r->out.$e->{NAME}", $env);
1783                 }
1784         }
1785         if ($fn->{RETURN_TYPE}) {
1786                 pidl "ndr_print_$fn->{RETURN_TYPE}(ndr, \"result\", r->out.result);";
1787         }
1788         pidl "ndr->depth--;";
1789         deindent;
1790         pidl "}";
1791         
1792         pidl "ndr->depth--;";
1793         deindent;
1794         pidl "}";
1795         pidl "";
1796 }
1797
1798 #####################################################################
1799 # parse a function
1800 sub ParseFunctionPush($)
1801
1802         my($fn) = shift;
1803
1804         return if has_property($fn, "nopush");
1805
1806         pidl fn_prefix($fn) . "NTSTATUS ndr_push_$fn->{NAME}(struct ndr_push *ndr, int flags, const struct $fn->{NAME} *r)";
1807         pidl "{";
1808         indent;
1809
1810         foreach my $e (@{$fn->{ELEMENTS}}) { 
1811                 DeclareArrayVariables($e);
1812         }
1813
1814         pidl "if (flags & NDR_IN) {";
1815         indent;
1816
1817         my $env = GenerateFunctionInEnv($fn);
1818
1819         foreach my $e (@{$fn->{ELEMENTS}}) {
1820                 if (grep(/in/,@{$e->{DIRECTION}})) {
1821                         ParseElementPush($e, "ndr", "r->in.", $env, 1, 1);
1822                 }
1823         }
1824
1825         deindent;
1826         pidl "}";
1827
1828         pidl "if (flags & NDR_OUT) {";
1829         indent;
1830
1831         $env = GenerateFunctionOutEnv($fn);
1832         foreach my $e (@{$fn->{ELEMENTS}}) {
1833                 if (grep(/out/,@{$e->{DIRECTION}})) {
1834                         ParseElementPush($e, "ndr", "r->out.", $env, 1, 1);
1835                 }
1836         }
1837
1838         if ($fn->{RETURN_TYPE}) {
1839                 pidl "NDR_CHECK(ndr_push_$fn->{RETURN_TYPE}(ndr, NDR_SCALARS, r->out.result));";
1840         }
1841     
1842         deindent;
1843         pidl "}";
1844         pidl "return NT_STATUS_OK;";
1845         deindent;
1846         pidl "}";
1847         pidl "";
1848 }
1849
1850 sub AllocateArrayLevel($$$$$)
1851 {
1852         my ($e,$l,$ndr,$env,$size) = @_;
1853
1854         return if (has_property($e, "charset"));
1855
1856         my $var = ParseExpr($e->{NAME}, $env);
1857
1858         check_null_pointer($size);
1859         my $pl = GetPrevLevel($e, $l);
1860         if (defined($pl) and 
1861             $pl->{TYPE} eq "POINTER" and 
1862             $pl->{POINTER_TYPE} eq "ref"
1863                 and not $l->{IS_ZERO_TERMINATED}) {
1864             pidl "if (ndr->flags & LIBNDR_FLAG_REF_ALLOC) {";
1865             pidl "\tNDR_ALLOC_N($ndr, $var, $size);";
1866             pidl "}";
1867         } else {
1868                 pidl "NDR_ALLOC_N($ndr, $var, $size);";
1869         }
1870
1871         if (grep(/in/,@{$e->{DIRECTION}}) and
1872             grep(/out/,@{$e->{DIRECTION}})) {
1873                 pidl "memcpy(r->out.$e->{NAME},r->in.$e->{NAME},$size * sizeof(*r->in.$e->{NAME}));";
1874         }
1875 }
1876
1877 #####################################################################
1878 # parse a function
1879 sub ParseFunctionPull($)
1880
1881         my($fn) = shift;
1882
1883         return if has_property($fn, "nopull");
1884
1885         # pull function args
1886         pidl fn_prefix($fn) . "NTSTATUS ndr_pull_$fn->{NAME}(struct ndr_pull *ndr, int flags, struct $fn->{NAME} *r)";
1887         pidl "{";
1888         indent;
1889
1890         # declare any internal pointers we need
1891         foreach my $e (@{$fn->{ELEMENTS}}) { 
1892                 DeclarePtrVariables($e); 
1893                 DeclareArrayVariables($e);
1894         }
1895
1896         pidl "if (flags & NDR_IN) {";
1897         indent;
1898
1899         # auto-init the out section of a structure. I originally argued that
1900         # this was a bad idea as it hides bugs, but coping correctly
1901         # with initialisation and not wiping ref vars is turning
1902         # out to be too tricky (tridge)
1903         foreach my $e (@{$fn->{ELEMENTS}}) {
1904                 next unless grep(/out/, @{$e->{DIRECTION}});
1905                 pidl "ZERO_STRUCT(r->out);";
1906                 pidl "";
1907                 last;
1908         }
1909
1910         my $env = GenerateFunctionInEnv($fn);
1911
1912         foreach my $e (@{$fn->{ELEMENTS}}) {
1913                 next unless (grep(/in/, @{$e->{DIRECTION}}));
1914                 ParseElementPull($e, "ndr", "r->in.", $env, 1, 1);
1915         }
1916
1917         # allocate the "simple" out ref variables. FIXME: Shouldn't this have it's
1918         # own flag rather then be in NDR_IN ?
1919
1920         foreach my $e (@{$fn->{ELEMENTS}}) {
1921                 next unless (grep(/out/, @{$e->{DIRECTION}}));
1922                 next unless ($e->{LEVELS}[0]->{TYPE} eq "POINTER" and 
1923                              $e->{LEVELS}[0]->{POINTER_TYPE} eq "ref");
1924                 next if (($e->{LEVELS}[1]->{TYPE} eq "DATA") and 
1925                                  ($e->{LEVELS}[1]->{DATA_TYPE} eq "string"));
1926                 next if (($e->{LEVELS}[1]->{TYPE} eq "ARRAY") 
1927                         and   $e->{LEVELS}[1]->{IS_ZERO_TERMINATED});
1928
1929                 if ($e->{LEVELS}[1]->{TYPE} eq "ARRAY") {
1930                         my $size = ParseExpr($e->{LEVELS}[1]->{SIZE_IS}, $env);
1931                         check_null_pointer($size);
1932                         
1933                         pidl "NDR_ALLOC_N(ndr, r->out.$e->{NAME}, $size);";
1934
1935                         if (grep(/in/, @{$e->{DIRECTION}})) {
1936                                 pidl "memcpy(r->out.$e->{NAME}, r->in.$e->{NAME}, $size * sizeof(*r->in.$e->{NAME}));";
1937                         } else {
1938                                 pidl "memset(r->out.$e->{NAME}, 0, $size * sizeof(*r->out.$e->{NAME}));";
1939                         }
1940                 } else {
1941                         pidl "NDR_ALLOC(ndr, r->out.$e->{NAME});";
1942                 
1943                         if (grep(/in/, @{$e->{DIRECTION}})) {
1944                                 pidl "*r->out.$e->{NAME} = *r->in.$e->{NAME};";
1945                         } else {
1946                                 pidl "ZERO_STRUCTP(r->out.$e->{NAME});";
1947                         }
1948                 }
1949         }
1950
1951         deindent;
1952         pidl "}";
1953         
1954         pidl "if (flags & NDR_OUT) {";
1955         indent;
1956
1957         $env = GenerateFunctionOutEnv($fn);
1958         foreach my $e (@{$fn->{ELEMENTS}}) {
1959                 next unless grep(/out/, @{$e->{DIRECTION}});
1960                 ParseElementPull($e, "ndr", "r->out.", $env, 1, 1);
1961         }
1962
1963         if ($fn->{RETURN_TYPE}) {
1964                 pidl "NDR_CHECK(ndr_pull_$fn->{RETURN_TYPE}(ndr, NDR_SCALARS, &r->out.result));";
1965         }
1966
1967         deindent;
1968         pidl "}";
1969         pidl "return NT_STATUS_OK;";
1970         deindent;
1971         pidl "}";
1972         pidl "";
1973 }
1974
1975 #####################################################################
1976 # produce a function call table
1977 sub FunctionTable($)
1978 {
1979         my($interface) = shift;
1980         my $count = 0;
1981         my $uname = uc $interface->{NAME};
1982
1983         $count = $#{$interface->{FUNCTIONS}}+1;
1984
1985         return if ($count == 0);
1986
1987         pidl "static const struct dcerpc_interface_call $interface->{NAME}\_calls[] = {";
1988         $count = 0;
1989         foreach my $d (@{$interface->{FUNCTIONS}}) {
1990                 next if not defined($d->{OPNUM});
1991                 pidl "\t{";
1992                 pidl "\t\t\"$d->{NAME}\",";
1993                 pidl "\t\tsizeof(struct $d->{NAME}),";
1994                 pidl "\t\t(ndr_push_flags_fn_t) ndr_push_$d->{NAME},";
1995                 pidl "\t\t(ndr_pull_flags_fn_t) ndr_pull_$d->{NAME},";
1996                 pidl "\t\t(ndr_print_function_t) ndr_print_$d->{NAME}";
1997                 pidl "\t},";
1998                 $count++;
1999         }
2000         pidl "\t{ NULL, 0, NULL, NULL, NULL }";
2001         pidl "};";
2002         pidl "";
2003
2004         pidl "static const char * const $interface->{NAME}\_endpoint_strings[] = {";
2005         foreach my $ep (@{$interface->{ENDPOINTS}}) {
2006                 pidl "\t$ep, ";
2007         }
2008         my $endpoint_count = $#{$interface->{ENDPOINTS}}+1;
2009         
2010         pidl "};";
2011         pidl "";
2012
2013         pidl "static const struct dcerpc_endpoint_list $interface->{NAME}\_endpoints = {";
2014         pidl "\t.count\t= $endpoint_count,";
2015         pidl "\t.names\t= $interface->{NAME}\_endpoint_strings";
2016         pidl "};";
2017         pidl "";
2018
2019         if (! defined $interface->{PROPERTIES}->{authservice}) {
2020                 $interface->{PROPERTIES}->{authservice} = "\"host\"";
2021         }
2022
2023         my @a = split / /, $interface->{PROPERTIES}->{authservice};
2024         my $authservice_count = $#a + 1;
2025
2026         pidl "static const char * const $interface->{NAME}\_authservice_strings[] = {";
2027         foreach my $ap (@a) {
2028                 pidl "\t$ap, ";
2029         }
2030         pidl "};";
2031         pidl "";
2032
2033         pidl "static const struct dcerpc_authservice_list $interface->{NAME}\_authservices = {";
2034         pidl "\t.count\t= $endpoint_count,";
2035         pidl "\t.names\t= $interface->{NAME}\_authservice_strings";
2036         pidl "};";
2037         pidl "";
2038
2039         pidl "\nconst struct dcerpc_interface_table dcerpc_table_$interface->{NAME} = {";
2040         pidl "\t.name\t\t= \"$interface->{NAME}\",";
2041         pidl "\t.uuid\t\t= DCERPC_$uname\_UUID,";
2042         pidl "\t.if_version\t= DCERPC_$uname\_VERSION,";
2043         pidl "\t.helpstring\t= DCERPC_$uname\_HELPSTRING,";
2044         pidl "\t.num_calls\t= $count,";
2045         pidl "\t.calls\t\t= $interface->{NAME}\_calls,";
2046         pidl "\t.endpoints\t= &$interface->{NAME}\_endpoints,";
2047         pidl "\t.authservices\t= &$interface->{NAME}\_authservices";
2048         pidl "};";
2049         pidl "";
2050
2051         pidl "static NTSTATUS dcerpc_ndr_$interface->{NAME}_init(void)";
2052         pidl "{";
2053         pidl "\treturn librpc_register_interface(&dcerpc_table_$interface->{NAME});";
2054         pidl "}";
2055         pidl "";
2056 }
2057
2058 #####################################################################
2059 # parse the interface definitions
2060 sub ParseInterface($$)
2061 {
2062         my($interface,$needed) = @_;
2063
2064         # Typedefs
2065         foreach my $d (@{$interface->{TYPEDEFS}}) {
2066                 ($needed->{"push_$d->{NAME}"}) && ParseTypedefPush($d);
2067                 ($needed->{"pull_$d->{NAME}"}) && ParseTypedefPull($d);
2068                 ($needed->{"print_$d->{NAME}"}) && ParseTypedefPrint($d);
2069
2070                 # Make sure we don't generate a function twice...
2071                 $needed->{"push_$d->{NAME}"} = $needed->{"pull_$d->{NAME}"} = 
2072                         $needed->{"print_$d->{NAME}"} = 0;
2073
2074                 ($needed->{"ndr_size_$d->{NAME}"}) && ParseTypedefNdrSize($d);
2075         }
2076
2077         # Functions
2078         foreach my $d (@{$interface->{FUNCTIONS}}) {
2079                 ($needed->{"push_$d->{NAME}"}) && ParseFunctionPush($d);
2080                 ($needed->{"pull_$d->{NAME}"}) && ParseFunctionPull($d);
2081                 ($needed->{"print_$d->{NAME}"}) && ParseFunctionPrint($d);
2082
2083                 # Make sure we don't generate a function twice...
2084                 $needed->{"push_$d->{NAME}"} = $needed->{"pull_$d->{NAME}"} = 
2085                         $needed->{"print_$d->{NAME}"} = 0;
2086         }
2087
2088         FunctionTable($interface);
2089 }
2090
2091 sub RegistrationFunction($$)
2092 {
2093         my ($idl,$filename) = @_;
2094
2095         $filename =~ /.*\/ndr_(.*).c/;
2096         my $basename = $1;
2097         pidl "NTSTATUS dcerpc_$basename\_init(void)";
2098         pidl "{";
2099         indent;
2100         pidl "NTSTATUS status = NT_STATUS_OK;";
2101         foreach my $interface (@{$idl}) {
2102                 next if $interface->{TYPE} ne "INTERFACE";
2103
2104                 my $count = ($#{$interface->{FUNCTIONS}}+1);
2105
2106                 next if ($count == 0);
2107
2108                 pidl "status = dcerpc_ndr_$interface->{NAME}_init();";
2109                 pidl "if (NT_STATUS_IS_ERR(status)) {";
2110                 pidl "\treturn status;";
2111                 pidl "}";
2112                 pidl "";
2113         }
2114         pidl "return status;";
2115         deindent;
2116         pidl "}";
2117         pidl "";
2118 }
2119
2120 #####################################################################
2121 # parse a parsed IDL structure back into an IDL file
2122 sub Parse($$)
2123 {
2124         my($ndr,$filename) = @_;
2125
2126         $tabs = "";
2127         my $h_filename = $filename;
2128         $res = "";
2129
2130         if ($h_filename =~ /(.*)\.c/) {
2131                 $h_filename = "$1.h";
2132         }
2133
2134         pidl "/* parser auto-generated by pidl */";
2135         pidl "";
2136         pidl "#include \"includes.h\"";
2137         pidl "#include \"$h_filename\"";
2138         pidl "";
2139
2140         my %needed = ();
2141
2142         foreach my $x (@{$ndr}) {
2143                 ($x->{TYPE} eq "INTERFACE") && NeededInterface($x, \%needed);
2144         }
2145
2146         foreach my $x (@{$ndr}) {
2147                 ($x->{TYPE} eq "INTERFACE") && ParseInterface($x, \%needed);
2148         }
2149
2150         RegistrationFunction($ndr, $filename);
2151
2152         return $res;
2153 }
2154
2155 sub NeededFunction($$)
2156 {
2157         my ($fn,$needed) = @_;
2158         $needed->{"pull_$fn->{NAME}"} = 1;
2159         $needed->{"push_$fn->{NAME}"} = 1;
2160         $needed->{"print_$fn->{NAME}"} = 1;
2161         foreach my $e (@{$fn->{ELEMENTS}}) {
2162                 $e->{PARENT} = $fn;
2163                 unless(defined($needed->{"pull_$e->{TYPE}"})) {
2164                         $needed->{"pull_$e->{TYPE}"} = 1;
2165                 }
2166                 unless(defined($needed->{"push_$e->{TYPE}"})) {
2167                         $needed->{"push_$e->{TYPE}"} = 1;
2168                 }
2169                 unless(defined($needed->{"print_$e->{TYPE}"})) {
2170                         $needed->{"print_$e->{TYPE}"} = 1;
2171                 }
2172         }
2173 }
2174
2175 sub NeededTypedef($$)
2176 {
2177         my ($t,$needed) = @_;
2178         if (has_property($t, "public")) {
2179                 $needed->{"pull_$t->{NAME}"} = not has_property($t, "nopull");
2180                 $needed->{"push_$t->{NAME}"} = not has_property($t, "nopush");
2181                 $needed->{"print_$t->{NAME}"} = not has_property($t, "noprint");
2182         }
2183
2184         if ($t->{DATA}->{TYPE} eq "STRUCT" or $t->{DATA}->{TYPE} eq "UNION") {
2185                 if (has_property($t, "gensize")) {
2186                         $needed->{"ndr_size_$t->{NAME}"} = 1;
2187                 }
2188
2189                 for my $e (@{$t->{DATA}->{ELEMENTS}}) {
2190                         $e->{PARENT} = $t->{DATA};
2191                         if ($needed->{"pull_$t->{NAME}"} and
2192                                 not defined($needed->{"pull_$e->{TYPE}"})) {
2193                                 $needed->{"pull_$e->{TYPE}"} = 1;
2194                         }
2195                         if ($needed->{"push_$t->{NAME}"} and
2196                                 not defined($needed->{"push_$e->{TYPE}"})) {
2197                                 $needed->{"push_$e->{TYPE}"} = 1;
2198                         }
2199                         if ($needed->{"print_$t->{NAME}"} and 
2200                                 not defined($needed->{"print_$e->{TYPE}"})) {
2201                                 $needed->{"print_$e->{TYPE}"} = 1;
2202                         }
2203                 }
2204         }
2205 }
2206
2207 #####################################################################
2208 # work out what parse functions are needed
2209 sub NeededInterface($$)
2210 {
2211         my ($interface,$needed) = @_;
2212         foreach my $d (@{$interface->{FUNCTIONS}}) {
2213             NeededFunction($d, $needed);
2214         }
2215         foreach my $d (reverse @{$interface->{TYPEDEFS}}) {
2216             NeededTypedef($d, $needed);
2217         }
2218 }
2219
2220 1;