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