* make pidl.pl exit with an error on a parse error
[samba.git] / source4 / build / pidl / parser.pm
1 ###################################################
2 # Samba4 parser generator for IDL structures
3 # Copyright tridge@samba.org 2000-2003
4 # Copyright tpot@samba.org 2001
5 # released under the GNU GPL
6
7 package IdlParser;
8
9 use Data::Dumper;
10
11 my($res);
12
13 # the list of needed functions
14 my %needed;
15
16 #####################################################################
17 # parse a properties list
18 sub ParseProperties($)
19 {
20     my($props) = shift;
21     foreach my $d (@{$props}) {
22         if (ref($d) ne "HASH") {
23             $res .= "[$d] ";
24         } else {
25             foreach my $k (keys %{$d}) {
26                 $res .= "[$k($d->{$k})] ";
27             }
28         }
29     }
30 }
31
32 ####################################################################
33 # work out the name of a size_is() variable
34 sub find_size_var($$)
35 {
36         my($e) = shift;
37         my($size) = shift;
38         my($fn) = $e->{PARENT};
39
40         if (util::is_constant($size)) {
41                 return $size;
42         }
43         
44         if ($fn->{TYPE} ne "FUNCTION") {
45                 return "r->$size";
46         }
47
48         for my $e2 (@{$fn->{DATA}}) {
49                 if ($e2->{NAME} eq $size) {
50                         if (util::has_property($e2, "in")) {
51                                 return "r->in.$size";
52                         }
53                         if (util::has_property($e2, "out")) {
54                                 return "r->out.$size";
55                         }
56                 }
57         }
58         die "invalid variable in $size for element $e->{NAME} in $fn->{NAME}\n";
59 }
60
61
62 #####################################################################
63 # work out the correct alignment for a structure
64 sub struct_alignment($)
65 {
66         my $s = shift;
67         # why do we need a minimum alignment of 4 ?? 
68         my $align = 4;
69         for my $e (@{$s->{ELEMENTS}}) {
70                 if ($align < util::type_align($e)) {
71                         $align = util::type_align($e);
72                 }
73         }
74         return $align;
75 }
76
77 #####################################################################
78 # parse an array - push side
79 sub ParseArrayPush($$$)
80 {
81         my $e = shift;
82         my $var_prefix = shift;
83         my $ndr_flags = shift;
84
85         my $size = find_size_var($e, util::array_size($e));
86
87         if (defined $e->{CONFORMANT_SIZE}) {
88                 # the conformant size has already been pushed
89         } elsif (!util::is_constant($size)) {
90                 # we need to emit the array size
91                 $res .= "\t\tNDR_CHECK(ndr_push_uint32(ndr, $size));\n";
92         }
93
94         if (util::is_scalar_type($e->{TYPE})) {
95                 $res .= "\t\tNDR_CHECK(ndr_push_array_$e->{TYPE}(ndr, $var_prefix$e->{NAME}, $size));\n";
96         } else {
97                 $res .= "\t\tNDR_CHECK(ndr_push_array(ndr, ndr_flags, $var_prefix$e->{NAME}, sizeof($var_prefix$e->{NAME}\[0]), $size, (ndr_push_flags_fn_t)ndr_push_$e->{TYPE}));\n";
98         }
99 }
100
101 #####################################################################
102 # print an array
103 sub ParseArrayPrint($$)
104 {
105         my $e = shift;
106         my $var_prefix = shift;
107         my $size = find_size_var($e, util::array_size($e));
108
109         if (util::is_scalar_type($e->{TYPE})) {
110                 $res .= "\t\tndr_print_array_$e->{TYPE}(ndr, \"$e->{NAME}\", $var_prefix$e->{NAME}, $size);\n";
111         } else {
112                 $res .= "\t\tndr_print_array(ndr, \"$e->{NAME}\", $var_prefix$e->{NAME}, sizeof($var_prefix$e->{NAME}\[0]), $size, (ndr_print_fn_t)ndr_print_$e->{TYPE});\n";
113         }
114 }
115
116 #####################################################################
117 # parse an array - pull side
118 sub ParseArrayPull($$$)
119 {
120         my $e = shift;
121         my $var_prefix = shift;
122         my $ndr_flags = shift;
123
124         my $size = find_size_var($e, util::array_size($e));
125         my $alloc_size = $size;
126
127         # if this is a conformant array then we use that size to allocate, and make sure
128         # we allocate enough to pull the elements
129         if (defined $e->{CONFORMANT_SIZE}) {
130                 $alloc_size = $e->{CONFORMANT_SIZE};
131
132                 $res .= "\tif ($size > $alloc_size) {\n";
133                 $res .= "\t\treturn ndr_pull_error(ndr, NDR_ERR_CONFORMANT_SIZE, \"Bad conformant size %u should be %u\", $alloc_size, $size);\n";
134                 $res .= "\t}\n";
135         } elsif (!util::is_constant($size)) {
136                 # non fixed arrays encode the size just before the array
137                 $res .= "\t{\n";
138                 $res .= "\t\tuint32 _array_size;\n";
139                 $res .= "\t\tNDR_CHECK(ndr_pull_uint32(ndr, &_array_size));\n";
140                 $res .= "\t\tif ($size > _array_size) {\n";
141                 $res .= "\t\t\treturn ndr_pull_error(ndr, NDR_ERR_ARRAY_SIZE, \"Bad array size %u should be %u\", _array_size, $size);\n";
142                 $res .= "\t\t}\n";
143                 $res .= "\t}\n";
144         }
145
146         if (util::need_alloc($e) && !util::is_constant($size)) {
147                 $res .= "\t\tNDR_ALLOC_N_SIZE(ndr, $var_prefix$e->{NAME}, $alloc_size, sizeof($var_prefix$e->{NAME}\[0]));\n";
148         }
149
150         if (util::has_property($e, "length_is")) {
151                 die "we don't handle varying arrays yet";
152         }
153
154         if (util::is_scalar_type($e->{TYPE})) {
155                 $res .= "\t\tNDR_CHECK(ndr_pull_array_$e->{TYPE}(ndr, $var_prefix$e->{NAME}, $size));\n";
156         } else {
157                 $res .= "\t\tNDR_CHECK(ndr_pull_array(ndr, $ndr_flags, (void **)$var_prefix$e->{NAME}, sizeof($var_prefix$e->{NAME}\[0]), $size, (ndr_pull_flags_fn_t)ndr_pull_$e->{TYPE}));\n";
158         }
159 }
160
161
162 #####################################################################
163 # parse scalars in a structure element
164 sub ParseElementPushScalar($$$)
165 {
166         my($e) = shift;
167         my($var_prefix) = shift;
168         my($ndr_flags) = shift;
169         my $cprefix = util::c_push_prefix($e);
170
171         if (defined $e->{VALUE}) {
172                 $res .= "\tNDR_CHECK(ndr_push_$e->{TYPE}(ndr, $e->{VALUE}));\n";
173         } elsif (util::need_wire_pointer($e)) {
174                 $res .= "\tNDR_CHECK(ndr_push_ptr(ndr, $var_prefix$e->{NAME}));\n";
175         } elsif (my $switch = util::has_property($e, "switch_is")) {
176                 ParseElementPushSwitch($e, $var_prefix, $ndr_flags, $switch);
177         } elsif (util::is_builtin_type($e->{TYPE})) {
178                 $res .= "\tNDR_CHECK(ndr_push_$e->{TYPE}(ndr, $cprefix$var_prefix$e->{NAME}));\n";
179         } else {
180                 $res .= "\tNDR_CHECK(ndr_push_$e->{TYPE}(ndr, $ndr_flags, $cprefix$var_prefix$e->{NAME}));\n";
181         }
182 }
183
184 #####################################################################
185 # print scalars in a structure element
186 sub ParseElementPrintScalar($$)
187 {
188         my($e) = shift;
189         my($var_prefix) = shift;
190         my $cprefix = util::c_push_prefix($e);
191
192         if (util::has_property($e, "struct_len")) {
193                 return;
194         }
195
196         if (defined $e->{VALUE}) {
197                 $res .= "\tndr_print_$e->{TYPE}(ndr, \"$e->{NAME}\", $e->{VALUE});\n";
198         } elsif (util::has_direct_buffers($e)) {
199                 $res .= "\tndr_print_ptr(ndr, \"$e->{NAME}\", $var_prefix$e->{NAME});\n";
200                 $res .= "\tndr->depth++;\n";
201                 ParseElementPrintBuffer($e, "r->");
202                 $res .= "\tndr->depth--;\n";
203         } elsif (my $switch = util::has_property($e, "switch_is")) {
204                 ParseElementPrintSwitch($e, $var_prefix, $switch);
205         } else {
206                 $res .= "\tndr_print_$e->{TYPE}(ndr, \"$e->{NAME}\", $cprefix$var_prefix$e->{NAME});\n";
207         }
208 }
209
210 #####################################################################
211 # parse scalars in a structure element - pull size
212 sub ParseElementPullSwitch($$$$)
213 {
214         my($e) = shift;
215         my($var_prefix) = shift;
216         my($ndr_flags) = shift;
217         my $switch = shift;
218         my $switch_var = find_size_var($e, $switch);
219
220         my $cprefix = util::c_pull_prefix($e);
221
222         $res .= "\t{ uint16 _level;\n";
223         $res .= "\tNDR_CHECK(ndr_pull_$e->{TYPE}(ndr, $ndr_flags, &_level, $cprefix$var_prefix$e->{NAME}));\n";
224         $res .= "\tif ((($ndr_flags) & NDR_SCALARS) && (_level != $switch_var)) return ndr_pull_error(ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value %u in $e->{NAME}\");\n";
225         $res .= "\t}\n";
226 }
227
228 #####################################################################
229 # push switch element
230 sub ParseElementPushSwitch($$$$)
231 {
232         my($e) = shift;
233         my($var_prefix) = shift;
234         my($ndr_flags) = shift;
235         my $switch = shift;
236         my $switch_var = find_size_var($e, $switch);
237         my $cprefix = util::c_push_prefix($e);
238
239         $res .= "\tNDR_CHECK(ndr_push_$e->{TYPE}(ndr, $ndr_flags, $switch_var, $cprefix$var_prefix$e->{NAME}));\n";
240 }
241
242 #####################################################################
243 # print scalars in a structure element 
244 sub ParseElementPrintSwitch($$$)
245 {
246         my($e) = shift;
247         my($var_prefix) = shift;
248         my $switch = shift;
249         my $switch_var = find_size_var($e, $switch);
250         my $cprefix = util::c_push_prefix($e);
251
252         $res .= "\tndr_print_$e->{TYPE}(ndr, \"$e->{NAME}\", $switch_var, $cprefix$var_prefix$e->{NAME});\n";
253 }
254
255
256 #####################################################################
257 # parse scalars in a structure element - pull size
258 sub ParseElementPullScalar($$$)
259 {
260         my($e) = shift;
261         my($var_prefix) = shift;
262         my($ndr_flags) = shift;
263         my $cprefix = util::c_pull_prefix($e);
264
265         if (defined $e->{VALUE}) {
266                 $res .= "\tNDR_CHECK(ndr_pull_$e->{TYPE}(ndr, $e->{VALUE}));\n";
267         } elsif (util::need_wire_pointer($e)) {
268                 $res .= "\tNDR_CHECK(ndr_pull_uint32(ndr, &_ptr_$e->{NAME}));\n";
269                 $res .= "\tif (_ptr_$e->{NAME}) {\n";
270                 $res .= "\t\tNDR_ALLOC(ndr, $var_prefix$e->{NAME});\n";
271                 $res .= "\t} else {\n";
272                 $res .= "\t\t$var_prefix$e->{NAME} = NULL;\n";
273                 $res .= "\t}\n";
274         } elsif (util::need_alloc($e)) {
275                 # no scalar component
276         } elsif (my $switch = util::has_property($e, "switch_is")) {
277                 ParseElementPullSwitch($e, $var_prefix, $ndr_flags, $switch);
278         } elsif (util::is_builtin_type($e->{TYPE})) {
279                 $res .= "\tNDR_CHECK(ndr_pull_$e->{TYPE}(ndr, $cprefix$var_prefix$e->{NAME}));\n";
280         } else {
281                 $res .= "\tNDR_CHECK(ndr_pull_$e->{TYPE}(ndr, $ndr_flags, $cprefix$var_prefix$e->{NAME}));\n";
282         }
283 }
284
285 #####################################################################
286 # parse buffers in a structure element
287 sub ParseElementPushBuffer($$$)
288 {
289         my($e) = shift;
290         my($var_prefix) = shift;
291         my($ndr_flags) = shift;
292         my $cprefix = util::c_push_prefix($e);
293
294         if (util::is_pure_scalar($e)) {
295                 return;
296         }
297
298         if (util::need_wire_pointer($e)) {
299                 $res .= "\tif ($var_prefix$e->{NAME}) {\n";
300         }
301             
302         if (util::array_size($e)) {
303                 ParseArrayPush($e, "r->", "NDR_SCALARS|NDR_BUFFERS");
304         } elsif (my $switch = util::has_property($e, "switch_is")) {
305                 if ($e->{POINTERS}) {
306                         ParseElementPushSwitch($e, $var_prefix, "NDR_BUFFERS|NDR_SCALARS", $switch);
307                 } else {
308                         ParseElementPushSwitch($e, $var_prefix, "NDR_BUFFERS", $switch);
309                 }
310         } elsif (util::is_builtin_type($e->{TYPE})) {
311                 $res .= "\t\tNDR_CHECK(ndr_push_$e->{TYPE}(ndr, $cprefix$var_prefix$e->{NAME}));\n";
312         } elsif ($e->{POINTERS}) {
313                 $res .= "\t\tNDR_CHECK(ndr_push_$e->{TYPE}(ndr, NDR_SCALARS|NDR_BUFFERS, $cprefix$var_prefix$e->{NAME}));\n";
314         } else {
315                 $res .= "\t\tNDR_CHECK(ndr_push_$e->{TYPE}(ndr, $ndr_flags, $cprefix$var_prefix$e->{NAME}));\n";
316         }
317
318         if (util::need_wire_pointer($e)) {
319                 $res .= "\t}\n";
320         }       
321 }
322
323 #####################################################################
324 # print buffers in a structure element
325 sub ParseElementPrintBuffer($$)
326 {
327         my($e) = shift;
328         my($var_prefix) = shift;
329         my $cprefix = util::c_push_prefix($e);
330
331         if (util::is_pure_scalar($e)) {
332                 return;
333         }
334
335         if (util::need_wire_pointer($e)) {
336                 $res .= "\tif ($var_prefix$e->{NAME}) {\n";
337         }
338             
339         if (util::array_size($e)) {
340                 ParseArrayPrint($e, "r->");
341         } elsif (my $switch = util::has_property($e, "switch_is")) {
342                 ParseElementPrintSwitch($e, $var_prefix, $switch);
343         } else {
344                 $res .= "\t\tndr_print_$e->{TYPE}(ndr, \"$e->{NAME}\", $cprefix$var_prefix$e->{NAME});\n";
345         }
346
347         if (util::need_wire_pointer($e)) {
348                 $res .= "\t}\n";
349         }       
350 }
351
352
353 #####################################################################
354 # parse buffers in a structure element - pull side
355 sub ParseElementPullBuffer($$$)
356 {
357         my($e) = shift;
358         my($var_prefix) = shift;
359         my($ndr_flags) = shift;
360         my $cprefix = util::c_pull_prefix($e);
361
362         if (util::is_pure_scalar($e)) {
363                 return;
364         }
365
366         if (util::need_wire_pointer($e)) {
367                 $res .= "\tif ($var_prefix$e->{NAME}) {\n";
368         }
369             
370         if (util::array_size($e)) {
371                 ParseArrayPull($e, "r->", "NDR_SCALARS|NDR_BUFFERS");
372         } elsif (my $switch = util::has_property($e, "switch_is")) {
373                 if ($e->{POINTERS}) {
374                         ParseElementPullSwitch($e, $var_prefix, "NDR_SCALARS|NDR_BUFFERS", $switch);
375                 } else {
376                         ParseElementPullSwitch($e, $var_prefix, "NDR_BUFFERS", $switch);
377                 }
378         } elsif (util::is_builtin_type($e->{TYPE})) {
379                 $res .= "\t\tNDR_CHECK(ndr_pull_$e->{TYPE}(ndr, $cprefix$var_prefix$e->{NAME}));\n";
380         } elsif ($e->{POINTERS}) {
381                 $res .= "\t\tNDR_CHECK(ndr_pull_$e->{TYPE}(ndr, NDR_SCALARS|NDR_BUFFERS, $cprefix$var_prefix$e->{NAME}));\n";
382         } else {
383                 $res .= "\t\tNDR_CHECK(ndr_pull_$e->{TYPE}(ndr, $ndr_flags, $cprefix$var_prefix$e->{NAME}));\n";
384         }
385
386         if (util::need_wire_pointer($e)) {
387                 $res .= "\t}\n";
388         }       
389 }
390
391 #####################################################################
392 # parse a struct
393 sub ParseStructPush($)
394 {
395         my($struct) = shift;
396         my($struct_len);
397         my $conform_e;
398
399         if (! defined $struct->{ELEMENTS}) {
400                 return;
401         }
402
403         # see if we have a structure length
404         foreach my $e (@{$struct->{ELEMENTS}}) {
405                 $e->{PARENT} = $struct;
406                 if (util::has_property($e, "struct_len")) {
407                         $struct_len = $e;
408                         $e->{VALUE} = "0";
409                 }
410         }       
411
412         if (defined $struct_len) {
413                 $res .= "\tstruct ndr_push_save _save1, _save2, _save3;\n";
414                 $res .= "\tndr_push_save(ndr, &_save1);\n";
415         }
416
417         # see if the structure contains a conformant array. If it
418         # does, then it must be the last element of the structure, and
419         # we need to push the conformant length early, as it fits on
420         # the wire before the structure (and even before the structure
421         # alignment)
422         my $e = $struct->{ELEMENTS}[-1];
423         if (defined $e->{ARRAY_LEN} && $e->{ARRAY_LEN} eq "*") {
424                 my $size = find_size_var($e, util::array_size($e));
425                 $e->{CONFORMANT_SIZE} = $size;
426                 $conform_e = $e;
427                 $res .= "\tNDR_CHECK(ndr_push_uint32(ndr, $size));\n";
428         }
429
430         my $align = struct_alignment($struct);
431         $res .= "\tNDR_CHECK(ndr_push_align(ndr, $align));\n";
432
433         $res .= "\tif (!(ndr_flags & NDR_SCALARS)) goto buffers;\n";
434
435         foreach my $e (@{$struct->{ELEMENTS}}) {
436                 if (defined($struct_len) && $e == $struct_len) {
437                         $res .= "\tNDR_CHECK(ndr_push_align(ndr, sizeof($e->{TYPE})));\n";
438                         $res .= "\tndr_push_save(ndr, &_save2);\n";
439                 }
440                 ParseElementPushScalar($e, "r->", "NDR_SCALARS");
441         }       
442
443         $res .= "buffers:\n";
444         $res .= "\tif (!(ndr_flags & NDR_BUFFERS)) goto done;\n";
445         foreach my $e (@{$struct->{ELEMENTS}}) {
446                 ParseElementPushBuffer($e, "r->", "NDR_BUFFERS");
447         }
448
449         if (defined $struct_len) {
450                 $res .= "\tndr_push_save(ndr, &_save3);\n";
451                 $res .= "\tndr_push_restore(ndr, &_save2);\n";
452                 $struct_len->{VALUE} = "_save3.offset - _save1.offset";
453                 ParseElementPushScalar($struct_len, "r->", "NDR_SCALARS");
454                 $res .= "\tndr_push_restore(ndr, &_save3);\n";
455         }
456
457         $res .= "done:\n";
458 }
459
460 #####################################################################
461 # generate a struct print function
462 sub ParseStructPrint($)
463 {
464         my($struct) = shift;
465
466         if (! defined $struct->{ELEMENTS}) {
467                 return;
468         }
469
470         $res .= "\tndr->depth++;\n";
471         foreach my $e (@{$struct->{ELEMENTS}}) {
472                 ParseElementPrintScalar($e, "r->");
473         }
474         $res .= "\tndr->depth--;\n";
475 }
476
477 #####################################################################
478 # parse a struct - pull side
479 sub ParseStructPull($)
480 {
481         my($struct) = shift;
482         my($struct_len);
483         my $conform_e;
484
485         if (! defined $struct->{ELEMENTS}) {
486                 return;
487         }
488
489         # see if the structure contains a conformant array. If it
490         # does, then it must be the last element of the structure, and
491         # we need to pull the conformant length early, as it fits on
492         # the wire before the structure (and even before the structure
493         # alignment)
494         my $e = $struct->{ELEMENTS}[-1];
495         if (defined $e->{ARRAY_LEN} && $e->{ARRAY_LEN} eq "*") {
496                 $conform_e = $e;
497                 $res .= "\tuint32 _conformant_size;\n";
498                 $conform_e->{CONFORMANT_SIZE} = "_conformant_size";
499         }
500
501         # declare any internal pointers we need
502         foreach my $e (@{$struct->{ELEMENTS}}) {
503                 $e->{PARENT} = $struct;
504                 if (util::need_wire_pointer($e)) {
505                         $res .= "\tuint32 _ptr_$e->{NAME};\n";
506                 }
507         }
508
509
510         # see if we have a structure length. If we do then we need to advance
511         # the ndr_pull offset to that length past the front of the structure
512         # when we have finished with the structure
513         # we also need to make sure that we limit the size of our parsing
514         # of this structure to the given size
515         foreach my $e (@{$struct->{ELEMENTS}}) {
516                 if (util::has_property($e, "struct_len")) {
517                         $struct_len = $e;
518                         $e->{VALUE} = "&_size";
519                 }
520         }       
521
522         if (defined $struct_len) {
523                 $res .= "\tuint32 _size;\n";
524                 $res .= "\tstruct ndr_pull_save _save;\n";
525                 $res .= "\tndr_pull_save(ndr, &_save);\n";
526         }
527
528         if (defined $conform_e) {
529                 $res .= "\tNDR_CHECK(ndr_pull_uint32(ndr, &$conform_e->{CONFORMANT_SIZE}));\n";
530         }
531
532         my $align = struct_alignment($struct);
533         $res .= "\tNDR_CHECK(ndr_pull_align(ndr, $align));\n";
534
535         $res .= "\tif (!(ndr_flags & NDR_SCALARS)) goto buffers;\n";
536         foreach my $e (@{$struct->{ELEMENTS}}) {
537                 ParseElementPullScalar($e, "r->", "NDR_SCALARS");
538                 if (defined($struct_len) && $e == $struct_len) {
539                         $res .= "\tNDR_CHECK(ndr_pull_limit_size(ndr, _size, 4));\n";
540                 }
541         }       
542
543         $res .= "buffers:\n";
544         $res .= "\tif (!(ndr_flags & NDR_BUFFERS)) goto done;\n";
545         foreach my $e (@{$struct->{ELEMENTS}}) {
546                 ParseElementPullBuffer($e, "r->", "NDR_BUFFERS");
547         }
548
549         if (defined $struct_len) {
550                 $res .= "\tndr_pull_restore(ndr, &_save);\n";
551                 $res .= "\tNDR_CHECK(ndr_pull_advance(ndr, _size));\n";
552         }
553
554         $res .= "done:\n";
555 }
556
557
558 #####################################################################
559 # parse a union - push side
560 sub ParseUnionPush($)
561 {
562         my $e = shift;
563         $res .= "\tif (!(ndr_flags & NDR_SCALARS)) goto buffers;\n";
564         $res .= "\tNDR_CHECK(ndr_push_uint16(ndr, level));\n";
565         $res .= "\tswitch (level) {\n";
566         foreach my $el (@{$e->{DATA}}) {
567                 $res .= "\tcase $el->{CASE}:\n";
568                 ParseElementPushScalar($el->{DATA}, "r->", "NDR_SCALARS");              
569                 $res .= "\tbreak;\n\n";
570         }
571         $res .= "\tdefault:\n";
572         $res .= "\t\treturn ndr_push_error(ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value \%u\", level);\n";
573         $res .= "\t}\n";
574         $res .= "buffers:\n";
575         $res .= "\tif (!(ndr_flags & NDR_BUFFERS)) goto done;\n";
576         $res .= "\tswitch (level) {\n";
577         foreach my $el (@{$e->{DATA}}) {
578                 $res .= "\tcase $el->{CASE}:\n";
579                 ParseElementPushBuffer($el->{DATA}, "r->", "ndr_flags");
580                 $res .= "\tbreak;\n\n";
581         }
582         $res .= "\tdefault:\n";
583         $res .= "\t\treturn ndr_push_error(ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value \%u\", level);\n";
584         $res .= "\t}\n";
585         $res .= "done:\n";
586 }
587
588 #####################################################################
589 # print a union
590 sub ParseUnionPrint($)
591 {
592         my $e = shift;
593
594         $res .= "\tswitch (level) {\n";
595         foreach my $el (@{$e->{DATA}}) {
596                 $res .= "\tcase $el->{CASE}:\n";
597                 ParseElementPrintScalar($el->{DATA}, "r->");
598                 $res .= "\tbreak;\n\n";
599         }
600         $res .= "\tdefault:\n\t\tndr_print_bad_level(ndr, name, level);\n";
601         $res .= "\t}\n";
602 }
603
604 #####################################################################
605 # parse a union - pull side
606 sub ParseUnionPull($)
607 {
608         my $e = shift;
609
610         $res .= "\tif (!(ndr_flags & NDR_SCALARS)) goto buffers;\n";
611         $res .= "\tNDR_CHECK(ndr_pull_uint16(ndr, level));\n";
612         $res .= "\tswitch (*level) {\n";
613         foreach my $el (@{$e->{DATA}}) {
614                 $res .= "\tcase $el->{CASE}: {\n";
615                 my $e2 = $el->{DATA};
616                 if ($e2->{POINTERS}) {
617                         $res .= "\t\tuint32 _ptr_$e2->{NAME};\n";
618                 }
619                 ParseElementPullScalar($el->{DATA}, "r->", "NDR_SCALARS");              
620                 $res .= "\tbreak; }\n\n";
621         }
622         $res .= "\tdefault:\n";
623         $res .= "\t\treturn ndr_pull_error(ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value \%u\", *level);\n";
624         $res .= "\t}\n";
625         $res .= "buffers:\n";
626         $res .= "\tif (!(ndr_flags & NDR_BUFFERS)) goto done;\n";
627         $res .= "\tswitch (*level) {\n";
628         foreach my $el (@{$e->{DATA}}) {
629                 $res .= "\tcase $el->{CASE}:\n";
630                 ParseElementPullBuffer($el->{DATA}, "r->", "NDR_BUFFERS");
631                 $res .= "\tbreak;\n\n";
632         }
633         $res .= "\tdefault:\n";
634         $res .= "\t\treturn ndr_pull_error(ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value \%u\", *level);\n";
635         $res .= "\t}\n";
636         $res .= "done:\n";
637 }
638
639 #####################################################################
640 # parse a type
641 sub ParseTypePush($)
642 {
643         my($data) = shift;
644
645         if (ref($data) eq "HASH") {
646                 ($data->{TYPE} eq "STRUCT") &&
647                     ParseStructPush($data);
648                 ($data->{TYPE} eq "UNION") &&
649                     ParseUnionPush($data);
650         }
651 }
652
653 #####################################################################
654 # generate a print function for a type
655 sub ParseTypePrint($)
656 {
657         my($data) = shift;
658
659         if (ref($data) eq "HASH") {
660                 ($data->{TYPE} eq "STRUCT") &&
661                     ParseStructPrint($data);
662                 ($data->{TYPE} eq "UNION") &&
663                     ParseUnionPrint($data);
664         }
665 }
666
667 #####################################################################
668 # parse a type
669 sub ParseTypePull($)
670 {
671         my($data) = shift;
672
673         if (ref($data) eq "HASH") {
674                 ($data->{TYPE} eq "STRUCT") &&
675                     ParseStructPull($data);
676                 ($data->{TYPE} eq "UNION") &&
677                     ParseUnionPull($data);
678         }
679 }
680
681 #####################################################################
682 # parse a typedef - push side
683 sub ParseTypedefPush($)
684 {
685         my($e) = shift;
686
687         if (! $needed{"push_$e->{NAME}"}) {
688 #               print "push_$e->{NAME} not needed\n";
689                 return;
690         }
691
692         if ($e->{DATA}->{TYPE} eq "STRUCT") {
693                 $res .= "static NTSTATUS ndr_push_$e->{NAME}(struct ndr_push *ndr, int ndr_flags, struct $e->{NAME} *r)";
694                 $res .= "\n{\n";
695                 ParseTypePush($e->{DATA});
696                 $res .= "\treturn NT_STATUS_OK;\n";
697                 $res .= "}\n\n";
698         }
699
700         if ($e->{DATA}->{TYPE} eq "UNION") {
701                 $res .= "static NTSTATUS ndr_push_$e->{NAME}(struct ndr_push *ndr, int ndr_flags, uint16 level, union $e->{NAME} *r)";
702                 $res .= "\n{\n";
703                 ParseTypePush($e->{DATA});
704                 $res .= "\treturn NT_STATUS_OK;\n";
705                 $res .= "}\n\n";
706         }
707 }
708
709
710 #####################################################################
711 # parse a typedef - pull side
712 sub ParseTypedefPull($)
713 {
714         my($e) = shift;
715
716         if (! $needed{"pull_$e->{NAME}"}) {
717 #               print "pull_$e->{NAME} not needed\n";
718                 return;
719         }
720
721         if ($e->{DATA}->{TYPE} eq "STRUCT") {
722                 $res .= "static NTSTATUS ndr_pull_$e->{NAME}(struct ndr_pull *ndr, int ndr_flags, struct $e->{NAME} *r)";
723                 $res .= "\n{\n";
724                 ParseTypePull($e->{DATA});
725                 $res .= "\treturn NT_STATUS_OK;\n";
726                 $res .= "}\n\n";
727         }
728
729         if ($e->{DATA}->{TYPE} eq "UNION") {
730                 $res .= "static NTSTATUS ndr_pull_$e->{NAME}(struct ndr_pull *ndr, int ndr_flags, uint16 *level, union $e->{NAME} *r)";
731                 $res .= "\n{\n";
732                 ParseTypePull($e->{DATA});
733                 $res .= "\treturn NT_STATUS_OK;\n";
734                 $res .= "}\n\n";
735         }
736 }
737
738
739 #####################################################################
740 # parse a typedef - push side
741 sub ParseTypedefPrint($)
742 {
743         my($e) = shift;
744
745         if ($e->{DATA}->{TYPE} eq "STRUCT") {
746                 $res .= "void ndr_print_$e->{NAME}(struct ndr_print *ndr, const char *name, struct $e->{NAME} *r)";
747                 $res .= "\n{\n";
748                 $res .= "\tndr_print_struct(ndr, name, \"$e->{NAME}\");\n";
749                 ParseTypePrint($e->{DATA});
750                 $res .= "}\n\n";
751         }
752
753         if ($e->{DATA}->{TYPE} eq "UNION") {
754                 $res .= "void ndr_print_$e->{NAME}(struct ndr_print *ndr, const char *name, uint16 level, union $e->{NAME} *r)";
755                 $res .= "\n{\n";
756                 $res .= "\tndr_print_union(ndr, name, level, \"$e->{NAME}\");\n";
757                 ParseTypePrint($e->{DATA});
758                 $res .= "}\n\n";
759         }
760 }
761
762
763 #####################################################################
764 # parse a function
765 sub ParseFunctionPush($)
766
767         my($function) = shift;
768
769         # Input function
770         $res .= "NTSTATUS ndr_push_$function->{NAME}(struct ndr_push *ndr, struct $function->{NAME} *r)\n{\n";
771
772         foreach my $e (@{$function->{DATA}}) {
773                 if (util::has_property($e, "in")) {
774                         $e->{PARENT} = $function;
775                         if (util::array_size($e)) {
776                                 $res .= "\tif (r->in.$e->{NAME}) {\n";
777                                 if (!util::is_scalar_type($e->{TYPE})) {
778                                         $res .= "\t\tint ndr_flags = NDR_SCALARS|NDR_BUFFERS;\n";
779                                 }
780                                 ParseArrayPush($e, "r->in.", "ndr_flags");
781                                 $res .= "\t}\n";
782                         } else {
783                                 ParseElementPushScalar($e, "r->in.", "NDR_SCALARS|NDR_BUFFERS");
784                                 ParseElementPushBuffer($e, "r->in.", "NDR_SCALARS|NDR_BUFFERS");
785                         }
786                 }
787         }
788     
789         $res .= "\n\treturn NT_STATUS_OK;\n}\n\n";
790 }
791
792 #####################################################################
793 # parse a function
794 sub ParseFunctionPull($)
795
796         my($fn) = shift;
797
798         # pull function args
799         $res .= "NTSTATUS ndr_pull_$fn->{NAME}(struct ndr_pull *ndr, struct $fn->{NAME} *r)\n{\n";
800
801         # declare any internal pointers we need
802         foreach my $e (@{$fn->{DATA}}) {
803                 if (util::has_property($e, "out")) {
804                         if (util::need_wire_pointer($e)) {
805                                 $res .= "\tuint32 _ptr_$e->{NAME};\n";
806                         }
807                 }
808         }
809
810         foreach my $e (@{$fn->{DATA}}) {
811                 if (util::has_property($e, "out")) {
812                         $e->{PARENT} = $fn;
813                         if (util::array_size($e)) {
814                                 $res .= "\tif (r->out.$e->{NAME}) {\n";
815                                 if (!util::is_scalar_type($e->{TYPE})) {
816                                         $res .= "\t\tint ndr_flags = NDR_SCALARS|NDR_BUFFERS;\n";
817                                 }
818                                 ParseArrayPull($e, "r->out.", "ndr_flags");
819                                 $res .= "\t}\n";
820                         } else {
821                                 ParseElementPullScalar($e, "r->out.", "NDR_SCALARS|NDR_BUFFERS");
822                                 if ($e->{POINTERS}) {
823                                         ParseElementPullBuffer($e, "r->out.", "NDR_SCALARS|NDR_BUFFERS");
824                                 }
825                         }
826                 }
827         }
828
829         if ($fn->{RETURN_TYPE} && $fn->{RETURN_TYPE} ne "void") {
830                 $res .= "\tNDR_CHECK(ndr_pull_$fn->{RETURN_TYPE}(ndr, &r->out.result));\n";
831         }
832
833     
834         $res .= "\n\treturn NT_STATUS_OK;\n}\n\n";
835 }
836
837 #####################################################################
838 # parse the interface definitions
839 sub ParseInterface($)
840 {
841         my($interface) = shift;
842         my($data) = $interface->{DATA};
843         foreach my $d (@{$data}) {
844                 ($d->{TYPE} eq "TYPEDEF") &&
845                     ParseTypedefPush($d);
846                 ($d->{TYPE} eq "FUNCTION") && 
847                     ParseFunctionPush($d);
848         }
849         foreach my $d (@{$data}) {
850                 ($d->{TYPE} eq "TYPEDEF") &&
851                     ParseTypedefPull($d);
852                 ($d->{TYPE} eq "FUNCTION") && 
853                     ParseFunctionPull($d);
854         }
855         foreach my $d (@{$data}) {
856                 ($d->{TYPE} eq "TYPEDEF") &&
857                     ParseTypedefPrint($d);
858         }
859 }
860
861 sub NeededFunction($)
862 {
863         my $fn = shift;
864         $needed{"pull_$fn->{NAME}"} = 1;
865         $needed{"push_$fn->{NAME}"} = 1;
866         foreach my $e (@{$fn->{DATA}}) {
867                 if (util::has_property($e, "out")) {
868                         $needed{"pull_$e->{TYPE}"} = 1;
869                 }
870                 if (util::has_property($e, "in")) {
871                         $needed{"push_$e->{TYPE}"} = 1;
872                 }
873         }
874 }
875
876 sub NeededTypedef($)
877 {
878         my $t = shift;
879         if ($t->{DATA}->{TYPE} eq "STRUCT") {
880                 for my $e (@{$t->{DATA}->{ELEMENTS}}) {
881                                 if ($needed{"pull_$t->{NAME}"}) {
882                                         $needed{"pull_$e->{TYPE}"} = 1;
883                                 }
884                                 if ($needed{"push_$t->{NAME}"}) {
885                                         $needed{"push_$e->{TYPE}"} = 1;
886                                 }
887                         }
888                 }
889         if ($t->{DATA}->{TYPE} eq "UNION") {
890                 for my $e (@{$t->{DATA}->{DATA}}) {
891                         if ($needed{"pull_$t->{NAME}"}) {
892                                 $needed{"pull_$e->{DATA}->{TYPE}"} = 1;
893                         }
894                         if ($needed{"push_$t->{NAME}"}) {
895                                 $needed{"push_$e->{DATA}->{TYPE}"} = 1;
896                         }
897                 }
898         }
899 }
900
901 #####################################################################
902 # work out what parse functions are needed
903 sub BuildNeeded($)
904 {
905         my($interface) = shift;
906         my($data) = $interface->{DATA};
907         foreach my $d (@{$data}) {
908                 ($d->{TYPE} eq "FUNCTION") && 
909                     NeededFunction($d);
910         }
911         foreach my $d (reverse @{$data}) {
912                 ($d->{TYPE} eq "TYPEDEF") &&
913                     NeededTypedef($d);
914         }
915 }
916
917
918 #####################################################################
919 # parse a parsed IDL structure back into an IDL file
920 sub Parse($)
921 {
922         my($idl) = shift;
923         $res = "/* parser auto-generated by pidl */\n\n";
924         $res .= "#include \"includes.h\"\n\n";
925         foreach my $x (@{$idl}) {
926                 if ($x->{TYPE} eq "INTERFACE") { 
927                         BuildNeeded($x);
928                         ParseInterface($x);
929                 }
930         }
931         return $res;
932 }
933
934 1;