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