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