d91c324b536ebaf030b56eaded9e5de14eefeadb
[metze/samba/wip.git] / pidl / lib / Parse / Pidl / NDR.pm
1 ###################################################
2 # Samba4 NDR info tree generator
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 =pod
9
10 =head1 NAME
11
12 Parse::Pidl::NDR - NDR parsing information generator
13
14 =head1 DESCRIPTION
15
16 Return a table describing the order in which the parts of an element
17 should be parsed
18 Possible level types:
19  - POINTER
20  - ARRAY
21  - SUBCONTEXT
22  - SWITCH
23  - DATA
24
25 =head1 AUTHOR
26
27 Jelmer Vernooij <jelmer@samba.org>
28
29 =cut
30
31 package Parse::Pidl::NDR;
32
33 require Exporter;
34 use vars qw($VERSION);
35 $VERSION = '0.01';
36 @ISA = qw(Exporter);
37 @EXPORT = qw(GetPrevLevel GetNextLevel ContainsDeferred ContainsPipe ContainsString);
38 @EXPORT_OK = qw(GetElementLevelTable ParseElement ValidElement align_type mapToScalar ParseType can_contain_deferred is_charset_array);
39
40 use strict;
41 use Parse::Pidl qw(warning fatal);
42 use Parse::Pidl::Typelist qw(hasType getType typeIs expandAlias mapScalarType is_fixed_size_scalar);
43 use Parse::Pidl::Util qw(has_property property_matches);
44
45 # Alignment of the built-in scalar types
46 my $scalar_alignment = {
47         'void' => 0,
48         'char' => 1,
49         'int8' => 1,
50         'uint8' => 1,
51         'int16' => 2,
52         'uint16' => 2,
53         'int1632' => 3,
54         'uint1632' => 3,
55         'int32' => 4,
56         'uint32' => 4,
57         'int3264' => 5,
58         'uint3264' => 5,
59         'hyper' => 8,
60         'double' => 8,
61         'pointer' => 8,
62         'dlong' => 4,
63         'udlong' => 4,
64         'udlongr' => 4,
65         'DATA_BLOB' => 4,
66         'string' => 4,
67         'string_array' => 4, #???
68         'time_t' => 4,
69         'uid_t' => 8,
70         'gid_t' => 8,
71         'NTTIME' => 4,
72         'NTTIME_1sec' => 4,
73         'NTTIME_hyper' => 8,
74         'WERROR' => 4,
75         'NTSTATUS' => 4,
76         'COMRESULT' => 4,
77         'dns_string' => 4,
78         'nbt_string' => 4,
79         'wrepl_nbt_name' => 4,
80         'ipv4address' => 4,
81         'ipv6address' => 4, #16?
82         'dnsp_name' => 1,
83         'dnsp_string' => 1
84 };
85
86 sub GetElementLevelTable($$$)
87 {
88         my ($e, $pointer_default, $ms_union) = @_;
89
90         my $order = [];
91         my $is_deferred = 0;
92         my @bracket_array = ();
93         my @length_is = ();
94         my @size_is = ();
95         my $pointer_idx = 0;
96
97         if (has_property($e, "size_is")) {
98                 @size_is = split /,/, has_property($e, "size_is");
99         }
100
101         if (has_property($e, "length_is")) {
102                 @length_is = split /,/, has_property($e, "length_is");
103         }
104
105         if (defined($e->{ARRAY_LEN})) {
106                 @bracket_array = @{$e->{ARRAY_LEN}};
107         }
108
109         if (has_property($e, "out")) {
110                 my $needptrs = 1;
111
112                 if (has_property($e, "string") and not has_property($e, "in")) { $needptrs++; }
113                 if ($#bracket_array >= 0) { $needptrs = 0; }
114
115                 warning($e, "[out] argument `$e->{NAME}' not a pointer") if ($needptrs > $e->{POINTERS});
116         }
117
118         my $allow_pipe = ($e->{PARENT}->{TYPE} eq "FUNCTION");
119         my $is_pipe = typeIs($e->{TYPE}, "PIPE");
120
121         if ($is_pipe) {
122                 if (not $allow_pipe) {
123                         fatal($e, "argument `$e->{NAME}' is a pipe and not allowed on $e->{PARENT}->{TYPE}");
124                 }
125
126                 if ($e->{POINTERS} > 1) {
127                         fatal($e, "$e->{POINTERS} are not allowed on pipe element $e->{NAME}");
128                 }
129
130                 if ($e->{POINTERS} < 0) {
131                         fatal($e, "pipe element $e->{NAME} needs pointer");
132                 }
133
134                 if ($e->{POINTERS} == 1 and pointer_type($e) ne "ref") {
135                         fatal($e, "pointer should be 'ref' on pipe element $e->{NAME}");
136                 }
137
138                 if (scalar(@size_is) > 0) {
139                         fatal($e, "size_is() on pipe element");
140                 }
141
142                 if (scalar(@length_is) > 0) {
143                         fatal($e, "length_is() on pipe element");
144                 }
145
146                 if (scalar(@bracket_array) > 0) {
147                         fatal($e, "brackets on pipe element");
148                 }
149
150                 if (defined(has_property($e, "subcontext"))) {
151                         fatal($e, "subcontext on pipe element");
152                 }
153
154                 if (has_property($e, "switch_is")) {
155                         fatal($e, "switch_is on pipe element");
156                 }
157
158                 if (can_contain_deferred($e->{TYPE})) {
159                         fatal($e, "$e->{TYPE} can_contain_deferred - not allowed on pipe element");
160                 }
161         }
162
163         # Parse the [][][][] style array stuff
164         for my $i (0 .. $#bracket_array) {
165                 my $d = $bracket_array[$#bracket_array - $i];
166                 my $size = $d;
167                 my $length = $d;
168                 my $is_surrounding = 0;
169                 my $is_varying = 0;
170                 my $is_conformant = 0;
171                 my $is_string = 0;
172                 my $is_fixed = 0;
173                 my $is_inline = 0;
174
175                 if ($d eq "*") {
176                         $is_conformant = 1;
177                         if ($size = shift @size_is) {
178                                 if ($e->{POINTERS} < 1 and has_property($e, "string")) {
179                                         $is_string = 1;
180                                         delete($e->{PROPERTIES}->{string});
181                                 }
182                         } elsif ((scalar(@size_is) == 0) and has_property($e, "string")) {
183                                 $is_string = 1;
184                                 delete($e->{PROPERTIES}->{string});
185                         } else {
186                                 fatal($e, "Must specify size_is() for conformant array!")
187                         }
188
189                         if (($length = shift @length_is) or $is_string) {
190                                 $is_varying = 1;
191                         } else {
192                                 $length = $size;
193                         }
194
195                         if ($e == $e->{PARENT}->{ELEMENTS}[-1] 
196                                 and $e->{PARENT}->{TYPE} ne "FUNCTION") {
197                                 $is_surrounding = 1;
198                         }
199                 }
200
201                 $is_fixed = 1 if (not $is_conformant and Parse::Pidl::Util::is_constant($size));
202                 $is_inline = 1 if (not $is_conformant and not Parse::Pidl::Util::is_constant($size));
203
204                 if ($i == 0 and $is_fixed and has_property($e, "string")) {
205                         $is_fixed = 0;
206                         $is_varying = 1;
207                         $is_string = 1;
208                         delete($e->{PROPERTIES}->{string});
209                 }
210
211                 push (@$order, {
212                         TYPE => "ARRAY",
213                         SIZE_IS => $size,
214                         LENGTH_IS => $length,
215                         IS_DEFERRED => $is_deferred,
216                         IS_SURROUNDING => $is_surrounding,
217                         IS_ZERO_TERMINATED => $is_string,
218                         IS_VARYING => $is_varying,
219                         IS_CONFORMANT => $is_conformant,
220                         IS_FIXED => $is_fixed,
221                         IS_INLINE => $is_inline
222                 });
223         }
224
225         # Next, all the pointers
226         foreach my $i (1..$e->{POINTERS}) {
227                 my $level = "EMBEDDED";
228                 # Top level "ref" pointers do not have a referrent identifier
229                 $level = "TOP" if ($i == 1 and $e->{PARENT}->{TYPE} eq "FUNCTION");
230
231                 my $pt;
232                 #
233                 # Only the first level gets the pointer type from the
234                 # pointer property, the others get them from
235                 # the pointer_default() interface property
236                 #
237                 # see http://msdn2.microsoft.com/en-us/library/aa378984(VS.85).aspx
238                 # (Here they talk about the rightmost pointer, but testing shows
239                 #  they mean the leftmost pointer.)
240                 #
241                 # --metze
242                 #
243                 $pt = pointer_type($e);
244                 if ($i > 1) {
245                         $is_deferred = 1 if ($pt ne "ref" and $e->{PARENT}->{TYPE} eq "FUNCTION");
246                         $pt = $pointer_default;
247                 }
248
249                 push (@$order, { 
250                         TYPE => "POINTER",
251                         POINTER_TYPE => $pt,
252                         POINTER_INDEX => $pointer_idx,
253                         IS_DEFERRED => "$is_deferred",
254                         LEVEL => $level
255                 });
256
257                 warning($e, "top-level \[out\] pointer `$e->{NAME}' is not a \[ref\] pointer") 
258                         if ($i == 1 and $pt ne "ref" and
259                                 $e->{PARENT}->{TYPE} eq "FUNCTION" and 
260                                 not has_property($e, "in"));
261
262                 $pointer_idx++;
263                 
264                 # everything that follows will be deferred
265                 $is_deferred = 1 if ($level ne "TOP");
266
267                 my $array_size = shift @size_is;
268                 my $array_length;
269                 my $is_varying;
270                 my $is_conformant;
271                 my $is_string = 0;
272                 if ($array_size) {
273                         $is_conformant = 1;
274                         if ($array_length = shift @length_is) {
275                                 $is_varying = 1;
276                         } else {
277                                 $array_length = $array_size;
278                                 $is_varying =0;
279                         }
280                 } 
281                 
282                 if (scalar(@size_is) == 0 and has_property($e, "string") and 
283                     $i == $e->{POINTERS}) {
284                         $is_string = 1;
285                         $is_varying = $is_conformant = has_property($e, "noheader")?0:1;
286                         delete($e->{PROPERTIES}->{string});
287                 }
288
289                 if ($array_size or $is_string) {
290                         push (@$order, {
291                                 TYPE => "ARRAY",
292                                 SIZE_IS => $array_size,
293                                 LENGTH_IS => $array_length,
294                                 IS_DEFERRED => $is_deferred,
295                                 IS_SURROUNDING => 0,
296                                 IS_ZERO_TERMINATED => $is_string,
297                                 IS_VARYING => $is_varying,
298                                 IS_CONFORMANT => $is_conformant,
299                                 IS_FIXED => 0,
300                                 IS_INLINE => 0
301                         });
302
303                         $is_deferred = 0;
304                 } 
305         }
306
307         if ($is_pipe) {
308                 push (@$order, {
309                         TYPE => "PIPE",
310                         IS_DEFERRED => 0,
311                         CONTAINS_DEFERRED => 0,
312                 });
313
314                 my $i = 0;
315                 foreach (@$order) { $_->{LEVEL_INDEX} = $i; $i+=1; }
316
317                 return $order;
318         }
319
320         if (defined(has_property($e, "subcontext"))) {
321                 my $hdr_size = has_property($e, "subcontext");
322                 my $subsize = has_property($e, "subcontext_size");
323                 if (not defined($subsize)) { 
324                         $subsize = -1; 
325                 }
326                 
327                 push (@$order, {
328                         TYPE => "SUBCONTEXT",
329                         HEADER_SIZE => $hdr_size,
330                         SUBCONTEXT_SIZE => $subsize,
331                         IS_DEFERRED => $is_deferred,
332                         COMPRESSION => has_property($e, "compression"),
333                 });
334         }
335
336         if (my $switch = has_property($e, "switch_is")) {
337                 push (@$order, {
338                         TYPE => "SWITCH", 
339                         SWITCH_IS => $switch,
340                         IS_DEFERRED => $is_deferred
341                 });
342         }
343
344         if (scalar(@size_is) > 0) {
345                 fatal($e, "size_is() on non-array element");
346         }
347
348         if (scalar(@length_is) > 0) {
349                 fatal($e, "length_is() on non-array element");
350         }
351
352         if (has_property($e, "string")) {
353                 fatal($e, "string() attribute on non-array element");
354         }
355
356         push (@$order, {
357                 TYPE => "DATA",
358                 DATA_TYPE => $e->{TYPE},
359                 IS_DEFERRED => $is_deferred,
360                 CONTAINS_DEFERRED => can_contain_deferred($e->{TYPE}),
361                 IS_SURROUNDING => 0 #FIXME
362         });
363
364         my $i = 0;
365         foreach (@$order) { $_->{LEVEL_INDEX} = $i; $i+=1; }
366
367         return $order;
368 }
369
370 sub GetTypedefLevelTable($$$$)
371 {
372         my ($e, $data, $pointer_default, $ms_union) = @_;
373
374         my $order = [];
375
376         push (@$order, {
377                 TYPE => "TYPEDEF"
378         });
379
380         my $i = 0;
381         foreach (@$order) { $_->{LEVEL_INDEX} = $i; $i+=1; }
382
383         return $order;
384 }
385
386 #####################################################################
387 # see if a type contains any deferred data 
388 sub can_contain_deferred($)
389 {
390         sub can_contain_deferred($);
391         my ($type) = @_;
392
393         return 1 unless (hasType($type)); # assume the worst
394
395         $type = getType($type);
396
397         return 0 if (Parse::Pidl::Typelist::is_scalar($type));
398
399         return can_contain_deferred($type->{DATA}) if ($type->{TYPE} eq "TYPEDEF");
400
401         return 0 unless defined($type->{ELEMENTS});
402
403         foreach (@{$type->{ELEMENTS}}) {
404                 return 1 if ($_->{POINTERS});
405                 return 1 if (can_contain_deferred ($_->{TYPE}));
406         }
407         
408         return 0;
409 }
410
411 sub pointer_type($)
412 {
413         my $e = shift;
414
415         return undef unless $e->{POINTERS};
416         
417         return "ref" if (has_property($e, "ref"));
418         return "full" if (has_property($e, "ptr"));
419         return "sptr" if (has_property($e, "sptr"));
420         return "unique" if (has_property($e, "unique"));
421         return "relative" if (has_property($e, "relative"));
422         return "relative_short" if (has_property($e, "relative_short"));
423         return "ignore" if (has_property($e, "ignore"));
424
425         return undef;
426 }
427
428 #####################################################################
429 # work out the correct alignment for a structure or union
430 sub find_largest_alignment($)
431 {
432         my $s = shift;
433
434         my $align = 1;
435         for my $e (@{$s->{ELEMENTS}}) {
436                 my $a = 1;
437
438                 if ($e->{POINTERS}) {
439                         # this is a hack for NDR64
440                         # the NDR layer translates this into
441                         # an alignment of 4 for NDR and 8 for NDR64
442                         $a = 5;
443                 } elsif (has_property($e, "subcontext")) { 
444                         $a = 1;
445                 } elsif (has_property($e, "transmit_as")) {
446                         $a = align_type($e->{PROPERTIES}->{transmit_as});
447                 } else {
448                         $a = align_type($e->{TYPE}); 
449                 }
450
451                 $align = $a if ($align < $a);
452         }
453
454         return $align;
455 }
456
457 #####################################################################
458 # align a type
459 sub align_type($)
460 {
461         sub align_type($);
462         my ($e) = @_;
463
464         if (ref($e) eq "HASH" and $e->{TYPE} eq "SCALAR") {
465                 return $scalar_alignment->{$e->{NAME}};
466         }
467
468         return 0 if ($e eq "EMPTY");
469
470         unless (hasType($e)) {
471             # it must be an external type - all we can do is guess 
472                 # warning($e, "assuming alignment of unknown type '$e' is 4");
473             return 4;
474         }
475
476         my $dt = getType($e);
477
478         if ($dt->{TYPE} eq "TYPEDEF") {
479                 return align_type($dt->{DATA});
480         } elsif ($dt->{TYPE} eq "CONFORMANCE") {
481                 return $dt->{DATA}->{ALIGN};
482         } elsif ($dt->{TYPE} eq "ENUM") {
483                 return align_type(Parse::Pidl::Typelist::enum_type_fn($dt));
484         } elsif ($dt->{TYPE} eq "BITMAP") {
485                 return align_type(Parse::Pidl::Typelist::bitmap_type_fn($dt));
486         } elsif (($dt->{TYPE} eq "STRUCT") or ($dt->{TYPE} eq "UNION")) {
487                 # Struct/union without body: assume 4
488                 return 4 unless (defined($dt->{ELEMENTS}));
489                 return find_largest_alignment($dt);
490         } elsif (($dt->{TYPE} eq "PIPE")) {
491                 return 5;
492         }
493
494         die("Unknown data type type $dt->{TYPE}");
495 }
496
497 sub ParseElement($$$)
498 {
499         my ($e, $pointer_default, $ms_union) = @_;
500
501         $e->{TYPE} = expandAlias($e->{TYPE});
502
503         if (ref($e->{TYPE}) eq "HASH") {
504                 $e->{TYPE} = ParseType($e->{TYPE}, $pointer_default, $ms_union);
505         }
506
507         return {
508                 NAME => $e->{NAME},
509                 TYPE => $e->{TYPE},
510                 PROPERTIES => $e->{PROPERTIES},
511                 LEVELS => GetElementLevelTable($e, $pointer_default, $ms_union),
512                 REPRESENTATION_TYPE => ($e->{PROPERTIES}->{represent_as} or $e->{TYPE}),
513                 ALIGN => align_type($e->{TYPE}),
514                 ORIGINAL => $e
515         };
516 }
517
518 sub ParseStruct($$$)
519 {
520         my ($struct, $pointer_default, $ms_union) = @_;
521         my @elements = ();
522         my $surrounding = undef;
523
524         return {
525                 TYPE => "STRUCT",
526                 NAME => $struct->{NAME},
527                 SURROUNDING_ELEMENT => undef,
528                 ELEMENTS => undef,
529                 PROPERTIES => $struct->{PROPERTIES},
530                 ORIGINAL => $struct,
531                 ALIGN => undef
532         } unless defined($struct->{ELEMENTS});
533
534         CheckPointerTypes($struct, $pointer_default);
535
536         foreach my $x (@{$struct->{ELEMENTS}}) 
537         {
538                 my $e = ParseElement($x, $pointer_default, $ms_union);
539                 if ($x != $struct->{ELEMENTS}[-1] and 
540                         $e->{LEVELS}[0]->{IS_SURROUNDING}) {
541                         fatal($x, "conformant member not at end of struct");
542                 }
543                 push @elements, $e;
544         }
545
546         my $e = $elements[-1];
547         if (defined($e) and defined($e->{LEVELS}[0]->{IS_SURROUNDING}) and
548                 $e->{LEVELS}[0]->{IS_SURROUNDING}) {
549                 $surrounding = $e;
550         }
551
552         if (defined $e->{TYPE} && $e->{TYPE} eq "string"
553             &&  property_matches($e, "flag", ".*LIBNDR_FLAG_STR_CONFORMANT.*")) {
554                 $surrounding = $struct->{ELEMENTS}[-1];
555         }
556
557         my $align = undef;
558         if ($struct->{NAME}) {
559                 $align = align_type($struct->{NAME});
560         }
561                 
562         return {
563                 TYPE => "STRUCT",
564                 NAME => $struct->{NAME},
565                 SURROUNDING_ELEMENT => $surrounding,
566                 ELEMENTS => \@elements,
567                 PROPERTIES => $struct->{PROPERTIES},
568                 ORIGINAL => $struct,
569                 ALIGN => $align
570         };
571 }
572
573 sub ParseUnion($$)
574 {
575         my ($e, $pointer_default, $ms_union) = @_;
576         my @elements = ();
577         my $is_ms_union = $ms_union;
578         $is_ms_union = 1 if has_property($e, "ms_union");
579         my $hasdefault = 0;
580         my $switch_type = has_property($e, "switch_type");
581         unless (defined($switch_type)) { $switch_type = "uint32"; }
582         if (has_property($e, "nodiscriminant")) { $switch_type = undef; }
583
584         return {
585                 TYPE => "UNION",
586                 NAME => $e->{NAME},
587                 SWITCH_TYPE => $switch_type,
588                 ELEMENTS => undef,
589                 PROPERTIES => $e->{PROPERTIES},
590                 HAS_DEFAULT => $hasdefault,
591                 IS_MS_UNION => $is_ms_union,
592                 ORIGINAL => $e,
593                 ALIGN => undef
594         } unless defined($e->{ELEMENTS});
595
596         CheckPointerTypes($e, $pointer_default);
597
598         foreach my $x (@{$e->{ELEMENTS}}) 
599         {
600                 my $t;
601                 if ($x->{TYPE} eq "EMPTY") {
602                         $t = { TYPE => "EMPTY" };
603                 } else {
604                         $t = ParseElement($x, $pointer_default, $ms_union);
605                 }
606                 if (has_property($x, "default")) {
607                         $t->{CASE} = "default";
608                         $hasdefault = 1;
609                 } elsif (defined($x->{PROPERTIES}->{case})) {
610                         $t->{CASE} = "case $x->{PROPERTIES}->{case}";
611                 } else {
612                         die("Union element $x->{NAME} has neither default nor case property");
613                 }
614                 push @elements, $t;
615         }
616
617         my $align = undef;
618         if ($e->{NAME}) {
619                 $align = align_type($e->{NAME});
620         }
621
622         return {
623                 TYPE => "UNION",
624                 NAME => $e->{NAME},
625                 SWITCH_TYPE => $switch_type,
626                 ELEMENTS => \@elements,
627                 PROPERTIES => $e->{PROPERTIES},
628                 HAS_DEFAULT => $hasdefault,
629                 IS_MS_UNION => $is_ms_union,
630                 ORIGINAL => $e,
631                 ALIGN => $align
632         };
633 }
634
635 sub ParseEnum($$)
636 {
637         my ($e, $pointer_default, $ms_union) = @_;
638
639         return {
640                 TYPE => "ENUM",
641                 NAME => $e->{NAME},
642                 BASE_TYPE => Parse::Pidl::Typelist::enum_type_fn($e),
643                 ELEMENTS => $e->{ELEMENTS},
644                 PROPERTIES => $e->{PROPERTIES},
645                 ORIGINAL => $e
646         };
647 }
648
649 sub ParseBitmap($$$)
650 {
651         my ($e, $pointer_default, $ms_union) = @_;
652
653         return {
654                 TYPE => "BITMAP",
655                 NAME => $e->{NAME},
656                 BASE_TYPE => Parse::Pidl::Typelist::bitmap_type_fn($e),
657                 ELEMENTS => $e->{ELEMENTS},
658                 PROPERTIES => $e->{PROPERTIES},
659                 ORIGINAL => $e
660         };
661 }
662
663 sub ParsePipe($$$)
664 {
665         my ($pipe, $pointer_default, $ms_union) = @_;
666
667         my $pname = $pipe->{NAME};
668         $pname = $pipe->{PARENT}->{NAME} unless defined $pname;
669
670         if (not defined($pipe->{PROPERTIES})
671             and defined($pipe->{PARENT}->{PROPERTIES})) {
672                 $pipe->{PROPERTIES} = $pipe->{PARENT}->{PROPERTIES};
673         }
674
675         if (ref($pipe->{DATA}) eq "HASH") {
676                 if (not defined($pipe->{DATA}->{PROPERTIES})
677                     and defined($pipe->{PROPERTIES})) {
678                         $pipe->{DATA}->{PROPERTIES} = $pipe->{PROPERTIES};
679                 }
680         }
681
682         my $struct = ParseStruct($pipe->{DATA}, $pointer_default, $ms_union);
683         $struct->{ALIGN} = 5;
684         $struct->{NAME} = "$pname\_chunk";
685
686         # 'count' is element [0] and 'array' [1]
687         my $e = $struct->{ELEMENTS}[1];
688         # level [0] is of type "ARRAY"
689         my $l = $e->{LEVELS}[1];
690
691         # here we check that pipe elements have a fixed size type
692         while (defined($l)) {
693                 my $cl = $l;
694                 $l = GetNextLevel($e, $cl);
695                 if ($cl->{TYPE} ne "DATA") {
696                         fatal($pipe, el_name($pipe) . ": pipe contains non DATA level");
697                 }
698
699                 # for now we only support scalars
700                 next if is_fixed_size_scalar($cl->{DATA_TYPE});
701
702                 fatal($pipe, el_name($pipe) . ": pipe contains non fixed size type[$cl->{DATA_TYPE}]");
703         }
704
705         return {
706                 TYPE => "PIPE",
707                 NAME => $pipe->{NAME},
708                 DATA => $struct,
709                 PROPERTIES => $pipe->{PROPERTIES},
710                 ORIGINAL => $pipe,
711         };
712 }
713
714 sub ParseType($$$)
715 {
716         my ($d, $pointer_default, $ms_union) = @_;
717
718         my $data = {
719                 STRUCT => \&ParseStruct,
720                 UNION => \&ParseUnion,
721                 ENUM => \&ParseEnum,
722                 BITMAP => \&ParseBitmap,
723                 TYPEDEF => \&ParseTypedef,
724                 PIPE => \&ParsePipe,
725         }->{$d->{TYPE}}->($d, $pointer_default, $ms_union);
726
727         return $data;
728 }
729
730 sub ParseTypedef($$)
731 {
732         my ($d, $pointer_default, $ms_union) = @_;
733
734         my $data;
735
736         if (ref($d->{DATA}) eq "HASH") {
737                 if (defined($d->{DATA}->{PROPERTIES})
738                     and not defined($d->{PROPERTIES})) {
739                         $d->{PROPERTIES} = $d->{DATA}->{PROPERTIES};
740                 }
741
742                 $data = ParseType($d->{DATA}, $pointer_default, $ms_union);
743                 $data->{ALIGN} = align_type($d->{NAME});
744         } else {
745                 $data = getType($d->{DATA});
746         }
747
748         return {
749                 NAME => $d->{NAME},
750                 TYPE => $d->{TYPE},
751                 PROPERTIES => $d->{PROPERTIES},
752                 LEVELS => GetTypedefLevelTable($d, $data, $pointer_default, $ms_union),
753                 DATA => $data,
754                 ORIGINAL => $d
755         };
756 }
757
758 sub ParseConst($$)
759 {
760         my ($ndr,$d) = @_;
761
762         return $d;
763 }
764
765 sub ParseFunction($$$$)
766 {
767         my ($ndr,$d,$opnum,$ms_union) = @_;
768         my @elements = ();
769         my $rettype = undef;
770         my $thisopnum = undef;
771
772         CheckPointerTypes($d, "ref");
773
774         if (not defined($d->{PROPERTIES}{noopnum})) {
775                 $thisopnum = ${$opnum};
776                 ${$opnum}++;
777         }
778
779         foreach my $x (@{$d->{ELEMENTS}}) {
780                 my $e = ParseElement($x, $ndr->{PROPERTIES}->{pointer_default}, $ms_union);
781                 push (@{$e->{DIRECTION}}, "in") if (has_property($x, "in"));
782                 push (@{$e->{DIRECTION}}, "out") if (has_property($x, "out"));
783
784                 push (@elements, $e);
785         }
786
787         if ($d->{RETURN_TYPE} ne "void") {
788                 $rettype = expandAlias($d->{RETURN_TYPE});
789         }
790         
791         return {
792                         NAME => $d->{NAME},
793                         TYPE => "FUNCTION",
794                         OPNUM => $thisopnum,
795                         RETURN_TYPE => $rettype,
796                         PROPERTIES => $d->{PROPERTIES},
797                         ELEMENTS => \@elements,
798                         ORIGINAL => $d
799                 };
800 }
801
802 sub CheckPointerTypes($$)
803 {
804         my ($s,$default) = @_;
805
806         return unless defined($s->{ELEMENTS});
807
808         foreach my $e (@{$s->{ELEMENTS}}) {
809                 if ($e->{POINTERS} and not defined(pointer_type($e))) {
810                         $e->{PROPERTIES}->{$default} = '1';
811                 }
812         }
813 }
814
815 sub FindNestedTypes($$)
816 {
817         sub FindNestedTypes($$);
818         my ($l, $t) = @_;
819
820         return unless defined($t->{ELEMENTS});
821         return if ($t->{TYPE} eq "ENUM");
822         return if ($t->{TYPE} eq "BITMAP");
823
824         foreach (@{$t->{ELEMENTS}}) {
825                 if (ref($_->{TYPE}) eq "HASH") {
826                         push (@$l, $_->{TYPE}) if (defined($_->{TYPE}->{NAME}));
827                         FindNestedTypes($l, $_->{TYPE});
828                 }
829         }
830 }
831
832 sub ParseInterface($)
833 {
834         my $idl = shift;
835         my @types = ();
836         my @consts = ();
837         my @functions = ();
838         my @endpoints;
839         my $opnum = 0;
840         my $version;
841         my $ms_union = 0;
842         $ms_union = 1 if has_property($idl, "ms_union");
843
844         if (not has_property($idl, "pointer_default")) {
845                 # MIDL defaults to "ptr" in DCE compatible mode (/osf)
846                 # and "unique" in Microsoft Extensions mode (default)
847                 $idl->{PROPERTIES}->{pointer_default} = "unique";
848         }
849
850         foreach my $d (@{$idl->{DATA}}) {
851                 if ($d->{TYPE} eq "FUNCTION") {
852                         push (@functions, ParseFunction($idl, $d, \$opnum, $ms_union));
853                 } elsif ($d->{TYPE} eq "CONST") {
854                         push (@consts, ParseConst($idl, $d));
855                 } else {
856                         push (@types, ParseType($d, $idl->{PROPERTIES}->{pointer_default}, $ms_union));
857                         FindNestedTypes(\@types, $d);
858                 }
859         }
860
861         $version = "0.0";
862
863         if(defined $idl->{PROPERTIES}->{version}) { 
864                 my @if_version = split(/\./, $idl->{PROPERTIES}->{version});
865                 if ($if_version[0] == $idl->{PROPERTIES}->{version}) {
866                                 $version = $idl->{PROPERTIES}->{version};
867                 } else {
868                                 $version = $if_version[1] << 16 | $if_version[0];
869                 }
870         }
871
872         # If no endpoint is set, default to the interface name as a named pipe
873         if (!defined $idl->{PROPERTIES}->{endpoint}) {
874                 push @endpoints, "\"ncacn_np:[\\\\pipe\\\\" . $idl->{NAME} . "]\"";
875         } else {
876                 @endpoints = split /,/, $idl->{PROPERTIES}->{endpoint};
877         }
878
879         return { 
880                 NAME => $idl->{NAME},
881                 UUID => lc(has_property($idl, "uuid")),
882                 VERSION => $version,
883                 TYPE => "INTERFACE",
884                 PROPERTIES => $idl->{PROPERTIES},
885                 FUNCTIONS => \@functions,
886                 CONSTS => \@consts,
887                 TYPES => \@types,
888                 ENDPOINTS => \@endpoints
889         };
890 }
891
892 # Convert a IDL tree to a NDR tree
893 # Gives a result tree describing all that's necessary for easily generating
894 # NDR parsers / generators
895 sub Parse($)
896 {
897         my $idl = shift;
898
899         return undef unless (defined($idl));
900
901         Parse::Pidl::NDR::Validate($idl);
902         
903         my @ndr = ();
904
905         foreach (@{$idl}) {
906                 ($_->{TYPE} eq "CPP_QUOTE") && push(@ndr, $_);
907                 ($_->{TYPE} eq "INTERFACE") && push(@ndr, ParseInterface($_));
908                 ($_->{TYPE} eq "IMPORT") && push(@ndr, $_);
909         }
910
911         return \@ndr;
912 }
913
914 sub GetNextLevel($$)
915 {
916         my $e = shift;
917         my $fl = shift;
918
919         my $seen = 0;
920
921         foreach my $l (@{$e->{LEVELS}}) {
922                 return $l if ($seen);
923                 ($seen = 1) if ($l == $fl);
924         }
925
926         return undef;
927 }
928
929 sub GetPrevLevel($$)
930 {
931         my ($e,$fl) = @_;
932         my $prev = undef;
933
934         foreach my $l (@{$e->{LEVELS}}) {
935                 (return $prev) if ($l == $fl);
936                 $prev = $l;
937         }
938
939         return undef;
940 }
941
942 sub ContainsString($)
943 {
944         my ($e) = @_;
945
946         foreach my $l (@{$e->{LEVELS}}) {
947                 return 1 if ($l->{TYPE} eq "ARRAY" and $l->{IS_ZERO_TERMINATED});
948         }
949
950         return 0;
951 }
952
953 sub ContainsDeferred($$)
954 {
955         my ($e,$l) = @_;
956
957         return 1 if ($l->{CONTAINS_DEFERRED});
958
959         while ($l = GetNextLevel($e,$l))
960         {
961                 return 1 if ($l->{IS_DEFERRED}); 
962                 return 1 if ($l->{CONTAINS_DEFERRED});
963         } 
964         
965         return 0;
966 }
967
968 sub ContainsPipe($$)
969 {
970         my ($e,$l) = @_;
971
972         return 1 if ($l->{TYPE} eq "PIPE");
973
974         while ($l = GetNextLevel($e,$l))
975         {
976                 return 1 if ($l->{TYPE} eq "PIPE");
977         }
978
979         return 0;
980 }
981
982 sub el_name($)
983 {
984         my $e = shift;
985         my $name = "<ANONYMOUS>";
986
987         $name = $e->{NAME} if defined($e->{NAME});
988
989         if (defined($e->{PARENT}) and defined($e->{PARENT}->{NAME})) {
990                 return "$e->{PARENT}->{NAME}.$name";
991         }
992
993         if (defined($e->{PARENT}) and
994             defined($e->{PARENT}->{PARENT}) and
995             defined($e->{PARENT}->{PARENT}->{NAME})) {
996                 return "$e->{PARENT}->{PARENT}->{NAME}.$name";
997         }
998
999         return $name;
1000 }
1001
1002 ###################################
1003 # find a sibling var in a structure
1004 sub find_sibling($$)
1005 {
1006         my($e,$name) = @_;
1007         my($fn) = $e->{PARENT};
1008
1009         if ($name =~ /\*(.*)/) {
1010                 $name = $1;
1011         }
1012
1013         for my $e2 (@{$fn->{ELEMENTS}}) {
1014                 return $e2 if ($e2->{NAME} eq $name);
1015         }
1016
1017         return undef;
1018 }
1019
1020 my %property_list = (
1021         # interface
1022         "helpstring"            => ["INTERFACE", "FUNCTION"],
1023         "version"               => ["INTERFACE"],
1024         "uuid"                  => ["INTERFACE"],
1025         "endpoint"              => ["INTERFACE"],
1026         "pointer_default"       => ["INTERFACE"],
1027         "helper"                => ["INTERFACE"],
1028         "pyhelper"              => ["INTERFACE"],
1029         "authservice"           => ["INTERFACE"],
1030         "restricted"            => ["INTERFACE"],
1031         "no_srv_register"       => ["INTERFACE"],
1032
1033         # dcom
1034         "object"                => ["INTERFACE"],
1035         "local"                 => ["INTERFACE", "FUNCTION"],
1036         "iid_is"                => ["ELEMENT"],
1037         "call_as"               => ["FUNCTION"],
1038         "idempotent"            => ["FUNCTION"],
1039
1040         # function
1041         "noopnum"               => ["FUNCTION"],
1042         "in"                    => ["ELEMENT"],
1043         "out"                   => ["ELEMENT"],
1044
1045         # pointer
1046         "ref"                   => ["ELEMENT", "TYPEDEF"],
1047         "ptr"                   => ["ELEMENT", "TYPEDEF"],
1048         "unique"                => ["ELEMENT", "TYPEDEF"],
1049         "ignore"                => ["ELEMENT"],
1050         "relative"              => ["ELEMENT", "TYPEDEF"],
1051         "relative_short"        => ["ELEMENT", "TYPEDEF"],
1052         "null_is_ffffffff"      => ["ELEMENT"],
1053         "relative_base"         => ["TYPEDEF", "STRUCT", "UNION"],
1054
1055         "gensize"               => ["TYPEDEF", "STRUCT", "UNION"],
1056         "value"                 => ["ELEMENT"],
1057         "flag"                  => ["ELEMENT", "TYPEDEF", "STRUCT", "UNION", "ENUM", "BITMAP", "PIPE"],
1058
1059         # generic
1060         "public"                => ["FUNCTION", "TYPEDEF", "STRUCT", "UNION", "ENUM", "BITMAP", "PIPE"],
1061         "nopush"                => ["FUNCTION", "TYPEDEF", "STRUCT", "UNION", "ENUM", "BITMAP", "PIPE"],
1062         "nopull"                => ["FUNCTION", "TYPEDEF", "STRUCT", "UNION", "ENUM", "BITMAP", "PIPE"],
1063         "nosize"                => ["FUNCTION", "TYPEDEF", "STRUCT", "UNION", "ENUM", "BITMAP"],
1064         "noprint"               => ["FUNCTION", "TYPEDEF", "STRUCT", "UNION", "ENUM", "BITMAP", "ELEMENT", "PIPE"],
1065         "nopython"              => ["FUNCTION", "TYPEDEF", "STRUCT", "UNION", "ENUM", "BITMAP"],
1066         "todo"                  => ["FUNCTION"],
1067
1068         # union
1069         "switch_is"             => ["ELEMENT"],
1070         "switch_type"           => ["ELEMENT", "UNION"],
1071         "nodiscriminant"        => ["UNION"],
1072         "ms_union"              => ["INTERFACE", "UNION"],
1073         "case"                  => ["ELEMENT"],
1074         "default"               => ["ELEMENT"],
1075
1076         "represent_as"          => ["ELEMENT"],
1077         "transmit_as"           => ["ELEMENT"],
1078
1079         # subcontext
1080         "subcontext"            => ["ELEMENT"],
1081         "subcontext_size"       => ["ELEMENT"],
1082         "compression"           => ["ELEMENT"],
1083
1084         # enum
1085         "enum8bit"              => ["ENUM"],
1086         "enum16bit"             => ["ENUM"],
1087         "v1_enum"               => ["ENUM"],
1088
1089         # bitmap
1090         "bitmap8bit"            => ["BITMAP"],
1091         "bitmap16bit"           => ["BITMAP"],
1092         "bitmap32bit"           => ["BITMAP"],
1093         "bitmap64bit"           => ["BITMAP"],
1094
1095         # array
1096         "range"                 => ["ELEMENT", "PIPE"],
1097         "size_is"               => ["ELEMENT"],
1098         "string"                => ["ELEMENT"],
1099         "noheader"              => ["ELEMENT"],
1100         "charset"               => ["ELEMENT"],
1101         "length_is"             => ["ELEMENT"],
1102 );
1103
1104 #####################################################################
1105 # check for unknown properties
1106 sub ValidProperties($$)
1107 {
1108         my ($e,$t) = @_;
1109
1110         return unless defined $e->{PROPERTIES};
1111
1112         foreach my $key (keys %{$e->{PROPERTIES}}) {
1113                 warning($e, el_name($e) . ": unknown property '$key'")
1114                         unless defined($property_list{$key});
1115
1116                 fatal($e, el_name($e) . ": property '$key' not allowed on '$t'")
1117                         unless grep(/^$t$/, @{$property_list{$key}});
1118         }
1119 }
1120
1121 sub mapToScalar($)
1122 {
1123         sub mapToScalar($);
1124         my $t = shift;
1125         return $t->{NAME} if (ref($t) eq "HASH" and $t->{TYPE} eq "SCALAR");
1126         my $ti = getType($t);
1127
1128         if (not defined ($ti)) {
1129                 return undef;
1130         } elsif ($ti->{TYPE} eq "TYPEDEF") {
1131                 return mapToScalar($ti->{DATA});
1132         } elsif ($ti->{TYPE} eq "ENUM") {
1133                 return Parse::Pidl::Typelist::enum_type_fn($ti);
1134         } elsif ($ti->{TYPE} eq "BITMAP") {
1135                 return Parse::Pidl::Typelist::bitmap_type_fn($ti);
1136         }
1137
1138         return undef;
1139 }
1140
1141 #####################################################################
1142 # validate an element
1143 sub ValidElement($)
1144 {
1145         my $e = shift;
1146
1147         ValidProperties($e,"ELEMENT");
1148
1149         # Check whether switches are used correctly.
1150         if (my $switch = has_property($e, "switch_is")) {
1151                 my $e2 = find_sibling($e, $switch);
1152                 my $type = getType($e->{TYPE});
1153
1154                 if (defined($type) and $type->{DATA}->{TYPE} ne "UNION") {
1155                         fatal($e, el_name($e) . ": switch_is() used on non-union type $e->{TYPE} which is a $type->{DATA}->{TYPE}");
1156                 }
1157
1158                 if (not has_property($type->{DATA}, "nodiscriminant") and defined($e2)) {
1159                         my $discriminator_type = has_property($type->{DATA}, "switch_type");
1160                         $discriminator_type = "uint32" unless defined ($discriminator_type);
1161
1162                         my $t1 = mapScalarType(mapToScalar($discriminator_type));
1163
1164                         if (not defined($t1)) {
1165                                 fatal($e, el_name($e) . ": unable to map discriminator type '$discriminator_type' to scalar");
1166                         }
1167
1168                         my $t2 = mapScalarType(mapToScalar($e2->{TYPE}));
1169                         if (not defined($t2)) {
1170                                 fatal($e, el_name($e) . ": unable to map variable used for switch_is() to scalar");
1171                         }
1172
1173                         if ($t1 ne $t2) {
1174                                 warning($e, el_name($e) . ": switch_is() is of type $e2->{TYPE} ($t2), while discriminator type for union $type->{NAME} is $discriminator_type ($t1)");
1175                         }
1176                 }
1177         }
1178
1179         if (has_property($e, "subcontext") and has_property($e, "represent_as")) {
1180                 fatal($e, el_name($e) . " : subcontext() and represent_as() can not be used on the same element");
1181         }
1182
1183         if (has_property($e, "subcontext") and has_property($e, "transmit_as")) {
1184                 fatal($e, el_name($e) . " : subcontext() and transmit_as() can not be used on the same element");
1185         }
1186
1187         if (has_property($e, "represent_as") and has_property($e, "transmit_as")) {
1188                 fatal($e, el_name($e) . " : represent_as() and transmit_as() can not be used on the same element");
1189         }
1190
1191         if (has_property($e, "represent_as") and has_property($e, "value")) {
1192                 fatal($e, el_name($e) . " : represent_as() and value() can not be used on the same element");
1193         }
1194
1195         if (has_property($e, "subcontext")) {
1196                 warning($e, "subcontext() is deprecated. Use represent_as() or transmit_as() instead");
1197         }
1198
1199         if (defined (has_property($e, "subcontext_size")) and not defined(has_property($e, "subcontext"))) {
1200                 fatal($e, el_name($e) . " : subcontext_size() on non-subcontext element");
1201         }
1202
1203         if (defined (has_property($e, "compression")) and not defined(has_property($e, "subcontext"))) {
1204                 fatal($e, el_name($e) . " : compression() on non-subcontext element");
1205         }
1206
1207         if (!$e->{POINTERS} && (
1208                 has_property($e, "ptr") or
1209                 has_property($e, "unique") or
1210                 has_property($e, "relative") or
1211                 has_property($e, "relative_short") or
1212                 has_property($e, "ref"))) {
1213                 fatal($e, el_name($e) . " : pointer properties on non-pointer element\n");      
1214         }
1215 }
1216
1217 #####################################################################
1218 # validate an enum
1219 sub ValidEnum($)
1220 {
1221         my ($enum) = @_;
1222
1223         ValidProperties($enum, "ENUM");
1224 }
1225
1226 #####################################################################
1227 # validate a bitmap
1228 sub ValidBitmap($)
1229 {
1230         my ($bitmap) = @_;
1231
1232         ValidProperties($bitmap, "BITMAP");
1233 }
1234
1235 #####################################################################
1236 # validate a struct
1237 sub ValidStruct($)
1238 {
1239         my($struct) = shift;
1240
1241         ValidProperties($struct, "STRUCT");
1242
1243         return unless defined($struct->{ELEMENTS});
1244
1245         foreach my $e (@{$struct->{ELEMENTS}}) {
1246                 $e->{PARENT} = $struct;
1247                 ValidElement($e);
1248         }
1249 }
1250
1251 #####################################################################
1252 # parse a union
1253 sub ValidUnion($)
1254 {
1255         my($union) = shift;
1256
1257         ValidProperties($union,"UNION");
1258
1259         if (has_property($union->{PARENT}, "nodiscriminant") and 
1260                 has_property($union->{PARENT}, "switch_type")) {
1261                 fatal($union->{PARENT}, $union->{PARENT}->{NAME} . ": switch_type(" . $union->{PARENT}->{PROPERTIES}->{switch_type} . ") on union without discriminant");
1262         }
1263
1264         return unless defined($union->{ELEMENTS});
1265
1266         foreach my $e (@{$union->{ELEMENTS}}) {
1267                 $e->{PARENT} = $union;
1268
1269                 if (defined($e->{PROPERTIES}->{default}) and 
1270                         defined($e->{PROPERTIES}->{case})) {
1271                         fatal($e, "Union member $e->{NAME} can not have both default and case properties!");
1272                 }
1273                 
1274                 unless (defined ($e->{PROPERTIES}->{default}) or 
1275                                 defined ($e->{PROPERTIES}->{case})) {
1276                         fatal($e, "Union member $e->{NAME} must have default or case property");
1277                 }
1278
1279                 if (has_property($e, "ref")) {
1280                         fatal($e, el_name($e) . ": embedded ref pointers are not supported yet\n");
1281                 }
1282
1283
1284                 ValidElement($e);
1285         }
1286 }
1287
1288 #####################################################################
1289 # validate a pipe
1290 sub ValidPipe($)
1291 {
1292         my ($pipe) = @_;
1293         my $struct = $pipe->{DATA};
1294
1295         ValidProperties($pipe, "PIPE");
1296
1297         $struct->{PARENT} = $pipe;
1298
1299         $struct->{FILE} = $pipe->{FILE} unless defined($struct->{FILE});
1300         $struct->{LINE} = $pipe->{LINE} unless defined($struct->{LINE});
1301
1302         ValidType($struct);
1303 }
1304
1305 #####################################################################
1306 # parse a typedef
1307 sub ValidTypedef($)
1308 {
1309         my($typedef) = shift;
1310         my $data = $typedef->{DATA};
1311
1312         ValidProperties($typedef, "TYPEDEF");
1313
1314         return unless (ref($data) eq "HASH");
1315
1316         $data->{PARENT} = $typedef;
1317
1318         $data->{FILE} = $typedef->{FILE} unless defined($data->{FILE});
1319         $data->{LINE} = $typedef->{LINE} unless defined($data->{LINE});
1320
1321         ValidType($data);
1322 }
1323
1324 #####################################################################
1325 # validate a function
1326 sub ValidFunction($)
1327 {
1328         my($fn) = shift;
1329
1330         ValidProperties($fn,"FUNCTION");
1331
1332         foreach my $e (@{$fn->{ELEMENTS}}) {
1333                 $e->{PARENT} = $fn;
1334                 if (has_property($e, "ref") && !$e->{POINTERS}) {
1335                         fatal($e, "[ref] variables must be pointers ($fn->{NAME}/$e->{NAME})");
1336                 }
1337                 ValidElement($e);
1338         }
1339 }
1340
1341 #####################################################################
1342 # validate a type
1343 sub ValidType($)
1344 {
1345         my ($t) = @_;
1346
1347         { 
1348                 TYPEDEF => \&ValidTypedef,
1349                 STRUCT => \&ValidStruct,
1350                 UNION => \&ValidUnion,
1351                 ENUM => \&ValidEnum,
1352                 BITMAP => \&ValidBitmap,
1353                 PIPE => \&ValidPipe
1354         }->{$t->{TYPE}}->($t);
1355 }
1356
1357 #####################################################################
1358 # parse the interface definitions
1359 sub ValidInterface($)
1360 {
1361         my($interface) = shift;
1362         my($data) = $interface->{DATA};
1363
1364         if (has_property($interface, "helper")) {
1365                 warning($interface, "helper() is pidl-specific and deprecated. Use `include' instead");
1366         }
1367
1368         ValidProperties($interface,"INTERFACE");
1369
1370         if (has_property($interface, "pointer_default")) {
1371                 if (not grep (/$interface->{PROPERTIES}->{pointer_default}/, 
1372                                         ("ref", "unique", "ptr"))) {
1373                         fatal($interface, "Unknown default pointer type `$interface->{PROPERTIES}->{pointer_default}'");
1374                 }
1375         }
1376
1377         if (has_property($interface, "object")) {
1378                 if (has_property($interface, "version") && 
1379                         $interface->{PROPERTIES}->{version} != 0) {
1380                         fatal($interface, "Object interfaces must have version 0.0 ($interface->{NAME})");
1381                 }
1382
1383                 if (!defined($interface->{BASE}) && 
1384                         not ($interface->{NAME} eq "IUnknown")) {
1385                         fatal($interface, "Object interfaces must all derive from IUnknown ($interface->{NAME})");
1386                 }
1387         }
1388                 
1389         foreach my $d (@{$data}) {
1390                 ($d->{TYPE} eq "FUNCTION") && ValidFunction($d);
1391                 ($d->{TYPE} eq "TYPEDEF" or 
1392                  $d->{TYPE} eq "STRUCT" or
1393                  $d->{TYPE} eq "UNION" or 
1394                  $d->{TYPE} eq "ENUM" or
1395                  $d->{TYPE} eq "BITMAP" or
1396                  $d->{TYPE} eq "PIPE") && ValidType($d);
1397         }
1398
1399 }
1400
1401 #####################################################################
1402 # Validate an IDL structure
1403 sub Validate($)
1404 {
1405         my($idl) = shift;
1406
1407         foreach my $x (@{$idl}) {
1408                 ($x->{TYPE} eq "INTERFACE") && 
1409                     ValidInterface($x);
1410                 ($x->{TYPE} eq "IMPORTLIB") &&
1411                         fatal($x, "importlib() not supported");
1412         }
1413 }
1414
1415 sub is_charset_array($$)
1416 {
1417         my ($e,$l) = @_;
1418
1419         return 0 if ($l->{TYPE} ne "ARRAY");
1420
1421         my $nl = GetNextLevel($e,$l);
1422
1423         return 0 unless ($nl->{TYPE} eq "DATA");
1424
1425         return has_property($e, "charset");
1426 }
1427
1428
1429
1430 1;