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