r15220: Another attempt to fix the bug found by the IBM Checker
[samba.git] / source / pidl / lib / Parse / Pidl / Samba4 / EJS.pm
1 ###################################################
2 # EJS function wrapper generator
3 # Copyright jelmer@samba.org 2005
4 # Copyright Andrew Tridgell 2005
5 # released under the GNU GPL
6
7 package Parse::Pidl::Samba4::EJS;
8
9 use strict;
10 use Parse::Pidl::Typelist;
11 use Parse::Pidl::Util qw(has_property);
12
13 use vars qw($VERSION);
14 $VERSION = '0.01';
15
16 my $res;
17 my $res_hdr;
18
19 my %constants;
20
21 my $tabs = "";
22
23 sub pidl_hdr ($)
24 {
25         $res_hdr .= shift;
26 }
27
28 sub pidl($)
29 {
30         my $d = shift;
31         if ($d) {
32                 $res .= $tabs;
33                 $res .= $d;
34         }
35         $res .= "\n";
36 }
37
38 sub indent()
39 {
40         $tabs .= "\t";
41 }
42
43 sub deindent()
44 {
45         $tabs = substr($tabs, 0, -1);
46 }
47
48 # this should probably be in ndr.pm
49 sub GenerateStructEnv($)
50 {
51         my $x = shift;
52         my %env;
53
54         foreach my $e (@{$x->{ELEMENTS}}) {
55                 if ($e->{NAME}) {
56                         $env{$e->{NAME}} = "r->$e->{NAME}";
57                 }
58         }
59
60         $env{"this"} = "r";
61
62         return \%env;
63 }
64
65 sub GenerateFunctionInEnv($)
66 {
67         my $fn = shift;
68         my %env;
69
70         foreach my $e (@{$fn->{ELEMENTS}}) {
71                 if (grep (/in/, @{$e->{DIRECTION}})) {
72                         $env{$e->{NAME}} = "r->in.$e->{NAME}";
73                 }
74         }
75
76         return \%env;
77 }
78
79 sub GenerateFunctionOutEnv($)
80 {
81         my $fn = shift;
82         my %env;
83
84         foreach my $e (@{$fn->{ELEMENTS}}) {
85                 if (grep (/out/, @{$e->{DIRECTION}})) {
86                         $env{$e->{NAME}} = "r->out.$e->{NAME}";
87                 } elsif (grep (/in/, @{$e->{DIRECTION}})) {
88                         $env{$e->{NAME}} = "r->in.$e->{NAME}";
89                 }
90         }
91
92         return \%env;
93 }
94
95 sub get_pointer_to($)
96 {
97         my $var_name = shift;
98         
99         if ($var_name =~ /^\*(.*)$/) {
100                 return $1;
101         } elsif ($var_name =~ /^\&(.*)$/) {
102                 return "&($var_name)";
103         } else {
104                 return "&$var_name";
105         }
106 }
107
108 sub get_value_of($)
109 {
110         my $var_name = shift;
111
112         if ($var_name =~ /^\&(.*)$/) {
113                 return $1;
114         } else {
115                 return "*$var_name";
116         }
117 }
118
119 #####################################################################
120 # work out is a parse function should be declared static or not
121 sub fn_declare($$)
122 {
123         my ($fn,$decl) = @_;
124
125         if (has_property($fn, "public")) {
126                 pidl_hdr "$decl;\n";
127                 pidl "_PUBLIC_ $decl";
128         } else {
129                 pidl "static $decl";
130         }
131 }
132
133 ###########################
134 # pull a scalar element
135 sub EjsPullScalar($$$$$)
136 {
137         my ($e, $l, $var, $name, $env) = @_;
138
139         return if (has_property($e, "value"));
140
141         my $pl = Parse::Pidl::NDR::GetPrevLevel($e, $l);
142         $var = get_pointer_to($var);
143         # have to handle strings specially :(
144         if ($e->{TYPE} eq "string" && $pl && $pl->{TYPE} eq "POINTER") {
145                 $var = get_pointer_to($var);
146         }
147         pidl "NDR_CHECK(ejs_pull_$e->{TYPE}(ejs, v, $name, $var));";
148 }
149
150 ###########################
151 # pull a pointer element
152 sub EjsPullPointer($$$$$)
153 {
154         my ($e, $l, $var, $name, $env) = @_;
155         pidl "if (ejs_pull_null(ejs, v, $name)) {";
156         indent;
157         if ($l->{POINTER_TYPE} eq "ref") {
158                 pidl "return NT_STATUS_INVALID_PARAMETER_MIX;";
159         } else {
160                 pidl "$var = NULL;";
161         }
162         deindent;
163         pidl "} else {";
164         indent;
165         pidl "EJS_ALLOC(ejs, $var);";
166         $var = get_value_of($var);              
167         EjsPullElement($e, Parse::Pidl::NDR::GetNextLevel($e, $l), $var, $name, $env);
168         deindent;
169         pidl "}";
170 }
171
172 ###########################
173 # pull a string element
174 sub EjsPullString($$$$$)
175 {
176         my ($e, $l, $var, $name, $env) = @_;
177         $var = get_pointer_to($var);
178         pidl "NDR_CHECK(ejs_pull_string(ejs, v, $name, $var));";
179 }
180
181
182 ###########################
183 # pull an array element
184 sub EjsPullArray($$$$$)
185 {
186         my ($e, $l, $var, $name, $env) = @_;
187         my $nl = Parse::Pidl::NDR::GetNextLevel($e, $l);
188         my $length = Parse::Pidl::Util::ParseExpr($l->{LENGTH_IS}, $env);
189         my $size = Parse::Pidl::Util::ParseExpr($l->{SIZE_IS}, $env);
190         my $pl = Parse::Pidl::NDR::GetPrevLevel($e, $l);
191         if ($pl && $pl->{TYPE} eq "POINTER") {
192                 $var = get_pointer_to($var);
193         }
194         # uint8 arrays are treated as data blobs
195         if ($nl->{TYPE} eq 'DATA' && $e->{TYPE} eq 'uint8') {
196                 if (!$l->{IS_FIXED}) {
197                         pidl "EJS_ALLOC_N(ejs, $var, $size);";
198                 }
199                 pidl "ejs_pull_array_uint8(ejs, v, $name, $var, $length);";
200                 return;
201         }
202         my $avar = $var . "[i]";
203         pidl "{";
204         indent;
205         pidl "uint32_t i;";
206         if (!$l->{IS_FIXED}) {
207                 pidl "EJS_ALLOC_N(ejs, $var, $size);";
208         }
209         pidl "for (i=0;i<$length;i++) {";
210         indent;
211         pidl "char *id = talloc_asprintf(ejs, \"%s.%u\", $name, i);";
212         EjsPullElement($e, $nl, $avar, "id", $env);
213         pidl "talloc_free(id);";
214         deindent;
215         pidl "}";
216         pidl "ejs_push_uint32(ejs, v, $name \".length\", &i);";
217         deindent;
218         pidl "}";
219 }
220
221 ###########################
222 # pull a switch element
223 sub EjsPullSwitch($$$$$)
224 {
225         my ($e, $l, $var, $name, $env) = @_;
226         my $switch_var = Parse::Pidl::Util::ParseExpr($l->{SWITCH_IS}, $env);
227         pidl "ejs_set_switch(ejs, $switch_var);";
228         EjsPullElement($e, Parse::Pidl::NDR::GetNextLevel($e, $l), $var, $name, $env);
229 }
230
231 ###########################
232 # pull a structure element
233 sub EjsPullElement($$$$$)
234 {
235         my ($e, $l, $var, $name, $env) = @_;
236         if (has_property($e, "charset")) {
237                 EjsPullString($e, $l, $var, $name, $env);
238         } elsif ($l->{TYPE} eq "ARRAY") {
239                 EjsPullArray($e, $l, $var, $name, $env);
240         } elsif ($l->{TYPE} eq "DATA") {
241                 EjsPullScalar($e, $l, $var, $name, $env);
242         } elsif (($l->{TYPE} eq "POINTER")) {
243                 EjsPullPointer($e, $l, $var, $name, $env);
244         } elsif (($l->{TYPE} eq "SWITCH")) {
245                 EjsPullSwitch($e, $l, $var, $name, $env);
246         } else {
247                 pidl "return ejs_panic(ejs, \"unhandled pull type $l->{TYPE}\");";
248         }
249 }
250
251 #############################################
252 # pull a structure/union element at top level
253 sub EjsPullElementTop($$)
254 {
255         my $e = shift;
256         my $env = shift;
257         my $l = $e->{LEVELS}[0];
258         my $var = Parse::Pidl::Util::ParseExpr($e->{NAME}, $env);
259         my $name = "\"$e->{NAME}\"";
260         EjsPullElement($e, $l, $var, $name, $env);
261 }
262
263 ###########################
264 # pull a struct
265 sub EjsStructPull($$)
266 {
267         my $name = shift;
268         my $d = shift;
269         my $env = GenerateStructEnv($d);
270         fn_declare($d, "NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, struct $name *r)");
271         pidl "{";
272         indent;
273         pidl "NDR_CHECK(ejs_pull_struct_start(ejs, &v, name));";
274         foreach my $e (@{$d->{ELEMENTS}}) {
275                 EjsPullElementTop($e, $env);
276         }
277         pidl "return NT_STATUS_OK;";
278         deindent;
279         pidl "}\n";
280 }
281
282 ###########################
283 # pull a union
284 sub EjsUnionPull($$)
285 {
286         my $name = shift;
287         my $d = shift;
288         my $have_default = 0;
289         my $env = GenerateStructEnv($d);
290         fn_declare($d, "NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, union $name *r)");
291         pidl "{";
292         indent;
293         pidl "NDR_CHECK(ejs_pull_struct_start(ejs, &v, name));";
294         pidl "switch (ejs->switch_var) {";
295         indent;
296         foreach my $e (@{$d->{ELEMENTS}}) {
297                 if ($e->{CASE} eq "default") {
298                         $have_default = 1;
299                 }
300                 pidl "$e->{CASE}:";
301                 indent;
302                 if ($e->{TYPE} ne "EMPTY") {
303                         EjsPullElementTop($e, $env);
304                 }
305                 pidl "break;";
306                 deindent;
307         }
308         if (! $have_default) {
309                 pidl "default:";
310                 indent;
311                 pidl "return ejs_panic(ejs, \"Bad switch value\");";
312                 deindent;
313         }
314         deindent;
315         pidl "}";
316         pidl "return NT_STATUS_OK;";
317         deindent;
318         pidl "}";
319 }
320
321 ##############################################
322 # put the enum elements in the constants array
323 sub EjsEnumConstant($)
324 {
325         my $d = shift;
326         my $v = 0;
327         foreach my $e (@{$d->{ELEMENTS}}) {
328                 my $el = $e;
329                 chomp $el;
330                 if ($el =~ /^(.*)=\s*(.*)\s*$/) {
331                         $el = $1;
332                         $v = $2;
333                 }
334                 $constants{$el} = $v;
335                 $v++;
336         }
337 }
338
339 ###########################
340 # pull a enum
341 sub EjsEnumPull($$)
342 {
343         my $name = shift;
344         my $d = shift;
345         EjsEnumConstant($d);
346         fn_declare($d, "NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, enum $name *r)");
347         pidl "{";
348         indent;
349         pidl "unsigned e;";
350         pidl "NDR_CHECK(ejs_pull_enum(ejs, v, name, &e));";
351         pidl "*r = e;";
352         pidl "return NT_STATUS_OK;";
353         deindent;
354         pidl "}\n";
355 }
356
357 ###########################
358 # pull a bitmap
359 sub EjsBitmapPull($$)
360 {
361         my $name = shift;
362         my $d = shift;
363         my $type_fn = $d->{BASE_TYPE};
364         my($type_decl) = Parse::Pidl::Typelist::mapType($d->{BASE_TYPE});
365         fn_declare($d, "NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, $type_decl *r)");
366         pidl "{";
367         indent;
368         pidl "return ejs_pull_$type_fn(ejs, v, name, r);";
369         deindent;
370         pidl "}";
371 }
372
373
374 ###########################
375 # generate a structure pull
376 sub EjsTypedefPull($)
377 {
378         my $d = shift;
379         return if (has_property($d, "noejs"));
380         if ($d->{DATA}->{TYPE} eq 'STRUCT') {
381                 EjsStructPull($d->{NAME}, $d->{DATA});
382         } elsif ($d->{DATA}->{TYPE} eq 'UNION') {
383                 EjsUnionPull($d->{NAME}, $d->{DATA});
384         } elsif ($d->{DATA}->{TYPE} eq 'ENUM') {
385                 EjsEnumPull($d->{NAME}, $d->{DATA});
386         } elsif ($d->{DATA}->{TYPE} eq 'BITMAP') {
387                 EjsBitmapPull($d->{NAME}, $d->{DATA});
388         } else {
389                 warn "Unhandled pull typedef $d->{NAME} of type $d->{DATA}->{TYPE}";
390         }
391 }
392
393 #####################
394 # generate a function
395 sub EjsPullFunction($)
396 {
397         my $d = shift;
398         my $env = GenerateFunctionInEnv($d);
399         my $name = $d->{NAME};
400
401         pidl "\nstatic NTSTATUS ejs_pull_$name(struct ejs_rpc *ejs, struct MprVar *v, struct $name *r)";
402         pidl "{";
403         indent;
404         pidl "NDR_CHECK(ejs_pull_struct_start(ejs, &v, \"input\"));";
405
406         # we pull non-array elements before array elements as arrays
407         # may have length_is() or size_is() properties that depend
408         # on the non-array elements
409         foreach my $e (@{$d->{ELEMENTS}}) {
410                 next unless (grep(/in/, @{$e->{DIRECTION}}));
411                 next if (has_property($e, "length_is") || 
412                          has_property($e, "size_is"));
413                 EjsPullElementTop($e, $env);
414         }
415
416         foreach my $e (@{$d->{ELEMENTS}}) {
417                 next unless (grep(/in/, @{$e->{DIRECTION}}));
418                 next unless (has_property($e, "length_is") || 
419                              has_property($e, "size_is"));
420                 EjsPullElementTop($e, $env);
421         }
422
423         pidl "return NT_STATUS_OK;";
424         deindent;
425         pidl "}\n";
426 }
427
428
429 ###########################
430 # push a scalar element
431 sub EjsPushScalar($$$$$)
432 {
433         my ($e, $l, $var, $name, $env) = @_;
434         # have to handle strings specially :(
435         my $pl = Parse::Pidl::NDR::GetPrevLevel($e, $l);
436         if ($e->{TYPE} ne "string" || ($pl && $pl->{TYPE} eq "POINTER")) {
437                 $var = get_pointer_to($var);
438         }
439         pidl "NDR_CHECK(ejs_push_$e->{TYPE}(ejs, v, $name, $var));";
440 }
441
442 ###########################
443 # push a string element
444 sub EjsPushString($$$$$)
445 {
446         my ($e, $l, $var, $name, $env) = @_;
447         pidl "NDR_CHECK(ejs_push_string(ejs, v, $name, $var));";
448 }
449
450 ###########################
451 # push a pointer element
452 sub EjsPushPointer($$$$$)
453 {
454         my ($e, $l, $var, $name, $env) = @_;
455         pidl "if (NULL == $var) {";
456         indent;
457         if ($l->{POINTER_TYPE} eq "ref") {
458                 pidl "return NT_STATUS_INVALID_PARAMETER_MIX;";
459         } else {
460                 pidl "NDR_CHECK(ejs_push_null(ejs, v, $name));";
461         }
462         deindent;
463         pidl "} else {";
464         indent;
465         $var = get_value_of($var);              
466         EjsPushElement($e, Parse::Pidl::NDR::GetNextLevel($e, $l), $var, $name, $env);
467         deindent;
468         pidl "}";
469 }
470
471 ###########################
472 # push a switch element
473 sub EjsPushSwitch($$$$$)
474 {
475         my ($e, $l, $var, $name, $env) = @_;
476         my $switch_var = Parse::Pidl::Util::ParseExpr($l->{SWITCH_IS}, $env);
477         pidl "ejs_set_switch(ejs, $switch_var);";
478         EjsPushElement($e, Parse::Pidl::NDR::GetNextLevel($e, $l), $var, $name, $env);
479 }
480
481
482 ###########################
483 # push an array element
484 sub EjsPushArray($$$$$)
485 {
486         my ($e, $l, $var, $name, $env) = @_;
487         my $nl = Parse::Pidl::NDR::GetNextLevel($e, $l);
488         my $length = Parse::Pidl::Util::ParseExpr($l->{LENGTH_IS}, $env);
489         my $pl = Parse::Pidl::NDR::GetPrevLevel($e, $l);
490         if ($pl && $pl->{TYPE} eq "POINTER") {
491                 $var = get_pointer_to($var);
492         }
493         # uint8 arrays are treated as data blobs
494         if ($nl->{TYPE} eq 'DATA' && $e->{TYPE} eq 'uint8') {
495                 pidl "ejs_push_array_uint8(ejs, v, $name, $var, $length);";
496                 return;
497         }
498         my $avar = $var . "[i]";
499         pidl "{";
500         indent;
501         pidl "uint32_t i;";
502         pidl "for (i=0;i<$length;i++) {";
503         indent;
504         pidl "const char *id = talloc_asprintf(ejs, \"%s.%u\", $name, i);";
505         EjsPushElement($e, $nl, $avar, "id", $env);
506         deindent;
507         pidl "}";
508         pidl "ejs_push_uint32(ejs, v, $name \".length\", &i);";
509         deindent;
510         pidl "}";
511 }
512
513 ################################
514 # push a structure/union element
515 sub EjsPushElement($$$$$)
516 {
517         my ($e, $l, $var, $name, $env) = @_;
518         if (has_property($e, "charset")) {
519                 EjsPushString($e, $l, $var, $name, $env);
520         } elsif ($l->{TYPE} eq "ARRAY") {
521                 EjsPushArray($e, $l, $var, $name, $env);
522         } elsif ($l->{TYPE} eq "DATA") {
523                 EjsPushScalar($e, $l, $var, $name, $env);
524         } elsif (($l->{TYPE} eq "POINTER")) {
525                 EjsPushPointer($e, $l, $var, $name, $env);
526         } elsif (($l->{TYPE} eq "SWITCH")) {
527                 EjsPushSwitch($e, $l, $var, $name, $env);
528         } else {
529                 pidl "return ejs_panic(ejs, \"unhandled push type $l->{TYPE}\");";
530         }
531 }
532
533 #############################################
534 # push a structure/union element at top level
535 sub EjsPushElementTop($$)
536 {
537         my $e = shift;
538         my $env = shift;
539         my $l = $e->{LEVELS}[0];
540         my $var = Parse::Pidl::Util::ParseExpr($e->{NAME}, $env);
541         my $name = "\"$e->{NAME}\"";
542         EjsPushElement($e, $l, $var, $name, $env);
543 }
544
545 ###########################
546 # push a struct
547 sub EjsStructPush($$)
548 {
549         my $name = shift;
550         my $d = shift;
551         my $env = GenerateStructEnv($d);
552         fn_declare($d, "NTSTATUS ejs_push_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, const struct $name *r)");
553         pidl "{";
554         indent;
555         pidl "NDR_CHECK(ejs_push_struct_start(ejs, &v, name));";
556         foreach my $e (@{$d->{ELEMENTS}}) {
557                 EjsPushElementTop($e, $env);
558         }
559         pidl "return NT_STATUS_OK;";
560         deindent;
561         pidl "}\n";
562 }
563
564 ###########################
565 # push a union
566 sub EjsUnionPush($$)
567 {
568         my $name = shift;
569         my $d = shift;
570         my $have_default = 0;
571         my $env = GenerateStructEnv($d);
572         fn_declare($d, "NTSTATUS ejs_push_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, const union $name *r)");
573         pidl "{";
574         indent;
575         pidl "NDR_CHECK(ejs_push_struct_start(ejs, &v, name));";
576         pidl "switch (ejs->switch_var) {";
577         indent;
578         foreach my $e (@{$d->{ELEMENTS}}) {
579                 if ($e->{CASE} eq "default") {
580                         $have_default = 1;
581                 }
582                 pidl "$e->{CASE}:";
583                 indent;
584                 if ($e->{TYPE} ne "EMPTY") {
585                         EjsPushElementTop($e, $env);
586                 }
587                 pidl "break;";
588                 deindent;
589         }
590         if (! $have_default) {
591                 pidl "default:";
592                 indent;
593                 pidl "return ejs_panic(ejs, \"Bad switch value\");";
594                 deindent;
595         }
596         deindent;
597         pidl "}";
598         pidl "return NT_STATUS_OK;";
599         deindent;
600         pidl "}";
601 }
602
603 ###########################
604 # push a enum
605 sub EjsEnumPush($$)
606 {
607         my $name = shift;
608         my $d = shift;
609         EjsEnumConstant($d);
610         fn_declare($d, "NTSTATUS ejs_push_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, const enum $name *r)");
611         pidl "{";
612         indent;
613         pidl "unsigned e = *r;";
614         pidl "NDR_CHECK(ejs_push_enum(ejs, v, name, &e));";
615         pidl "return NT_STATUS_OK;";
616         deindent;
617         pidl "}\n";
618 }
619
620 ###########################
621 # push a bitmap
622 sub EjsBitmapPush($$)
623 {
624         my $name = shift;
625         my $d = shift;
626         my $type_fn = $d->{BASE_TYPE};
627         my($type_decl) = Parse::Pidl::Typelist::mapType($d->{BASE_TYPE});
628         # put the bitmap elements in the constants array
629         foreach my $e (@{$d->{ELEMENTS}}) {
630                 if ($e =~ /^(\w*)\s*(.*)\s*$/) {
631                         my $bname = $1;
632                         my $v = $2;
633                         $constants{$bname} = $v;
634                 }
635         }
636         fn_declare($d, "NTSTATUS ejs_push_$name(struct ejs_rpc *ejs, struct MprVar *v, const char *name, const $type_decl *r)");
637         pidl "{";
638         indent;
639         pidl "return ejs_push_$type_fn(ejs, v, name, r);";
640         deindent;
641         pidl "}";
642 }
643
644
645 ###########################
646 # generate a structure push
647 sub EjsTypedefPush($)
648 {
649         my $d = shift;
650         return if (has_property($d, "noejs"));
651
652         if ($d->{DATA}->{TYPE} eq 'STRUCT') {
653                 EjsStructPush($d->{NAME}, $d->{DATA});
654         } elsif ($d->{DATA}->{TYPE} eq 'UNION') {
655                 EjsUnionPush($d->{NAME}, $d->{DATA});
656         } elsif ($d->{DATA}->{TYPE} eq 'ENUM') {
657                 EjsEnumPush($d->{NAME}, $d->{DATA});
658         } elsif ($d->{DATA}->{TYPE} eq 'BITMAP') {
659                 EjsBitmapPush($d->{NAME}, $d->{DATA});
660         } else {
661                 warn "Unhandled push typedef $d->{NAME} of type $d->{DATA}->{TYPE}";
662         }
663 }
664
665
666 #####################
667 # generate a function
668 sub EjsPushFunction($)
669 {
670         my $d = shift;
671         my $env = GenerateFunctionOutEnv($d);
672
673         pidl "\nstatic NTSTATUS ejs_push_$d->{NAME}(struct ejs_rpc *ejs, struct MprVar *v, const struct $d->{NAME} *r)";
674         pidl "{";
675         indent;
676         pidl "NDR_CHECK(ejs_push_struct_start(ejs, &v, \"output\"));";
677
678         foreach my $e (@{$d->{ELEMENTS}}) {
679                 next unless (grep(/out/, @{$e->{DIRECTION}}));
680                 EjsPushElementTop($e, $env);
681         }
682
683         if ($d->{RETURN_TYPE}) {
684                 my $t = $d->{RETURN_TYPE};
685                 pidl "NDR_CHECK(ejs_push_$t(ejs, v, \"result\", &r->out.result));";
686         }
687
688         pidl "return NT_STATUS_OK;";
689         deindent;
690         pidl "}\n";
691 }
692
693
694 #################################
695 # generate a ejs mapping function
696 sub EjsFunction($$)
697 {
698         my $d = shift;
699         my $iface = shift;
700         my $name = $d->{NAME};
701         my $callnum = uc("DCERPC_$name");
702         my $table = "&dcerpc_table_$iface";
703
704         pidl "static int ejs_$name(int eid, int argc, struct MprVar **argv)";
705         pidl "{";
706         indent;
707         pidl "return ejs_rpc_call(eid, argc, argv, $table, $callnum, (ejs_pull_function_t)ejs_pull_$name, (ejs_push_function_t)ejs_push_$name);";
708         deindent;
709         pidl "}\n";
710 }
711
712 ###################
713 # handle a constant
714 sub EjsConst($)
715 {
716     my $const = shift;
717     $constants{$const->{NAME}} = $const->{VALUE};
718 }
719
720 #####################################################################
721 # parse the interface definitions
722 sub EjsInterface($$)
723 {
724         my($interface,$needed) = @_;
725         my @fns = ();
726         my $name = $interface->{NAME};
727
728         %constants = ();
729
730         pidl_hdr "#ifndef _HEADER_EJS_$interface->{NAME}\n";
731         pidl_hdr "#define _HEADER_EJS_$interface->{NAME}\n\n";
732
733         if (has_property($interface, "depends")) {
734                 foreach (split / /, $interface->{PROPERTIES}->{depends}) {
735                         pidl_hdr "#include \"librpc/gen_ndr/ndr_$_\_ejs\.h\"\n";
736                 }
737         }
738
739         pidl_hdr "\n";
740
741         foreach my $d (@{$interface->{TYPES}}) {
742                 ($needed->{"push_$d->{NAME}"}) && EjsTypedefPush($d);
743                 ($needed->{"pull_$d->{NAME}"}) && EjsTypedefPull($d);
744         }
745
746         foreach my $d (@{$interface->{FUNCTIONS}}) {
747                 next if not defined($d->{OPNUM});
748                 next if Parse::Pidl::Util::has_property($d, "noejs");
749
750                 EjsPullFunction($d);
751                 EjsPushFunction($d);
752                 EjsFunction($d, $name);
753
754                 push (@fns, $d->{NAME});
755         }
756
757         foreach my $d (@{$interface->{CONSTS}}) {
758                 EjsConst($d);
759         }
760
761         pidl "static int ejs_$name\_init(int eid, int argc, struct MprVar **argv)";
762         pidl "{";
763         indent;
764         pidl "struct MprVar *obj = mprInitObject(eid, \"$name\", argc, argv);";
765         foreach (@fns) {
766                 pidl "mprSetCFunction(obj, \"$_\", ejs_$_);";
767         }
768         foreach my $v (keys %constants) {
769                 my $value = $constants{$v};
770                 if (substr($value, 0, 1) eq "\"") {
771                         pidl "mprSetVar(obj, \"$v\", mprString($value));";
772                 } else {
773                         pidl "mprSetVar(obj, \"$v\", mprCreateNumberVar($value));";
774                 }
775         }
776         pidl "return ejs_rpc_init(obj, \"$name\");";
777         deindent;
778         pidl "}\n";
779
780         pidl "NTSTATUS ejs_init_$name(void)";
781         pidl "{";
782         indent;
783         pidl "ejsDefineCFunction(-1, \"$name\_init\", ejs_$name\_init, NULL, MPR_VAR_SCRIPT_HANDLE);";
784         pidl "return NT_STATUS_OK;";
785         deindent;
786         pidl "}";
787
788         pidl_hdr "\n";
789         pidl_hdr "#endif /* _HEADER_EJS_$interface->{NAME} */\n";
790 }
791
792 #####################################################################
793 # parse a parsed IDL into a C header
794 sub Parse($$)
795 {
796     my($ndr,$hdr) = @_;
797     
798     my $ejs_hdr = $hdr;
799     $ejs_hdr =~ s/.h$/_ejs.h/;
800     $res = "";
801         $res_hdr = "";
802
803     pidl_hdr "/* header auto-generated by pidl */\n\n";
804         
805     pidl "
806 /* EJS wrapper functions auto-generated by pidl */
807 #include \"includes.h\"
808 #include \"librpc/rpc/dcerpc.h\"
809 #include \"lib/appweb/ejs/ejs.h\"
810 #include \"scripting/ejs/ejsrpc.h\"
811 #include \"scripting/ejs/smbcalls.h\"
812 #include \"librpc/gen_ndr/ndr_misc_ejs.h\"
813 #include \"$hdr\"
814 #include \"$ejs_hdr\"
815
816 ";
817
818     my %needed = ();
819
820     foreach my $x (@{$ndr}) {
821             ($x->{TYPE} eq "INTERFACE") && NeededInterface($x, \%needed);
822     }
823
824     foreach my $x (@{$ndr}) {
825             ($x->{TYPE} eq "INTERFACE") && EjsInterface($x, \%needed);
826     }
827
828     return ($res_hdr, $res);
829 }
830
831 sub NeededFunction($$)
832 {
833         my ($fn,$needed) = @_;
834         $needed->{"pull_$fn->{NAME}"} = 1;
835         $needed->{"push_$fn->{NAME}"} = 1;
836         foreach my $e (@{$fn->{ELEMENTS}}) {
837                 if (grep (/in/, @{$e->{DIRECTION}})) {
838                         $needed->{"pull_$e->{TYPE}"} = 1;
839                 }
840                 if (grep (/out/, @{$e->{DIRECTION}})) {
841                         $needed->{"push_$e->{TYPE}"} = 1;
842                 }
843         }
844 }
845
846 sub NeededTypedef($$)
847 {
848         my ($t,$needed) = @_;
849         if (Parse::Pidl::Util::has_property($t, "public")) {
850                 $needed->{"pull_$t->{NAME}"} = not Parse::Pidl::Util::has_property($t, "noejs");
851                 $needed->{"push_$t->{NAME}"} = not Parse::Pidl::Util::has_property($t, "noejs");
852         }
853         if ($t->{DATA}->{TYPE} ne "STRUCT" && 
854             $t->{DATA}->{TYPE} ne "UNION") {
855                 return;
856         }
857         for my $e (@{$t->{DATA}->{ELEMENTS}}) {
858                 if ($needed->{"pull_$t->{NAME}"}) {
859                         $needed->{"pull_$e->{TYPE}"} = 1;
860                 }
861                 if ($needed->{"push_$t->{NAME}"}) {
862                         $needed->{"push_$e->{TYPE}"} = 1;
863                 }
864         }
865 }
866
867 #####################################################################
868 # work out what parse functions are needed
869 sub NeededInterface($$)
870 {
871         my ($interface,$needed) = @_;
872         foreach my $d (@{$interface->{FUNCTIONS}}) {
873             NeededFunction($d, $needed);
874         }
875         foreach my $d (reverse @{$interface->{TYPES}}) {
876             NeededTypedef($d, $needed);
877         }
878 }
879
880 1;