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