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