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