Update from samba tree revision 12488 to 12669
[metze/wireshark/wip.git] / tools / 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-2005
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 ContainsString);
38
39 use strict;
40 use Parse::Pidl::Typelist qw(hasType getType);
41 use Parse::Pidl::Util qw(has_property property_matches);
42
43 # Alignment of the built-in scalar types
44 my $scalar_alignment = {
45         'void' => 0,
46         'char' => 1,
47         'int8' => 1,
48         'uint8' => 1,
49         'int16' => 2,
50         'uint16' => 2,
51         'int32' => 4,
52         'uint32' => 4,
53         'hyper' => 8,
54         'dlong' => 4,
55         'udlong' => 4,
56         'udlongr' => 4,
57         'DATA_BLOB' => 4,
58         'string' => 4,
59         'string_array' => 4, #???
60         'time_t' => 4,
61         'NTTIME' => 4,
62         'NTTIME_1sec' => 4,
63         'NTTIME_hyper' => 8,
64         'WERROR' => 4,
65         'NTSTATUS' => 4,
66         'COMRESULT' => 4,
67         'nbt_string' => 4,
68         'wrepl_nbt_name' => 4,
69         'ipv4address' => 4
70 };
71
72
73
74 sub nonfatal($$)
75 {
76         my ($e,$s) = @_;
77         warn ("$e->{FILE}:$e->{LINE}: Warning: $s\n");
78 }
79
80 #####################################################################
81 # signal a fatal validation error
82 sub fatal($$)
83 {
84         my ($pos,$s) = @_;
85         die("$pos->{FILE}:$pos->{LINE}:$s\n");
86 }
87
88 sub GetElementLevelTable($)
89 {
90         my $e = shift;
91
92         my $order = [];
93         my $is_deferred = 0;
94         my @bracket_array = ();
95         my @length_is = ();
96         my @size_is = ();
97         my $pointer_idx = 0;
98
99         if (has_property($e, "size_is")) {
100                 @size_is = split /,/, has_property($e, "size_is");
101         }
102
103         if (has_property($e, "length_is")) {
104                 @length_is = split /,/, has_property($e, "length_is");
105         }
106
107         if (defined($e->{ARRAY_LEN})) {
108                 @bracket_array = @{$e->{ARRAY_LEN}};
109         }
110
111         # Parse the [][][][] style array stuff
112         for my $i (0 .. $#bracket_array) {
113                 my $d = $bracket_array[$#bracket_array - $i];
114                 my $size = $d;
115                 my $length = $d;
116                 my $is_surrounding = 0;
117                 my $is_varying = 0;
118                 my $is_conformant = 0;
119                 my $is_string = 0;
120
121                 if ($d eq "*") {
122                         $is_conformant = 1;
123                         if ($size = shift @size_is) {
124                         } elsif ((scalar(@size_is) == 0) and has_property($e, "string")) {
125                                 $is_string = 1;
126                                 delete($e->{PROPERTIES}->{string});
127                         } else {
128                                 print "$e->{FILE}:$e->{LINE}: Must specify size_is() for conformant array!\n";
129                                 exit 1;
130                         }
131
132                         if (($length = shift @length_is) or $is_string) {
133                                 $is_varying = 1;
134                         } else {
135                                 $length = $size;
136                         }
137
138                         if ($e == $e->{PARENT}->{ELEMENTS}[-1] 
139                                 and $e->{PARENT}->{TYPE} ne "FUNCTION") {
140                                 $is_surrounding = 1;
141                         }
142                 }
143
144                 push (@$order, {
145                         TYPE => "ARRAY",
146                         SIZE_IS => $size,
147                         LENGTH_IS => $length,
148                         IS_DEFERRED => "$is_deferred",
149                         IS_SURROUNDING => "$is_surrounding",
150                         IS_ZERO_TERMINATED => "$is_string",
151                         IS_VARYING => "$is_varying",
152                         IS_CONFORMANT => "$is_conformant",
153                         IS_FIXED => (not $is_conformant and Parse::Pidl::Util::is_constant($size)),
154                         IS_INLINE => (not $is_conformant and not Parse::Pidl::Util::is_constant($size))
155                 });
156         }
157
158         # Next, all the pointers
159         foreach my $i (1..$e->{POINTERS}) {
160                 my $pt = pointer_type($e);
161
162                 my $level = "EMBEDDED";
163                 # Top level "ref" pointers do not have a referrent identifier
164                 $level = "TOP" if ( defined($pt) 
165                                 and $i == 1
166                                 and $e->{PARENT}->{TYPE} eq "FUNCTION");
167
168                 push (@$order, { 
169                         TYPE => "POINTER",
170                         # for now, there can only be one pointer type per element
171                         POINTER_TYPE => pointer_type($e),
172                         POINTER_INDEX => $pointer_idx,
173                         IS_DEFERRED => "$is_deferred",
174                         LEVEL => $level
175                 });
176
177                 $pointer_idx++;
178                 
179                 # everything that follows will be deferred
180                 $is_deferred = 1 if ($e->{PARENT}->{TYPE} ne "FUNCTION");
181
182                 my $array_size = shift @size_is;
183                 my $array_length;
184                 my $is_varying;
185                 my $is_conformant;
186                 my $is_string = 0;
187                 if ($array_size) {
188                         $is_conformant = 1;
189                         if ($array_length = shift @length_is) {
190                                 $is_varying = 1;
191                         } else {
192                                 $array_length = $array_size;
193                                 $is_varying =0;
194                         }
195                 } 
196                 
197                 if (scalar(@size_is) == 0 and has_property($e, "string")) {
198                         $is_string = 1;
199                         $is_varying = $is_conformant = has_property($e, "noheader")?0:1;
200                         delete($e->{PROPERTIES}->{string});
201                 }
202
203                 if ($array_size or $is_string) {
204                         push (@$order, {
205                                 TYPE => "ARRAY",
206                                 IS_ZERO_TERMINATED => "$is_string",
207                                 SIZE_IS => $array_size,
208                                 LENGTH_IS => $array_length,
209                                 IS_DEFERRED => "$is_deferred",
210                                 IS_SURROUNDING => 0,
211                                 IS_VARYING => "$is_varying",
212                                 IS_CONFORMANT => "$is_conformant",
213                                 IS_FIXED => 0,
214                                 IS_INLINE => 0,
215                         });
216
217                         $is_deferred = 0;
218                 } 
219         }
220
221         if (defined(has_property($e, "subcontext"))) {
222                 my $hdr_size = has_property($e, "subcontext");
223                 my $subsize = has_property($e, "subcontext_size");
224                 if (not defined($subsize)) { 
225                         $subsize = -1; 
226                 }
227                 
228                 push (@$order, {
229                         TYPE => "SUBCONTEXT",
230                         HEADER_SIZE => $hdr_size,
231                         SUBCONTEXT_SIZE => $subsize,
232                         IS_DEFERRED => $is_deferred,
233                         COMPRESSION => has_property($e, "compression"),
234                         OBFUSCATION => has_property($e, "obfuscation")
235                 });
236         }
237
238         if (my $switch = has_property($e, "switch_is")) {
239                 push (@$order, {
240                         TYPE => "SWITCH", 
241                         SWITCH_IS => $switch,
242                         IS_DEFERRED => $is_deferred
243                 });
244         }
245
246         if (scalar(@size_is) > 0) {
247                 nonfatal($e, "size_is() on non-array element");
248         }
249
250         if (scalar(@length_is) > 0) {
251                 nonfatal($e, "length_is() on non-array element");
252         }
253
254         if (has_property($e, "string")) {
255                 nonfatal($e, "string() attribute on non-array element");
256         }
257
258         push (@$order, {
259                 TYPE => "DATA",
260                 DATA_TYPE => $e->{TYPE},
261                 IS_DEFERRED => $is_deferred,
262                 CONTAINS_DEFERRED => can_contain_deferred($e),
263                 IS_SURROUNDING => 0 #FIXME
264         });
265
266         my $i = 0;
267         foreach (@$order) { $_->{LEVEL_INDEX} = $i; $i+=1; }
268
269         return $order;
270 }
271
272 #####################################################################
273 # see if a type contains any deferred data 
274 sub can_contain_deferred
275 {
276         my $e = shift;
277
278         return 0 if (Parse::Pidl::Typelist::is_scalar($e->{TYPE}));
279         return 1 unless (hasType($e->{TYPE})); # assume the worst
280
281         my $type = getType($e->{TYPE});
282
283         foreach my $x (@{$type->{DATA}->{ELEMENTS}}) {
284                 return 1 if ($x->{POINTERS});
285                 return 1 if (can_contain_deferred ($x));
286         }
287         
288         return 0;
289 }
290
291 sub pointer_type($)
292 {
293         my $e = shift;
294
295         return undef unless $e->{POINTERS};
296         
297         return "ref" if (has_property($e, "ref"));
298         return "ptr" if (has_property($e, "ptr"));
299         return "sptr" if (has_property($e, "sptr"));
300         return "unique" if (has_property($e, "unique"));
301         return "relative" if (has_property($e, "relative"));
302         return "ignore" if (has_property($e, "ignore"));
303
304         return undef;
305 }
306
307 #####################################################################
308 # work out the correct alignment for a structure or union
309 sub find_largest_alignment($)
310 {
311         my $s = shift;
312
313         my $align = 1;
314         for my $e (@{$s->{ELEMENTS}}) {
315                 my $a = 1;
316
317                 if ($e->{POINTERS}) {
318                         $a = 4; 
319                 } elsif (has_property($e, "subcontext")){ 
320                         $a = 1;
321                 } else {
322                         $a = align_type($e->{TYPE}); 
323                 }
324
325                 $align = $a if ($align < $a);
326         }
327
328         return $align;
329 }
330
331 #####################################################################
332 # align a type
333 sub align_type($)
334 {
335         sub align_type($);
336         my $e = shift;
337
338         unless (hasType($e)) {
339             # it must be an external type - all we can do is guess 
340                 # print "Warning: assuming alignment of unknown type '$e' is 4\n";
341             return 4;
342         }
343
344         my $dt = getType($e)->{DATA};
345
346         if ($dt->{TYPE} eq "ENUM") {
347                 return align_type(Parse::Pidl::Typelist::enum_type_fn($dt));
348         } elsif ($dt->{TYPE} eq "BITMAP") {
349                 return align_type(Parse::Pidl::Typelist::bitmap_type_fn($dt));
350         } elsif (($dt->{TYPE} eq "STRUCT") or ($dt->{TYPE} eq "UNION")) {
351                 return find_largest_alignment($dt);
352         } elsif ($dt->{TYPE} eq "SCALAR") {
353                 return $scalar_alignment->{$dt->{NAME}};
354         }
355
356         die("Unknown data type type $dt->{TYPE}");
357 }
358
359 sub ParseElement($)
360 {
361         my $e = shift;
362
363         return {
364                 NAME => $e->{NAME},
365                 TYPE => $e->{TYPE},
366                 PROPERTIES => $e->{PROPERTIES},
367                 LEVELS => GetElementLevelTable($e),
368                 ALIGN => align_type($e->{TYPE}),
369                 ORIGINAL => $e
370         };
371 }
372
373 sub ParseStruct($$)
374 {
375         my ($ndr,$struct) = @_;
376         my @elements = ();
377         my $surrounding = undef;
378
379         foreach my $x (@{$struct->{ELEMENTS}}) 
380         {
381                 my $e = ParseElement($x);
382                 if ($x != $struct->{ELEMENTS}[-1] and 
383                         $e->{LEVELS}[0]->{IS_SURROUNDING}) {
384                         print "$x->{FILE}:$x->{LINE}: error: conformant member not at end of struct\n";
385                 }
386                 push @elements, $e;
387         }
388
389         my $e = $elements[-1];
390         if (defined($e) and defined($e->{LEVELS}[0]->{IS_SURROUNDING}) and
391                 $e->{LEVELS}[0]->{IS_SURROUNDING}) {
392                 $surrounding = $e;
393         }
394
395         if (defined $e->{TYPE} && $e->{TYPE} eq "string"
396             &&  property_matches($e, "flag", ".*LIBNDR_FLAG_STR_CONFORMANT.*")) {
397                 $surrounding = $struct->{ELEMENTS}[-1];
398         }
399                 
400         return {
401                 TYPE => "STRUCT",
402                 SURROUNDING_ELEMENT => $surrounding,
403                 ELEMENTS => \@elements,
404                 PROPERTIES => $struct->{PROPERTIES},
405                 ORIGINAL => $struct
406         };
407 }
408
409 sub ParseUnion($$)
410 {
411         my ($ndr,$e) = @_;
412         my @elements = ();
413         my $switch_type = has_property($e, "switch_type");
414         unless (defined($switch_type)) { $switch_type = "uint32"; }
415
416         if (has_property($e, "nodiscriminant")) { $switch_type = undef; }
417         
418         my $hasdefault = 0;
419         foreach my $x (@{$e->{ELEMENTS}}) 
420         {
421                 my $t;
422                 if ($x->{TYPE} eq "EMPTY") {
423                         $t = { TYPE => "EMPTY" };
424                 } else {
425                         $t = ParseElement($x);
426                 }
427                 if (has_property($x, "default")) {
428                         $t->{CASE} = "default";
429                         $hasdefault = 1;
430                 } elsif (defined($x->{PROPERTIES}->{case})) {
431                         $t->{CASE} = "case $x->{PROPERTIES}->{case}";
432                 } else {
433                         die("Union element $x->{NAME} has neither default nor case property");
434                 }
435                 push @elements, $t;
436         }
437
438         return {
439                 TYPE => "UNION",
440                 SWITCH_TYPE => $switch_type,
441                 ELEMENTS => \@elements,
442                 PROPERTIES => $e->{PROPERTIES},
443                 HAS_DEFAULT => $hasdefault,
444                 ORIGINAL => $e
445         };
446 }
447
448 sub ParseEnum($$)
449 {
450         my ($ndr,$e) = @_;
451
452         return {
453                 TYPE => "ENUM",
454                 BASE_TYPE => Parse::Pidl::Typelist::enum_type_fn($e),
455                 ELEMENTS => $e->{ELEMENTS},
456                 PROPERTIES => $e->{PROPERTIES},
457                 ORIGINAL => $e
458         };
459 }
460
461 sub ParseBitmap($$)
462 {
463         my ($ndr,$e) = @_;
464
465         return {
466                 TYPE => "BITMAP",
467                 BASE_TYPE => Parse::Pidl::Typelist::bitmap_type_fn($e),
468                 ELEMENTS => $e->{ELEMENTS},
469                 PROPERTIES => $e->{PROPERTIES},
470                 ORIGINAL => $e
471         };
472 }
473
474 sub ParseType($$)
475 {
476         my ($ndr, $d) = @_;
477
478         if ($d->{TYPE} eq "STRUCT" or $d->{TYPE} eq "UNION") {
479                 CheckPointerTypes($d, $ndr->{PROPERTIES}->{pointer_default});
480         }
481
482         my $data = {
483                 STRUCT => \&ParseStruct,
484                 UNION => \&ParseUnion,
485                 ENUM => \&ParseEnum,
486                 BITMAP => \&ParseBitmap,
487                 TYPEDEF => \&ParseTypedef,
488         }->{$d->{TYPE}}->($ndr, $d);
489
490         return $data;
491 }
492
493 sub ParseTypedef($$)
494 {
495         my ($ndr,$d) = @_;
496
497         if (defined($d->{PROPERTIES}) && !defined($d->{DATA}->{PROPERTIES})) {
498                 $d->{DATA}->{PROPERTIES} = $d->{PROPERTIES};
499         }
500
501         my $data = ParseType($ndr, $d->{DATA});
502         $data->{ALIGN} = align_type($d->{NAME});
503
504         return {
505                 NAME => $d->{NAME},
506                 TYPE => $d->{TYPE},
507                 PROPERTIES => $d->{PROPERTIES},
508                 DATA => $data,
509                 ORIGINAL => $d
510         };
511 }
512
513 sub ParseConst($$)
514 {
515         my ($ndr,$d) = @_;
516
517         return $d;
518 }
519
520 sub ParseFunction($$$)
521 {
522         my ($ndr,$d,$opnum) = @_;
523         my @elements = ();
524         my $rettype = undef;
525         my $thisopnum = undef;
526
527         CheckPointerTypes($d, $ndr->{PROPERTIES}->{pointer_default_top});
528
529         if (not defined($d->{PROPERTIES}{noopnum})) {
530                 $thisopnum = ${$opnum};
531                 ${$opnum}++;
532         }
533
534         foreach my $x (@{$d->{ELEMENTS}}) {
535                 my $e = ParseElement($x);
536                 push (@{$e->{DIRECTION}}, "in") if (has_property($x, "in"));
537                 push (@{$e->{DIRECTION}}, "out") if (has_property($x, "out"));
538                 push (@elements, $e);
539         }
540
541         if ($d->{RETURN_TYPE} ne "void") {
542                 $rettype = $d->{RETURN_TYPE};
543         }
544         
545         my $async = 0;
546         if (has_property($d, "async")) { $async = 1; }
547         
548         return {
549                         NAME => $d->{NAME},
550                         TYPE => "FUNCTION",
551                         OPNUM => $thisopnum,
552                         ASYNC => $async,
553                         RETURN_TYPE => $rettype,
554                         PROPERTIES => $d->{PROPERTIES},
555                         ELEMENTS => \@elements,
556                         ORIGINAL => $d
557                 };
558 }
559
560 sub CheckPointerTypes($$)
561 {
562         my $s = shift;
563         my $default = shift;
564
565         foreach my $e (@{$s->{ELEMENTS}}) {
566                 if ($e->{POINTERS} and not defined(pointer_type($e))) {
567                         $e->{PROPERTIES}->{$default} = 1;
568                 }
569         }
570 }
571
572 sub ParseInterface($)
573 {
574         my $idl = shift;
575         my @types = ();
576         my @consts = ();
577         my @functions = ();
578         my @endpoints;
579         my @declares = ();
580         my $opnum = 0;
581         my $version;
582
583         if (not has_property($idl, "pointer_default")) {
584                 # MIDL defaults to "ptr" in DCE compatible mode (/osf)
585                 # and "unique" in Microsoft Extensions mode (default)
586                 $idl->{PROPERTIES}->{pointer_default} = "unique";
587         }
588
589         if (not has_property($idl, "pointer_default_top")) {
590                 $idl->{PROPERTIES}->{pointer_default_top} = "ref";
591         }
592
593         foreach my $d (@{$idl->{DATA}}) {
594                 if ($d->{TYPE} eq "DECLARE") {
595                         push (@declares, $d);
596                 } elsif ($d->{TYPE} eq "FUNCTION") {
597                         push (@functions, ParseFunction($idl, $d, \$opnum));
598                 } elsif ($d->{TYPE} eq "CONST") {
599                         push (@consts, ParseConst($idl, $d));
600                 } else {
601                         push (@types, ParseType($idl, $d));
602                 }
603         }
604
605         $version = "0.0";
606
607         if(defined $idl->{PROPERTIES}->{version}) { 
608                 $version = $idl->{PROPERTIES}->{version}; 
609         }
610
611         # If no endpoint is set, default to the interface name as a named pipe
612         if (!defined $idl->{PROPERTIES}->{endpoint}) {
613                 push @endpoints, "\"ncacn_np:[\\\\pipe\\\\" . $idl->{NAME} . "]\"";
614         } else {
615                 @endpoints = split / /, $idl->{PROPERTIES}->{endpoint};
616         }
617
618         return { 
619                 NAME => $idl->{NAME},
620                 UUID => lc(has_property($idl, "uuid")),
621                 VERSION => $version,
622                 TYPE => "INTERFACE",
623                 PROPERTIES => $idl->{PROPERTIES},
624                 FUNCTIONS => \@functions,
625                 CONSTS => \@consts,
626                 TYPES => \@types,
627                 DECLARES => \@declares,
628                 ENDPOINTS => \@endpoints
629         };
630 }
631
632 # Convert a IDL tree to a NDR tree
633 # Gives a result tree describing all that's necessary for easily generating
634 # NDR parsers / generators
635 sub Parse($)
636 {
637         my $idl = shift;
638
639         return undef unless (defined($idl));
640
641         Parse::Pidl::NDR::Validate($idl);
642         
643         my @ndr = ();
644
645         foreach (@{$idl}) {
646                 ($_->{TYPE} eq "INTERFACE") && push(@ndr, ParseInterface($_));
647         }
648
649         return \@ndr;
650 }
651
652 sub GetNextLevel($$)
653 {
654         my $e = shift;
655         my $fl = shift;
656
657         my $seen = 0;
658
659         foreach my $l (@{$e->{LEVELS}}) {
660                 return $l if ($seen);
661                 ($seen = 1) if ($l == $fl);
662         }
663
664         return undef;
665 }
666
667 sub GetPrevLevel($$)
668 {
669         my ($e,$fl) = @_;
670         my $prev = undef;
671
672         foreach my $l (@{$e->{LEVELS}}) {
673                 (return $prev) if ($l == $fl);
674                 $prev = $l;
675         }
676
677         return undef;
678 }
679
680 sub ContainsString($)
681 {
682         my ($e) = @_;
683
684         foreach my $l (@{$e->{LEVELS}}) {
685                 return 1 if ($l->{TYPE} eq "ARRAY" and $l->{IS_ZERO_TERMINATED});
686         }
687
688         return 0;
689 }
690
691 sub ContainsDeferred($$)
692 {
693         my ($e,$l) = @_;
694
695         return 1 if ($l->{CONTAINS_DEFERRED});
696
697         while ($l = GetNextLevel($e,$l))
698         {
699                 return 1 if ($l->{IS_DEFERRED}); 
700                 return 1 if ($l->{CONTAINS_DEFERRED});
701         } 
702         
703         return 0;
704 }
705
706 sub el_name($)
707 {
708         my $e = shift;
709
710         if ($e->{PARENT} && $e->{PARENT}->{NAME}) {
711                 return "$e->{PARENT}->{NAME}.$e->{NAME}";
712         }
713
714         if ($e->{PARENT} && $e->{PARENT}->{PARENT}->{NAME}) {
715                 return "$e->{PARENT}->{PARENT}->{NAME}.$e->{NAME}";
716         }
717
718         if ($e->{PARENT}) {
719                 return "$e->{PARENT}->{NAME}.$e->{NAME}";
720         }
721
722         return $e->{NAME};
723 }
724
725 ###################################
726 # find a sibling var in a structure
727 sub find_sibling($$)
728 {
729         my($e,$name) = @_;
730         my($fn) = $e->{PARENT};
731
732         if ($name =~ /\*(.*)/) {
733                 $name = $1;
734         }
735
736         for my $e2 (@{$fn->{ELEMENTS}}) {
737                 return $e2 if ($e2->{NAME} eq $name);
738         }
739
740         return undef;
741 }
742
743 my %property_list = (
744         # interface
745         "helpstring"            => ["INTERFACE", "FUNCTION"],
746         "version"               => ["INTERFACE"],
747         "uuid"                  => ["INTERFACE"],
748         "endpoint"              => ["INTERFACE"],
749         "pointer_default"       => ["INTERFACE"],
750         "pointer_default_top"   => ["INTERFACE"],
751         "depends"               => ["INTERFACE"],
752         "authservice"           => ["INTERFACE"],
753
754         # dcom
755         "object"                => ["INTERFACE"],
756         "local"                 => ["INTERFACE", "FUNCTION"],
757         "iid_is"                => ["ELEMENT"],
758         "call_as"               => ["FUNCTION"],
759         "idempotent"            => ["FUNCTION"],
760
761         # function
762         "noopnum"               => ["FUNCTION"],
763         "in"                    => ["ELEMENT"],
764         "out"                   => ["ELEMENT"],
765         "async"                 => ["FUNCTION"],
766
767         # pointer
768         "ref"                   => ["ELEMENT"],
769         "ptr"                   => ["ELEMENT"],
770         "sptr"                  => ["ELEMENT"],
771         "unique"                => ["ELEMENT"],
772         "ignore"                => ["ELEMENT"],
773         "relative"              => ["ELEMENT"],
774         "relative_base"         => ["TYPEDEF"],
775
776         "gensize"               => ["TYPEDEF"],
777         "value"                 => ["ELEMENT"],
778         "flag"                  => ["ELEMENT", "TYPEDEF"],
779
780         # generic
781         "public"                => ["FUNCTION", "TYPEDEF"],
782         "nopush"                => ["FUNCTION", "TYPEDEF"],
783         "nopull"                => ["FUNCTION", "TYPEDEF"],
784         "noprint"               => ["FUNCTION", "TYPEDEF"],
785         "noejs"                 => ["FUNCTION", "TYPEDEF"],
786
787         # union
788         "switch_is"             => ["ELEMENT"],
789         "switch_type"           => ["ELEMENT", "TYPEDEF"],
790         "nodiscriminant"        => ["TYPEDEF"],
791         "case"                  => ["ELEMENT"],
792         "default"               => ["ELEMENT"],
793
794         # subcontext
795         "subcontext"            => ["ELEMENT"],
796         "subcontext_size"       => ["ELEMENT"],
797         "compression"           => ["ELEMENT"],
798         "obfuscation"           => ["ELEMENT"],
799
800         # enum
801         "enum8bit"              => ["TYPEDEF"],
802         "enum16bit"             => ["TYPEDEF"],
803         "v1_enum"               => ["TYPEDEF"],
804
805         # bitmap
806         "bitmap8bit"            => ["TYPEDEF"],
807         "bitmap16bit"           => ["TYPEDEF"],
808         "bitmap32bit"           => ["TYPEDEF"],
809         "bitmap64bit"           => ["TYPEDEF"],
810
811         # array
812         "range"                 => ["ELEMENT"],
813         "size_is"               => ["ELEMENT"],
814         "string"                => ["ELEMENT"],
815         "noheader"              => ["ELEMENT"],
816         "charset"               => ["ELEMENT"],
817         "length_is"             => ["ELEMENT"],
818 );
819
820 #####################################################################
821 # check for unknown properties
822 sub ValidProperties($$)
823 {
824         my ($e,$t) = @_;
825
826         return unless defined $e->{PROPERTIES};
827
828         foreach my $key (keys %{$e->{PROPERTIES}}) {
829                 fatal($e, el_name($e) . ": unknown property '$key'\n")
830                         unless defined($property_list{$key});
831
832                 fatal($e, el_name($e) . ": property '$key' not allowed on '$t'\n")
833                         unless grep($t, @{$property_list{$key}});
834         }
835 }
836
837 sub mapToScalar($)
838 {
839         my $t = shift;
840         my $ti = getType($t);
841
842         if (not defined ($ti)) {
843                 return undef;
844         } elsif ($ti->{DATA}->{TYPE} eq "ENUM") {
845                 return Parse::Pidl::Typelist::enum_type_fn($ti->{DATA});
846         } elsif ($ti->{DATA}->{TYPE} eq "BITMAP") {
847                 return Parse::Pidl::Typelist::enum_type_fn($ti->{DATA});
848         } elsif ($ti->{DATA}->{TYPE} eq "SCALAR") {
849                 return $t;
850         }
851
852         return undef;
853 }
854
855 #####################################################################
856 # parse a struct
857 sub ValidElement($)
858 {
859         my $e = shift;
860
861         ValidProperties($e,"ELEMENT");
862
863         if (has_property($e, "ptr")) {
864                 fatal($e, el_name($e) . " : pidl does not support full NDR pointers yet\n");
865         }
866
867         # Check whether switches are used correctly.
868         if (my $switch = has_property($e, "switch_is")) {
869                 my $e2 = find_sibling($e, $switch);
870                 my $type = getType($e->{TYPE});
871
872                 if (defined($type) and $type->{DATA}->{TYPE} ne "UNION") {
873                         fatal($e, el_name($e) . ": switch_is() used on non-union type $e->{TYPE} which is a $type->{DATA}->{TYPE}");
874                 }
875
876                 if (!has_property($type, "nodiscriminant") and defined($e2)) {
877                         my $discriminator_type = has_property($type, "switch_type");
878                         $discriminator_type = "uint32" unless defined ($discriminator_type);
879
880                         my $t1 = mapToScalar($discriminator_type);
881
882                         if (not defined($t1)) {
883                                 fatal($e, el_name($e) . ": unable to map discriminator type '$discriminator_type' to scalar");
884                         }
885
886                         my $t2 = mapToScalar($e2->{TYPE});
887                         if (not defined($t2)) {
888                                 fatal($e, el_name($e) . ": unable to map variable used for switch_is() to scalar");
889                         }
890
891                         if ($t1 ne $t2) {
892                                 nonfatal($e, el_name($e) . ": switch_is() is of type $e2->{TYPE} ($t2), while discriminator type for union $type->{NAME} is $discriminator_type ($t1)");
893                         }
894                 }
895         }
896
897         if (defined (has_property($e, "subcontext_size")) and not defined(has_property($e, "subcontext"))) {
898                 fatal($e, el_name($e) . " : subcontext_size() on non-subcontext element");
899         }
900
901         if (defined (has_property($e, "compression")) and not defined(has_property($e, "subcontext"))) {
902                 fatal($e, el_name($e) . " : compression() on non-subcontext element");
903         }
904
905         if (defined (has_property($e, "obfuscation")) and not defined(has_property($e, "subcontext"))) {
906                 fatal($e, el_name($e) . " : obfuscation() on non-subcontext element");
907         }
908
909         if (!$e->{POINTERS} && (
910                 has_property($e, "ptr") or
911                 has_property($e, "sptr") or
912                 has_property($e, "unique") or
913                 has_property($e, "relative") or
914                 has_property($e, "ref"))) {
915                 fatal($e, el_name($e) . " : pointer properties on non-pointer element\n");      
916         }
917 }
918
919 #####################################################################
920 # parse a struct
921 sub ValidStruct($)
922 {
923         my($struct) = shift;
924
925         ValidProperties($struct,"STRUCT");
926
927         foreach my $e (@{$struct->{ELEMENTS}}) {
928                 $e->{PARENT} = $struct;
929                 ValidElement($e);
930         }
931 }
932
933 #####################################################################
934 # parse a union
935 sub ValidUnion($)
936 {
937         my($union) = shift;
938
939         ValidProperties($union,"UNION");
940
941         if (has_property($union->{PARENT}, "nodiscriminant") and has_property($union->{PARENT}, "switch_type")) {
942                 fatal($union->{PARENT}, $union->{PARENT}->{NAME} . ": switch_type() on union without discriminant");
943         }
944         
945         foreach my $e (@{$union->{ELEMENTS}}) {
946                 $e->{PARENT} = $union;
947
948                 if (defined($e->{PROPERTIES}->{default}) and 
949                         defined($e->{PROPERTIES}->{case})) {
950                         fatal $e, "Union member $e->{NAME} can not have both default and case properties!\n";
951                 }
952                 
953                 unless (defined ($e->{PROPERTIES}->{default}) or 
954                                 defined ($e->{PROPERTIES}->{case})) {
955                         fatal $e, "Union member $e->{NAME} must have default or case property\n";
956                 }
957
958                 if (has_property($e, "ref")) {
959                         fatal($e, el_name($e) . " : embedded ref pointers are not supported yet\n");
960                 }
961
962
963                 ValidElement($e);
964         }
965 }
966
967 #####################################################################
968 # parse a typedef
969 sub ValidTypedef($)
970 {
971         my($typedef) = shift;
972         my $data = $typedef->{DATA};
973
974         ValidProperties($typedef,"TYPEDEF");
975
976         $data->{PARENT} = $typedef;
977
978         if (ref($data) eq "HASH") {
979                 if ($data->{TYPE} eq "STRUCT") {
980                         ValidStruct($data);
981                 }
982
983                 if ($data->{TYPE} eq "UNION") {
984                         ValidUnion($data);
985                 }
986         }
987 }
988
989 #####################################################################
990 # parse a function
991 sub ValidFunction($)
992 {
993         my($fn) = shift;
994
995         ValidProperties($fn,"FUNCTION");
996
997         foreach my $e (@{$fn->{ELEMENTS}}) {
998                 $e->{PARENT} = $fn;
999                 if (has_property($e, "ref") && !$e->{POINTERS}) {
1000                         fatal $e, "[ref] variables must be pointers ($fn->{NAME}/$e->{NAME})\n";
1001                 }
1002                 ValidElement($e);
1003         }
1004 }
1005
1006 #####################################################################
1007 # parse the interface definitions
1008 sub ValidInterface($)
1009 {
1010         my($interface) = shift;
1011         my($data) = $interface->{DATA};
1012
1013         ValidProperties($interface,"INTERFACE");
1014
1015         if (has_property($interface, "pointer_default") && 
1016                 $interface->{PROPERTIES}->{pointer_default} eq "ptr") {
1017                 fatal $interface, "Full pointers are not supported yet\n";
1018         }
1019
1020         if (has_property($interface, "object")) {
1021                 if (has_property($interface, "version") && 
1022                         $interface->{PROPERTIES}->{version} != 0) {
1023                         fatal $interface, "Object interfaces must have version 0.0 ($interface->{NAME})\n";
1024                 }
1025
1026                 if (!defined($interface->{BASE}) && 
1027                         not ($interface->{NAME} eq "IUnknown")) {
1028                         fatal $interface, "Object interfaces must all derive from IUnknown ($interface->{NAME})\n";
1029                 }
1030         }
1031                 
1032         foreach my $d (@{$data}) {
1033                 ($d->{TYPE} eq "TYPEDEF") &&
1034                     ValidTypedef($d);
1035                 ($d->{TYPE} eq "FUNCTION") && 
1036                     ValidFunction($d);
1037         }
1038
1039 }
1040
1041 #####################################################################
1042 # Validate an IDL structure
1043 sub Validate($)
1044 {
1045         my($idl) = shift;
1046
1047         foreach my $x (@{$idl}) {
1048                 ($x->{TYPE} eq "INTERFACE") && 
1049                     ValidInterface($x);
1050         }
1051 }
1052
1053 1;