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