pidl: If STR_NULLTERM we concider it's a string as well
[kai/samba-autobuild/.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         if (property_matches($e, "flag", ".*STR_NULLTERM.*")) {
947                 return 1;
948         }
949         foreach my $l (@{$e->{LEVELS}}) {
950                 return 1 if ($l->{TYPE} eq "ARRAY" and $l->{IS_ZERO_TERMINATED});
951         }
952
953         return 0;
954 }
955
956 sub ContainsDeferred($$)
957 {
958         my ($e,$l) = @_;
959
960         return 1 if ($l->{CONTAINS_DEFERRED});
961
962         while ($l = GetNextLevel($e,$l))
963         {
964                 return 1 if ($l->{IS_DEFERRED}); 
965                 return 1 if ($l->{CONTAINS_DEFERRED});
966         } 
967         
968         return 0;
969 }
970
971 sub ContainsPipe($$)
972 {
973         my ($e,$l) = @_;
974
975         return 1 if ($l->{TYPE} eq "PIPE");
976
977         while ($l = GetNextLevel($e,$l))
978         {
979                 return 1 if ($l->{TYPE} eq "PIPE");
980         }
981
982         return 0;
983 }
984
985 sub el_name($)
986 {
987         my $e = shift;
988         my $name = "<ANONYMOUS>";
989
990         $name = $e->{NAME} if defined($e->{NAME});
991
992         if (defined($e->{PARENT}) and defined($e->{PARENT}->{NAME})) {
993                 return "$e->{PARENT}->{NAME}.$name";
994         }
995
996         if (defined($e->{PARENT}) and
997             defined($e->{PARENT}->{PARENT}) and
998             defined($e->{PARENT}->{PARENT}->{NAME})) {
999                 return "$e->{PARENT}->{PARENT}->{NAME}.$name";
1000         }
1001
1002         return $name;
1003 }
1004
1005 ###################################
1006 # find a sibling var in a structure
1007 sub find_sibling($$)
1008 {
1009         my($e,$name) = @_;
1010         my($fn) = $e->{PARENT};
1011
1012         if ($name =~ /\*(.*)/) {
1013                 $name = $1;
1014         }
1015
1016         for my $e2 (@{$fn->{ELEMENTS}}) {
1017                 return $e2 if ($e2->{NAME} eq $name);
1018         }
1019
1020         return undef;
1021 }
1022
1023 my %property_list = (
1024         # interface
1025         "helpstring"            => ["INTERFACE", "FUNCTION"],
1026         "version"               => ["INTERFACE"],
1027         "uuid"                  => ["INTERFACE"],
1028         "endpoint"              => ["INTERFACE"],
1029         "pointer_default"       => ["INTERFACE"],
1030         "helper"                => ["INTERFACE"],
1031         "pyhelper"              => ["INTERFACE"],
1032         "authservice"           => ["INTERFACE"],
1033         "restricted"            => ["INTERFACE"],
1034         "no_srv_register"       => ["INTERFACE"],
1035
1036         # dcom
1037         "object"                => ["INTERFACE"],
1038         "local"                 => ["INTERFACE", "FUNCTION"],
1039         "iid_is"                => ["ELEMENT"],
1040         "call_as"               => ["FUNCTION"],
1041         "idempotent"            => ["FUNCTION"],
1042
1043         # function
1044         "noopnum"               => ["FUNCTION"],
1045         "in"                    => ["ELEMENT"],
1046         "out"                   => ["ELEMENT"],
1047
1048         # pointer
1049         "ref"                   => ["ELEMENT", "TYPEDEF"],
1050         "ptr"                   => ["ELEMENT", "TYPEDEF"],
1051         "unique"                => ["ELEMENT", "TYPEDEF"],
1052         "ignore"                => ["ELEMENT"],
1053         "relative"              => ["ELEMENT", "TYPEDEF"],
1054         "relative_short"        => ["ELEMENT", "TYPEDEF"],
1055         "null_is_ffffffff"      => ["ELEMENT"],
1056         "relative_base"         => ["TYPEDEF", "STRUCT", "UNION"],
1057
1058         "gensize"               => ["TYPEDEF", "STRUCT", "UNION"],
1059         "value"                 => ["ELEMENT"],
1060         "flag"                  => ["ELEMENT", "TYPEDEF", "STRUCT", "UNION", "ENUM", "BITMAP", "PIPE"],
1061
1062         # generic
1063         "public"                => ["FUNCTION", "TYPEDEF", "STRUCT", "UNION", "ENUM", "BITMAP", "PIPE"],
1064         "nopush"                => ["FUNCTION", "TYPEDEF", "STRUCT", "UNION", "ENUM", "BITMAP", "PIPE"],
1065         "nopull"                => ["FUNCTION", "TYPEDEF", "STRUCT", "UNION", "ENUM", "BITMAP", "PIPE"],
1066         "nosize"                => ["FUNCTION", "TYPEDEF", "STRUCT", "UNION", "ENUM", "BITMAP"],
1067         "noprint"               => ["FUNCTION", "TYPEDEF", "STRUCT", "UNION", "ENUM", "BITMAP", "ELEMENT", "PIPE"],
1068         "nopython"              => ["FUNCTION", "TYPEDEF", "STRUCT", "UNION", "ENUM", "BITMAP"],
1069         "todo"                  => ["FUNCTION"],
1070
1071         # union
1072         "switch_is"             => ["ELEMENT"],
1073         "switch_type"           => ["ELEMENT", "UNION"],
1074         "nodiscriminant"        => ["UNION"],
1075         "ms_union"              => ["INTERFACE", "UNION"],
1076         "case"                  => ["ELEMENT"],
1077         "default"               => ["ELEMENT"],
1078
1079         "represent_as"          => ["ELEMENT"],
1080         "transmit_as"           => ["ELEMENT"],
1081
1082         # subcontext
1083         "subcontext"            => ["ELEMENT"],
1084         "subcontext_size"       => ["ELEMENT"],
1085         "compression"           => ["ELEMENT"],
1086
1087         # enum
1088         "enum8bit"              => ["ENUM"],
1089         "enum16bit"             => ["ENUM"],
1090         "v1_enum"               => ["ENUM"],
1091
1092         # bitmap
1093         "bitmap8bit"            => ["BITMAP"],
1094         "bitmap16bit"           => ["BITMAP"],
1095         "bitmap32bit"           => ["BITMAP"],
1096         "bitmap64bit"           => ["BITMAP"],
1097
1098         # array
1099         "range"                 => ["ELEMENT", "PIPE"],
1100         "size_is"               => ["ELEMENT"],
1101         "string"                => ["ELEMENT"],
1102         "noheader"              => ["ELEMENT"],
1103         "charset"               => ["ELEMENT"],
1104         "length_is"             => ["ELEMENT"],
1105 );
1106
1107 #####################################################################
1108 # check for unknown properties
1109 sub ValidProperties($$)
1110 {
1111         my ($e,$t) = @_;
1112
1113         return unless defined $e->{PROPERTIES};
1114
1115         foreach my $key (keys %{$e->{PROPERTIES}}) {
1116                 warning($e, el_name($e) . ": unknown property '$key'")
1117                         unless defined($property_list{$key});
1118
1119                 fatal($e, el_name($e) . ": property '$key' not allowed on '$t'")
1120                         unless grep(/^$t$/, @{$property_list{$key}});
1121         }
1122 }
1123
1124 sub mapToScalar($)
1125 {
1126         sub mapToScalar($);
1127         my $t = shift;
1128         return $t->{NAME} if (ref($t) eq "HASH" and $t->{TYPE} eq "SCALAR");
1129         my $ti = getType($t);
1130
1131         if (not defined ($ti)) {
1132                 return undef;
1133         } elsif ($ti->{TYPE} eq "TYPEDEF") {
1134                 return mapToScalar($ti->{DATA});
1135         } elsif ($ti->{TYPE} eq "ENUM") {
1136                 return Parse::Pidl::Typelist::enum_type_fn($ti);
1137         } elsif ($ti->{TYPE} eq "BITMAP") {
1138                 return Parse::Pidl::Typelist::bitmap_type_fn($ti);
1139         }
1140
1141         return undef;
1142 }
1143
1144 #####################################################################
1145 # validate an element
1146 sub ValidElement($)
1147 {
1148         my $e = shift;
1149
1150         ValidProperties($e,"ELEMENT");
1151
1152         # Check whether switches are used correctly.
1153         if (my $switch = has_property($e, "switch_is")) {
1154                 my $e2 = find_sibling($e, $switch);
1155                 my $type = getType($e->{TYPE});
1156
1157                 if (defined($type) and $type->{DATA}->{TYPE} ne "UNION") {
1158                         fatal($e, el_name($e) . ": switch_is() used on non-union type $e->{TYPE} which is a $type->{DATA}->{TYPE}");
1159                 }
1160
1161                 if (not has_property($type->{DATA}, "nodiscriminant") and defined($e2)) {
1162                         my $discriminator_type = has_property($type->{DATA}, "switch_type");
1163                         $discriminator_type = "uint32" unless defined ($discriminator_type);
1164
1165                         my $t1 = mapScalarType(mapToScalar($discriminator_type));
1166
1167                         if (not defined($t1)) {
1168                                 fatal($e, el_name($e) . ": unable to map discriminator type '$discriminator_type' to scalar");
1169                         }
1170
1171                         my $t2 = mapScalarType(mapToScalar($e2->{TYPE}));
1172                         if (not defined($t2)) {
1173                                 fatal($e, el_name($e) . ": unable to map variable used for switch_is() to scalar");
1174                         }
1175
1176                         if ($t1 ne $t2) {
1177                                 warning($e, el_name($e) . ": switch_is() is of type $e2->{TYPE} ($t2), while discriminator type for union $type->{NAME} is $discriminator_type ($t1)");
1178                         }
1179                 }
1180         }
1181
1182         if (has_property($e, "subcontext") and has_property($e, "represent_as")) {
1183                 fatal($e, el_name($e) . " : subcontext() and represent_as() can not be used on the same element");
1184         }
1185
1186         if (has_property($e, "subcontext") and has_property($e, "transmit_as")) {
1187                 fatal($e, el_name($e) . " : subcontext() and transmit_as() can not be used on the same element");
1188         }
1189
1190         if (has_property($e, "represent_as") and has_property($e, "transmit_as")) {
1191                 fatal($e, el_name($e) . " : represent_as() and transmit_as() can not be used on the same element");
1192         }
1193
1194         if (has_property($e, "represent_as") and has_property($e, "value")) {
1195                 fatal($e, el_name($e) . " : represent_as() and value() can not be used on the same element");
1196         }
1197
1198         if (has_property($e, "subcontext")) {
1199                 warning($e, "subcontext() is deprecated. Use represent_as() or transmit_as() instead");
1200         }
1201
1202         if (defined (has_property($e, "subcontext_size")) and not defined(has_property($e, "subcontext"))) {
1203                 fatal($e, el_name($e) . " : subcontext_size() on non-subcontext element");
1204         }
1205
1206         if (defined (has_property($e, "compression")) and not defined(has_property($e, "subcontext"))) {
1207                 fatal($e, el_name($e) . " : compression() on non-subcontext element");
1208         }
1209
1210         if (!$e->{POINTERS} && (
1211                 has_property($e, "ptr") or
1212                 has_property($e, "unique") or
1213                 has_property($e, "relative") or
1214                 has_property($e, "relative_short") or
1215                 has_property($e, "ref"))) {
1216                 fatal($e, el_name($e) . " : pointer properties on non-pointer element\n");      
1217         }
1218 }
1219
1220 #####################################################################
1221 # validate an enum
1222 sub ValidEnum($)
1223 {
1224         my ($enum) = @_;
1225
1226         ValidProperties($enum, "ENUM");
1227 }
1228
1229 #####################################################################
1230 # validate a bitmap
1231 sub ValidBitmap($)
1232 {
1233         my ($bitmap) = @_;
1234
1235         ValidProperties($bitmap, "BITMAP");
1236 }
1237
1238 #####################################################################
1239 # validate a struct
1240 sub ValidStruct($)
1241 {
1242         my($struct) = shift;
1243
1244         ValidProperties($struct, "STRUCT");
1245
1246         return unless defined($struct->{ELEMENTS});
1247
1248         foreach my $e (@{$struct->{ELEMENTS}}) {
1249                 $e->{PARENT} = $struct;
1250                 ValidElement($e);
1251         }
1252 }
1253
1254 #####################################################################
1255 # parse a union
1256 sub ValidUnion($)
1257 {
1258         my($union) = shift;
1259
1260         ValidProperties($union,"UNION");
1261
1262         if (has_property($union->{PARENT}, "nodiscriminant") and 
1263                 has_property($union->{PARENT}, "switch_type")) {
1264                 fatal($union->{PARENT}, $union->{PARENT}->{NAME} . ": switch_type(" . $union->{PARENT}->{PROPERTIES}->{switch_type} . ") on union without discriminant");
1265         }
1266
1267         return unless defined($union->{ELEMENTS});
1268
1269         foreach my $e (@{$union->{ELEMENTS}}) {
1270                 $e->{PARENT} = $union;
1271
1272                 if (defined($e->{PROPERTIES}->{default}) and 
1273                         defined($e->{PROPERTIES}->{case})) {
1274                         fatal($e, "Union member $e->{NAME} can not have both default and case properties!");
1275                 }
1276                 
1277                 unless (defined ($e->{PROPERTIES}->{default}) or 
1278                                 defined ($e->{PROPERTIES}->{case})) {
1279                         fatal($e, "Union member $e->{NAME} must have default or case property");
1280                 }
1281
1282                 if (has_property($e, "ref")) {
1283                         fatal($e, el_name($e) . ": embedded ref pointers are not supported yet\n");
1284                 }
1285
1286
1287                 ValidElement($e);
1288         }
1289 }
1290
1291 #####################################################################
1292 # validate a pipe
1293 sub ValidPipe($)
1294 {
1295         my ($pipe) = @_;
1296         my $struct = $pipe->{DATA};
1297
1298         ValidProperties($pipe, "PIPE");
1299
1300         $struct->{PARENT} = $pipe;
1301
1302         $struct->{FILE} = $pipe->{FILE} unless defined($struct->{FILE});
1303         $struct->{LINE} = $pipe->{LINE} unless defined($struct->{LINE});
1304
1305         ValidType($struct);
1306 }
1307
1308 #####################################################################
1309 # parse a typedef
1310 sub ValidTypedef($)
1311 {
1312         my($typedef) = shift;
1313         my $data = $typedef->{DATA};
1314
1315         ValidProperties($typedef, "TYPEDEF");
1316
1317         return unless (ref($data) eq "HASH");
1318
1319         $data->{PARENT} = $typedef;
1320
1321         $data->{FILE} = $typedef->{FILE} unless defined($data->{FILE});
1322         $data->{LINE} = $typedef->{LINE} unless defined($data->{LINE});
1323
1324         ValidType($data);
1325 }
1326
1327 #####################################################################
1328 # validate a function
1329 sub ValidFunction($)
1330 {
1331         my($fn) = shift;
1332
1333         ValidProperties($fn,"FUNCTION");
1334
1335         foreach my $e (@{$fn->{ELEMENTS}}) {
1336                 $e->{PARENT} = $fn;
1337                 if (has_property($e, "ref") && !$e->{POINTERS}) {
1338                         fatal($e, "[ref] variables must be pointers ($fn->{NAME}/$e->{NAME})");
1339                 }
1340                 ValidElement($e);
1341         }
1342 }
1343
1344 #####################################################################
1345 # validate a type
1346 sub ValidType($)
1347 {
1348         my ($t) = @_;
1349
1350         { 
1351                 TYPEDEF => \&ValidTypedef,
1352                 STRUCT => \&ValidStruct,
1353                 UNION => \&ValidUnion,
1354                 ENUM => \&ValidEnum,
1355                 BITMAP => \&ValidBitmap,
1356                 PIPE => \&ValidPipe
1357         }->{$t->{TYPE}}->($t);
1358 }
1359
1360 #####################################################################
1361 # parse the interface definitions
1362 sub ValidInterface($)
1363 {
1364         my($interface) = shift;
1365         my($data) = $interface->{DATA};
1366
1367         if (has_property($interface, "helper")) {
1368                 warning($interface, "helper() is pidl-specific and deprecated. Use `include' instead");
1369         }
1370
1371         ValidProperties($interface,"INTERFACE");
1372
1373         if (has_property($interface, "pointer_default")) {
1374                 if (not grep (/$interface->{PROPERTIES}->{pointer_default}/, 
1375                                         ("ref", "unique", "ptr"))) {
1376                         fatal($interface, "Unknown default pointer type `$interface->{PROPERTIES}->{pointer_default}'");
1377                 }
1378         }
1379
1380         if (has_property($interface, "object")) {
1381                 if (has_property($interface, "version") && 
1382                         $interface->{PROPERTIES}->{version} != 0) {
1383                         fatal($interface, "Object interfaces must have version 0.0 ($interface->{NAME})");
1384                 }
1385
1386                 if (!defined($interface->{BASE}) && 
1387                         not ($interface->{NAME} eq "IUnknown")) {
1388                         fatal($interface, "Object interfaces must all derive from IUnknown ($interface->{NAME})");
1389                 }
1390         }
1391                 
1392         foreach my $d (@{$data}) {
1393                 ($d->{TYPE} eq "FUNCTION") && ValidFunction($d);
1394                 ($d->{TYPE} eq "TYPEDEF" or 
1395                  $d->{TYPE} eq "STRUCT" or
1396                  $d->{TYPE} eq "UNION" or 
1397                  $d->{TYPE} eq "ENUM" or
1398                  $d->{TYPE} eq "BITMAP" or
1399                  $d->{TYPE} eq "PIPE") && ValidType($d);
1400         }
1401
1402 }
1403
1404 #####################################################################
1405 # Validate an IDL structure
1406 sub Validate($)
1407 {
1408         my($idl) = shift;
1409
1410         foreach my $x (@{$idl}) {
1411                 ($x->{TYPE} eq "INTERFACE") && 
1412                     ValidInterface($x);
1413                 ($x->{TYPE} eq "IMPORTLIB") &&
1414                         fatal($x, "importlib() not supported");
1415         }
1416 }
1417
1418 sub is_charset_array($$)
1419 {
1420         my ($e,$l) = @_;
1421
1422         return 0 if ($l->{TYPE} ne "ARRAY");
1423
1424         my $nl = GetNextLevel($e,$l);
1425
1426         return 0 unless ($nl->{TYPE} eq "DATA");
1427
1428         return has_property($e, "charset");
1429 }
1430
1431
1432
1433 1;