b98120078102df8a4181318332ee69ee7c5358a3
[samba.git] / source4 / build / pidl / header.pm
1 ###################################################
2 # create C header files for an IDL structure
3 # Copyright tridge@samba.org 2000
4 # released under the GNU GPL
5
6 package IdlHeader;
7
8 use strict;
9 use needed;
10
11 my($res);
12 my($tab_depth);
13
14 sub tabs()
15 {
16         for (my($i)=0; $i < $tab_depth; $i++) {
17                 $res .= "\t";
18         }
19 }
20
21 #####################################################################
22 # parse a properties list
23 sub HeaderProperties($)
24 {
25     my($props) = shift;
26
27     return;
28
29     foreach my $d (@{$props}) {
30         if (ref($d) ne "HASH") {
31             $res .= "/* [$d] */ ";
32         } else {
33             foreach my $k (keys %{$d}) {
34                 $res .= "/* [$k($d->{$k})] */ ";
35             }
36         }
37     }
38 }
39
40 #####################################################################
41 # parse a structure element
42 sub HeaderElement($)
43 {
44     my($element) = shift;
45
46     (defined $element->{PROPERTIES}) && HeaderProperties($element->{PROPERTIES});
47     $res .= tabs();
48     HeaderType($element, $element->{TYPE}, "");
49     $res .= " ";
50     if ($element->{POINTERS} && 
51         $element->{TYPE} ne "string") {
52             my($n) = $element->{POINTERS};
53             for (my($i)=$n; $i > 0; $i--) {
54                     $res .= "*";
55             }
56     }
57     if (defined $element->{ARRAY_LEN} && 
58         !util::is_constant($element->{ARRAY_LEN}) &&
59         !$element->{POINTERS}) {
60             # conformant arrays are ugly! I choose to implement them with
61             # pointers instead of the [1] method
62             $res .= "*";
63     }
64     $res .= "$element->{NAME}";
65     if (defined $element->{ARRAY_LEN} && util::is_constant($element->{ARRAY_LEN})) {
66             $res .= "[$element->{ARRAY_LEN}]";
67     }
68     $res .= ";\n";
69 }
70
71 #####################################################################
72 # parse a struct
73 sub HeaderStruct($$)
74 {
75     my($struct) = shift;
76     my($name) = shift;
77     $res .= "\nstruct $name {\n";
78     $tab_depth++;
79     my $el_count=0;
80     if (defined $struct->{ELEMENTS}) {
81         foreach my $e (@{$struct->{ELEMENTS}}) {
82             HeaderElement($e);
83             $el_count++;
84         }
85     }
86     if ($el_count == 0) {
87             # some compilers can't handle empty structures
88             $res .= "\tchar _empty_;\n";
89     }
90     $tab_depth--;
91     $res .= "}";
92 }
93
94 #####################################################################
95 # parse a struct
96 sub HeaderEnum($$)
97 {
98     my($enum) = shift;
99     my($name) = shift;
100     $res .= "\nenum $name {\n";
101     $tab_depth++;
102     my $els = \@{$enum->{ELEMENTS}};
103     foreach my $i (0 .. $#{$els}-1) {
104             my $e = ${$els}[$i];
105             tabs();
106             chomp $e;
107             $res .= "$e,\n";
108     }
109
110     my $e = ${$els}[$#{$els}];
111     tabs();
112     chomp $e;
113     if ($e !~ /^(.*?)\s*$/) {
114             die "Bad enum $name\n";
115     }
116     $res .= "$1\n";
117     $tab_depth--;
118     $res .= "}";
119 }
120
121
122 #####################################################################
123 # parse a union
124 sub HeaderUnion($$)
125 {
126         my($union) = shift;
127         my($name) = shift;
128         my %done = ();
129
130         (defined $union->{PROPERTIES}) && HeaderProperties($union->{PROPERTIES});
131         $res .= "\nunion $name {\n";
132         $tab_depth++;
133         foreach my $e (@{$union->{DATA}}) {
134                 if ($e->{TYPE} eq "UNION_ELEMENT") {
135                         if (! defined $done{$e->{DATA}->{NAME}}) {
136                                 HeaderElement($e->{DATA});
137                         }
138                         $done{$e->{DATA}->{NAME}} = 1;
139                 }
140         }
141         $tab_depth--;
142         $res .= "}";
143 }
144
145 #####################################################################
146 # parse a type
147 sub HeaderType($$$)
148 {
149         my $e = shift;
150         my($data) = shift;
151         my($name) = shift;
152         if (ref($data) eq "HASH") {
153                 ($data->{TYPE} eq "ENUM") &&
154                     HeaderEnum($data, $name);
155                 ($data->{TYPE} eq "STRUCT") &&
156                     HeaderStruct($data, $name);
157                 ($data->{TYPE} eq "UNION") &&
158                     HeaderUnion($data, $name);
159                 return;
160         }
161         if ($data =~ "string") {
162                 $res .= "const char *";
163         } elsif (util::is_scalar_type($data)) {
164                 $res .= "$data";
165         } elsif (util::has_property($e, "switch_is")) {
166                 $res .= "union $data";
167         } else {
168                 $res .= "struct $data";
169         }
170 }
171
172 #####################################################################
173 # parse a typedef
174 sub HeaderTypedef($)
175 {
176     my($typedef) = shift;
177     HeaderType($typedef, $typedef->{DATA}, $typedef->{NAME});
178     $res .= ";\n";
179 }
180
181 #####################################################################
182 # prototype a typedef
183 sub HeaderTypedefProto($)
184 {
185     my($d) = shift;
186     if (!util::has_property($d->{DATA}, "public")) {
187             return;
188     }
189
190     if ($d->{DATA}{TYPE} eq "STRUCT") {
191             $res .= "NTSTATUS ndr_push_$d->{NAME}(struct ndr_push *, int , struct $d->{NAME} *);\n";
192             $res .= "NTSTATUS ndr_pull_$d->{NAME}(struct ndr_pull *, int , struct $d->{NAME} *);\n";
193             if (!util::has_property($d->{DATA}, "noprint")) {
194                     $res .= "void ndr_print_$d->{NAME}(struct ndr_print *, const char *, struct $d->{NAME} *);\n";
195             }
196
197         if (needed::is_needed("ndr_size_$d->{NAME}")) {
198                 $res .= "size_t ndr_size_$d->{NAME}(int , struct $d->{NAME} *, int );\n";
199         }
200     }
201     if ($d->{DATA}{TYPE} eq "UNION") {
202             $res .= "NTSTATUS ndr_push_$d->{NAME}(struct ndr_push *, int, int, union $d->{NAME} *);\n";
203             $res .= "NTSTATUS ndr_pull_$d->{NAME}(struct ndr_pull *, int, int, union $d->{NAME} *);\n";
204             if (!util::has_property($d->{DATA}, "noprint")) {
205                     $res .= "void ndr_print_$d->{NAME}(struct ndr_print *, const char *, int, union $d->{NAME} *);\n";
206             }
207     }
208 }
209
210 #####################################################################
211 # parse a typedef
212 sub HeaderConst($)
213 {
214     my($const) = shift;
215     $res .= "#define $const->{NAME}\t( $const->{VALUE} )\n";
216 }
217
218 #####################################################################
219 # parse a function
220 sub HeaderFunctionInOut($$)
221 {
222     my($fn) = shift;
223     my($prop) = shift;
224
225     foreach my $e (@{$fn->{DATA}}) {
226             if (util::has_property($e, $prop)) {
227                     HeaderElement($e);
228             }
229     }
230 }
231
232 #####################################################################
233 # determine if we need an "in" or "out" section
234 sub HeaderFunctionInOut_needed($$)
235 {
236     my($fn) = shift;
237     my($prop) = shift;
238
239     if ($prop eq "out" && $fn->{RETURN_TYPE} && $fn->{RETURN_TYPE} ne "void") {
240             return 1;
241     }
242
243     foreach my $e (@{$fn->{DATA}}) {
244             if (util::has_property($e, $prop)) {
245                     return 1;
246             }
247     }
248
249     return undef;
250 }
251
252
253 #####################################################################
254 # parse a function
255 sub HeaderFunction($)
256 {
257     my($fn) = shift;
258
259     $res .= "\nstruct $fn->{NAME} {\n";
260     $tab_depth++;
261     my $needed = 0;
262
263     if (HeaderFunctionInOut_needed($fn, "in")) {
264             tabs();
265             $res .= "struct {\n";
266             $tab_depth++;
267             HeaderFunctionInOut($fn, "in");
268             $tab_depth--;
269             tabs();
270             $res .= "} in;\n\n";
271             $needed++;
272     }
273
274     if (HeaderFunctionInOut_needed($fn, "out")) {
275             tabs();
276             $res .= "struct {\n";
277             $tab_depth++;
278             HeaderFunctionInOut($fn, "out");
279             if ($fn->{RETURN_TYPE} && $fn->{RETURN_TYPE} ne "void") {
280                     tabs();
281                     $res .= "$fn->{RETURN_TYPE} result;\n";
282             }
283             $tab_depth--;
284             tabs();
285             $res .= "} out;\n\n";
286             $needed++;
287     }
288
289     if (! $needed) {
290             # sigh - some compilers don't like empty structures
291             tabs();
292             $res .= "int _dummy_element;\n";
293     }
294
295     $tab_depth--;
296     $res .= "};\n\n";
297 }
298
299 #####################################################################
300 # output prototypes for a IDL function
301 sub HeaderFnProto($)
302 {
303     my $fn = shift;
304     my $name = $fn->{NAME};
305         
306         my $firstarg = "dcerpc_pipe";
307         if (util::has_property($fn, "object")) {
308                 $firstarg = "dcom_interface"; 
309         }
310         
311     $res .= "void ndr_print_$name(struct ndr_print *, const char *, int, struct $name *);\n";
312     $res .= "struct rpc_request *dcerpc_$name\_send(struct $firstarg *, TALLOC_CTX *, struct $name *);\n";
313     $res .= "NTSTATUS dcerpc_$name(struct $firstarg *, TALLOC_CTX *, struct $name *);\n";
314     $res .= "\n";
315 }
316
317 #####################################################################
318 # parse the interface definitions
319 sub HeaderInterface($)
320 {
321     my($interface) = shift;
322     my($data) = $interface->{DATA};
323
324     my $count = 0;
325
326     $res .= "#ifndef _HEADER_NDR_$interface->{NAME}\n";
327     $res .= "#define _HEADER_NDR_$interface->{NAME}\n\n";
328
329     if (defined $interface->{PROPERTIES}->{depends}) {
330             my @d = split / /, $interface->{PROPERTIES}->{depends};
331             foreach my $i (@d) {
332                     $res .= "#include \"librpc/gen_ndr/ndr_$i\.h\"\n";
333             }
334     }
335
336     if (defined $interface->{PROPERTIES}->{uuid}) {
337             my $name = uc $interface->{NAME};
338             $res .= "#define DCERPC_$name\_UUID " . 
339                 util::make_str($interface->{PROPERTIES}->{uuid}) . "\n";
340
341                 if(!defined $interface->{PROPERTIES}->{version}) { $interface->{PROPERTIES}->{version} = "0.0"; }
342             $res .= "#define DCERPC_$name\_VERSION $interface->{PROPERTIES}->{version}\n";
343
344             $res .= "#define DCERPC_$name\_NAME \"$interface->{NAME}\"\n";
345
346                 if(!defined $interface->{PROPERTIES}->{helpstring}) { $interface->{PROPERTIES}->{helpstring} = "NULL"; }
347                 $res .= "#define DCERPC_$name\_HELPSTRING $interface->{PROPERTIES}->{helpstring}\n";
348
349             $res .= "\nextern const struct dcerpc_interface_table dcerpc_table_$interface->{NAME};\n";
350             $res .= "NTSTATUS dcerpc_server_$interface->{NAME}_init(void);\n\n";
351     }
352
353     foreach my $d (@{$data}) {
354             if ($d->{TYPE} eq "FUNCTION") {
355                     my $u_name = uc $d->{NAME};
356                         $res .= "#define DCERPC_$u_name (";
357                 
358                         if (defined($interface->{BASE})) {
359                                 $res .= "DCERPC_" . uc $interface->{BASE} . "_CALL_COUNT + ";
360                         }
361                         
362                     $res .= sprintf("0x%02x", $count) . ")\n";
363                     $count++;
364             }
365     }
366
367         $res .= "\n#define DCERPC_" . uc $interface->{NAME} . "_CALL_COUNT (";
368         
369         if (defined($interface->{BASE})) {
370                 $res .= "DCERPC_" . uc $interface->{BASE} . "_CALL_COUNT + ";
371         }
372         
373         $res .= "$count)\n\n";
374
375     foreach my $d (@{$data}) {
376         ($d->{TYPE} eq "CONST") &&
377             HeaderConst($d);
378         ($d->{TYPE} eq "TYPEDEF") &&
379             HeaderTypedef($d);
380         ($d->{TYPE} eq "TYPEDEF") &&
381             HeaderTypedefProto($d);
382         ($d->{TYPE} eq "FUNCTION") && 
383             HeaderFunction($d);
384         ($d->{TYPE} eq "FUNCTION") && 
385             HeaderFnProto($d);
386     }
387
388     $res .= "#endif /* _HEADER_NDR_$interface->{NAME} */\n";
389 }
390
391 #####################################################################
392 # parse a parsed IDL into a C header
393 sub Parse($)
394 {
395     my($idl) = shift;
396     $tab_depth = 0;
397
398     $res = "/* header auto-generated by pidl */\n\n";
399     foreach my $x (@{$idl}) {
400             if ($x->{TYPE} eq "INTERFACE") {
401                     needed::BuildNeeded($x);
402                     HeaderInterface($x);
403             }
404     }
405     return $res;
406 }
407
408 1;