Add support for implementing LDB modules in Python.
[ira/wip.git] / source4 / lib / appweb / ejs / ejsLib.c
1 /*
2  *      @file   ejs.c
3  *      @brief  Embedded JavaScript (EJS) 
4  *      @overview Main module interface logic.
5  */
6 /********************************* Copyright **********************************/
7 /*
8  *      @copy   default.g
9  *      
10  *      Copyright (c) Mbedthis Software LLC, 2003-2005. All Rights Reserved.
11  *      Portions Copyright (c) GoAhead Software, 1995-2000. All Rights Reserved.
12  *      
13  *      This software is distributed under commercial and open source licenses.
14  *      You may use the GPL open source license described below or you may acquire 
15  *      a commercial license from Mbedthis Software. You agree to be fully bound 
16  *      by the terms of either license. Consult the LICENSE.TXT distributed with 
17  *      this software for full details.
18  *      
19  *      This software is open source; you can redistribute it and/or modify it 
20  *      under the terms of the GNU General Public License as published by the 
21  *      Free Software Foundation; either version 2 of the License, or (at your 
22  *      option) any later version. See the GNU General Public License for more 
23  *      details at: http://www.mbedthis.com/downloads/gplLicense.html
24  *      
25  *      This program is distributed WITHOUT ANY WARRANTY; without even the 
26  *      implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
27  *      
28  *      This GPL license does NOT permit incorporating this software into 
29  *      proprietary programs. If you are unable to comply with the GPL, you must
30  *      acquire a commercial license to use this software. Commercial licenses 
31  *      for this software and support services are available from Mbedthis 
32  *      Software at http://www.mbedthis.com 
33  *      
34  *      @end
35  */
36 /********************************** Includes **********************************/
37
38 #include        "ejsInternal.h"
39
40 #if BLD_FEATURE_EJS
41
42 /********************************** Local Data ********************************/
43
44 /*
45  *      These fields must be locked before any access when multithreaded
46  */
47 static MprVar   master;                                 /* Master object */
48 static MprArray *ejsList;                               /* List of ej handles */
49
50 #if BLD_FEATURE_MULTITHREAD
51 static EjsLock          lock;
52 static EjsUnlock        unlock;
53 static void                     *lockData;
54 #define ejsLock()       if (lock) { (lock)(lockData); } else
55 #define ejsUnlock()     if (unlock) { (unlock)(lockData); } else
56 #else
57 #define ejsLock()               
58 #define ejsUnlock()     
59 #endif
60
61
62 /*
63   save/restore global ejs state - used to cope with simultaneous ejs requests
64   this is a workaround for the use of global variables in ejs
65 */
66 struct ejs_state_ctx {
67         MprVar master;
68         MprArray *ejsList;
69 };
70
71 void *ejs_save_state(void)
72 {
73         struct ejs_state_ctx *ctx = talloc(talloc_autofree_context(), struct ejs_state_ctx);
74         ctx->master = master;
75         ctx->ejsList = ejsList;
76         return ctx;
77 }
78
79 void ejs_restore_state(void *ptr)
80 {
81         struct ejs_state_ctx *ctx = talloc_get_type(ptr, struct ejs_state_ctx);
82         master = ctx->master;
83         ejsList = ctx->ejsList;
84         talloc_free(ctx);
85 }
86
87
88 /****************************** Forward Declarations **************************/
89
90 static char     *getNextVarToken(char **next, char *tokBuf, int tokBufLen);
91
92 /************************************* Code ***********************************/
93 /*
94  *      Initialize the EJ subsystem
95  */
96
97 int ejsOpen(EjsLock lockFn, EjsUnlock unlockFn, void *data)
98 {
99         MprVar  *np;
100
101 #if BLD_FEATURE_MULTITHREAD
102         if (lockFn) {
103                 lock = lockFn;
104                 unlock = unlockFn;
105                 lockData = data;
106         }
107 #endif
108         ejsLock();
109
110         /*
111          *      Master is the top level object (above global). It is used to clone its
112          *      contents into the global scope for each. This is never visible to the
113          *      user, so don't use ejsCreateObj().
114          */
115         master = mprCreateObjVar("master", EJS_SMALL_OBJ_HASH_SIZE);
116         if (master.type == MPR_TYPE_UNDEFINED) {
117                 ejsUnlock();
118                 return MPR_ERR_CANT_ALLOCATE;
119         }
120
121         ejsList = mprCreateArray();
122         ejsDefineStandardProperties(&master);
123
124         /*
125          *      Make these objects immutable
126          */
127         np = mprGetFirstProperty(&master, MPR_ENUM_FUNCTIONS | MPR_ENUM_DATA);
128         while (np) {
129                 mprSetVarReadonly(np, 1);
130                 np = mprGetNextProperty(&master, np, MPR_ENUM_FUNCTIONS | 
131                         MPR_ENUM_DATA);
132         }
133         ejsUnlock();
134         return 0;
135 }
136
137 /******************************************************************************/
138
139 void ejsClose()
140 {
141         ejsLock();
142         mprDestroyArray(ejsList);
143         mprDestroyVar(&master);
144         ejsUnlock();
145 }
146
147 /******************************************************************************/
148 /*
149  *      Create and initialize an EJS engine
150  */
151
152 EjsId ejsOpenEngine(EjsHandle primaryHandle, EjsHandle altHandle)
153 {
154         MprVar  *np;
155         Ejs             *ep;
156
157         ep = (Ejs *)mprMalloc(sizeof(Ejs));
158         if (ep == 0) {
159                 return (EjsId) -1;
160         }
161         memset(ep, 0, sizeof(Ejs));
162
163         ejsLock();
164         ep->eid = (EjsId) mprAddToArray(ejsList, ep);
165         ejsUnlock();
166
167         /*
168          *      Create array of local variable frames
169          */
170         ep->frames = mprCreateArray();
171         if (ep->frames == 0) {
172                 ejsCloseEngine(ep->eid);
173                 return (EjsId) -1;
174         }
175         ep->primaryHandle = primaryHandle;
176         ep->altHandle = altHandle;
177
178         /*
179          *      Create first frame: global variables
180          */
181         ep->global = (MprVar*) mprMalloc(sizeof(MprVar));
182         *ep->global = ejsCreateObj("global", EJS_OBJ_HASH_SIZE);
183         if (ep->global->type == MPR_TYPE_UNDEFINED) {
184                 ejsCloseEngine(ep->eid);
185                 return (EjsId) -1;
186         }
187         mprAddToArray(ep->frames, ep->global);
188
189         /*
190          *      Create first local variable frame
191          */
192         ep->local = (MprVar*) mprMalloc(sizeof(MprVar));
193         *ep->local = ejsCreateObj("local", EJS_OBJ_HASH_SIZE);
194         if (ep->local->type == MPR_TYPE_UNDEFINED) {
195                 ejsCloseEngine(ep->eid);
196                 return (EjsId) -1;
197         }
198         mprAddToArray(ep->frames, ep->local);
199
200         /*
201          *      Clone all master variables into the global frame. This does a
202          *      reference copy.
203          *
204          *              ejsDefineStandardProperties(ep->global);
205          */
206         np = mprGetFirstProperty(&master, MPR_ENUM_FUNCTIONS | MPR_ENUM_DATA);
207         while (np) {
208                 mprCreateProperty(ep->global, np->name, np);
209                 np = mprGetNextProperty(&master, np, MPR_ENUM_FUNCTIONS | 
210                         MPR_ENUM_DATA);
211         }
212
213         mprCreateProperty(ep->global, "global", ep->global);
214         mprCreateProperty(ep->global, "this", ep->global);
215         mprCreateProperty(ep->local, "local", ep->local);
216
217         return ep->eid;
218 }
219
220 /******************************************************************************/
221 /*
222  *      Close an EJS instance
223  */
224
225 void ejsCloseEngine(EjsId eid)
226 {
227         Ejs             *ep;
228         MprVar  *vp;
229         void    **handles;
230         int             i;
231
232         if ((ep = ejsPtr(eid)) == NULL) {
233                 mprAssert(ep);
234                 return;
235         }
236
237         mprFree(ep->error);
238         mprDestroyVar(&ep->result);
239         mprDestroyVar(&ep->tokenNumber);
240
241         if (ep->local) {
242                 mprDeleteProperty(ep->local, "local");
243         }
244         mprDeleteProperty(ep->global, "this");
245         mprDeleteProperty(ep->global, "global");
246
247         handles = ep->frames->handles;
248         for (i = 0; i < ep->frames->max; i++) {
249                 vp = handles[i];
250                 if (vp) {
251 #if BLD_DEBUG
252                         if (vp->type == MPR_TYPE_OBJECT && vp->properties->refCount > 1) {
253                                 mprLog(7, "ejsCloseEngine: %s has ref count %d\n",
254                                         vp->name, vp->properties->refCount);
255                         }
256 #endif
257                         mprDestroyVar(vp);
258                         mprFree(vp);
259                         mprRemoveFromArray(ep->frames, i);
260                 }
261         }
262         mprDestroyArray(ep->frames);
263
264         ejsLock();
265         mprRemoveFromArray(ejsList, (int) ep->eid);
266         ejsUnlock();
267
268         mprFree(ep);
269 }
270
271 /******************************************************************************/
272 /*
273  *      Evaluate an EJS script file
274  */
275
276 int ejsEvalFile(EjsId eid, char *path, MprVar *result, char **emsg)
277 {
278         struct stat      sbuf;
279         Ejs                     *ep;
280         char            *script;
281         int                     rc, fd;
282
283         mprAssert(path && *path);
284
285         if (emsg) {
286                 *emsg = NULL;
287         }
288
289         if ((ep = ejsPtr(eid)) == NULL) {
290                 mprAssert(ep);
291                 goto error;
292         }
293
294         if ((fd = open(path, O_RDONLY | O_BINARY, 0666)) < 0) {
295                 ejsError(ep, "Can't open %s\n", path);
296                 goto error;
297         }
298         
299         if (stat(path, &sbuf) < 0) {
300                 close(fd);
301                 ejsError(ep, "Cant stat %s", path);
302                 goto error;
303         }
304         
305         if ((script = (char*) mprMalloc(sbuf.st_size + 1)) == NULL) {
306                 close(fd);
307                 ejsError(ep, "Cant malloc %d", (int) sbuf.st_size);
308                 goto error;
309         }
310         
311         if (read(fd, script, sbuf.st_size) != (int) sbuf.st_size) {
312                 close(fd);
313                 mprFree(script);
314                 ejsError(ep, "Error reading %s", path);
315                 goto error;
316         }
317         
318         script[sbuf.st_size] = '\0';
319         close(fd);
320
321         rc = ejsEvalBlock(eid, script, result, emsg);
322         mprFree(script);
323
324         return rc;
325
326 /*
327  *      Error return
328  */
329 error:
330         if(emsg)
331                 *emsg = mprStrdup(ep->error);
332         return -1;
333 }
334
335 /******************************************************************************/
336 /*
337  *      Create a new variable scope block. This pushes the old local frame down
338  *      the stack and creates a new local variables frame.
339  */
340
341 int ejsOpenBlock(EjsId eid)
342 {
343         Ejs             *ep;
344
345         if((ep = ejsPtr(eid)) == NULL) {
346                 return -1;
347         }
348
349         ep->local = (MprVar*) mprMalloc(sizeof(MprVar));
350         *ep->local = ejsCreateObj("localBlock", EJS_OBJ_HASH_SIZE);
351
352         mprCreateProperty(ep->local, "local", ep->local);
353
354         return mprAddToArray(ep->frames, ep->local);
355 }
356
357 /******************************************************************************/
358 /*
359  *      Close a variable scope block opened via ejsOpenBlock. Pop back the old
360  *      local variables frame.
361  */
362
363 int ejsCloseBlock(EjsId eid, int fid)
364 {
365         Ejs             *ep;
366
367         if((ep = ejsPtr(eid)) == NULL) {
368                 mprAssert(ep);
369                 return -1;
370         }
371
372         /*
373          *      Must remove self-references before destroying "local"
374          */
375         mprDeleteProperty(ep->local, "local");
376
377         mprDestroyVar(ep->local);
378         mprFree(ep->local);
379
380         mprRemoveFromArray(ep->frames, fid);
381         ep->local = (MprVar*) ep->frames->handles[ep->frames->used - 1];
382
383         return 0;
384 }
385
386 /******************************************************************************/
387 /*
388  *      Create a new variable scope block and evaluate a script. All frames
389  *      created during this context will be automatically deleted when complete.
390  *      vp and emsg are optional. i.e. created local variables will be discarded
391  *      when this routine returns.
392  */
393
394 int ejsEvalBlock(EjsId eid, char *script, MprVar *vp, char **emsg)
395 {
396         int             rc, fid;
397
398         mprAssert(script);
399
400         fid = ejsOpenBlock(eid);
401         rc = ejsEvalScript(eid, script, vp, emsg);
402         ejsCloseBlock(eid, fid);
403
404         return rc;
405 }
406
407 /******************************************************************************/
408 /*
409  *      Parse and evaluate a EJS. Return the result in *vp. The result is "owned"
410  *      by EJ and the caller must not free it. Returns -1 on errors and zero 
411  *      for success. On errors, emsg will be set to the reason. The caller must 
412  *      free emsg.
413  */
414
415 int ejsEvalScript(EjsId eid, char *script, MprVar *vp, char **emsg)
416 {
417         Ejs                     *ep;
418         int                     state;
419         void            *endlessLoopTest;
420         int                     loopCounter;
421         
422         if (emsg) {
423                 *emsg = NULL;
424         } 
425
426         if ((ep = ejsPtr(eid)) == NULL) {
427                 mprAssert(ep);
428                 return -1;
429         }
430
431         mprDestroyVar(&ep->result);
432
433         if (script == 0) {
434                 return 0;
435         }
436
437         /*
438          *      Allocate a new evaluation block, and save the old one
439          */
440         ejsLexOpenScript(ep, script);
441
442         /*
443          *      Do the actual parsing and evaluation
444          */
445         loopCounter = 0;
446         endlessLoopTest = NULL;
447         ep->exitStatus = 0;
448
449         do {
450                 state = ejsParse(ep, EJS_STATE_BEGIN, EJS_FLAGS_EXE);
451
452                 if (state == EJS_STATE_RET) {
453                         state = EJS_STATE_EOF;
454                 }
455                 /*
456                  *      Stuck parser and endless recursion protection.
457                  */
458                 if (endlessLoopTest == ep->input->scriptServp) {
459                         if (loopCounter++ > 10) {
460                                 state = EJS_STATE_ERR;
461                                 ejsError(ep, "Syntax error");
462                         }
463                 } else {
464                         endlessLoopTest = ep->input->scriptServp;
465                         loopCounter = 0;
466                 }
467         } while (state != EJS_STATE_EOF && state != EJS_STATE_ERR);
468
469         ejsLexCloseScript(ep);
470
471         /*
472          *      Return any error string to the user
473          */
474         if (state == EJS_STATE_ERR && emsg) {
475                 *emsg = mprStrdup(ep->error);
476         }
477
478         if (state == EJS_STATE_ERR) {
479                 return -1;
480         }
481
482         if (vp) {
483                 *vp = ep->result;
484         }
485
486         return ep->exitStatus;
487 }
488
489 /******************************************************************************/
490 /*
491  *      Core error handling
492  */
493
494 static void ejsErrorCore(Ejs* ep, const char *fmt, va_list args) 
495         PRINTF_ATTRIBUTE(2, 0);
496
497 static void ejsErrorCore(Ejs* ep, const char *fmt, va_list args)
498 {
499         EjsInput        *ip;
500         char            *errbuf, *msgbuf;
501         int frame = 0;
502
503         mprAssert(ep);
504
505         msgbuf = NULL;
506         mprAllocVsprintf(&msgbuf, MPR_MAX_STRING, fmt, args);
507
508         ip = ep->input;
509         mprAllocSprintf(&errbuf, MPR_MAX_STRING, "%s\nBacktrace:\n", msgbuf);
510
511         /* form a backtrace */
512         while (ip) {
513                 char *msg2, *ebuf2;
514                 mprAllocSprintf(&msg2, MPR_MAX_STRING,
515                                                 "\t[%2d] %20s:%-4d -> %s\n",
516                                                 frame++, ip->procName?ip->procName:"", ip->lineNumber, ip->line);
517                 ebuf2 = mprRealloc(errbuf, strlen(errbuf) + strlen(msg2) + 1);
518                 if (ebuf2 == NULL) break;
519                 errbuf = ebuf2;
520                 memcpy(errbuf+strlen(errbuf), msg2, strlen(msg2)+1);
521                 mprFree(msg2);
522                 ip = ip->next;
523         }
524         mprFree(ep->error);
525         ep->error = errbuf;
526         mprFree(msgbuf);
527 }
528
529 /******************************************************************************/
530 /*
531  *      Internal use function to set the error message
532  */
533
534 void ejsError(Ejs* ep, const char* fmt, ...)
535 {
536         va_list         args;
537
538         va_start(args, fmt);
539         ejsErrorCore(ep, fmt, args);
540         va_end(args);
541 }
542
543 /******************************************************************************/
544 /*
545  *      Public routine to set the error message
546  */
547
548 void ejsSetErrorMsg(EjsId eid, const char* fmt, ...)
549 {
550         va_list         args;
551         Ejs                     *ep;
552
553         if ((ep = ejsPtr(eid)) == NULL) {
554                 mprAssert(ep);
555                 return;
556         }
557         va_start(args, fmt);
558         ejsErrorCore(ep, fmt, args);
559         va_end(args);
560 }
561
562 /******************************************************************************/
563 /*
564  *      Get the current line number
565  */
566
567 int ejsGetLineNumber(EjsId eid)
568 {
569         Ejs             *ep;
570
571         if ((ep = ejsPtr(eid)) == NULL) {
572                 mprAssert(ep);
573                 return -1;
574         }
575         return ep->input->lineNumber;
576 }
577
578 /******************************************************************************/
579 /*
580  *      Return the local object
581  */
582
583 MprVar *ejsGetLocalObject(EjsId eid)
584 {
585         Ejs             *ep;
586
587         if ((ep = ejsPtr(eid)) == NULL) {
588                 mprAssert(ep);
589                 return 0; 
590         }
591         return ep->local;
592 }
593
594 /******************************************************************************/
595 /*
596  *      Return the global object
597  */
598
599 MprVar *ejsGetGlobalObject(EjsId eid)
600 {
601         Ejs             *ep;
602
603         if ((ep = ejsPtr(eid)) == NULL) {
604                 mprAssert(ep);
605                 return 0;
606         }
607         return ep->global;
608 }
609
610 /******************************************************************************/
611 /*
612  *      Copy the value of an object property. Return value is in "value".
613  *      If deepCopy is true, copy all object/strings. Otherwise, object reference
614  *      counts are incremented. Callers must always call mprDestroyVar on the 
615  *      return value to prevent leaks.
616  *
617  *      Returns: -1 on errors or if the variable is not found.
618  */
619
620 int ejsCopyVar(EjsId eid, const char *var, MprVar *value, bool deepCopy)
621 {
622         Ejs                     *ep;
623         MprVar          *vp;
624
625         mprAssert(var && *var);
626         mprAssert(value);
627
628         if ((ep = ejsPtr(eid)) == NULL) {
629                 mprAssert(ep);
630                 return -1;
631         }
632
633         if (ejsGetVarCore(ep, var, 0, &vp, 0) < 0) {
634                 return -1;
635         }
636
637         return mprCopyProperty(value, vp, deepCopy);
638 }
639
640 /******************************************************************************/
641 /*
642  *      Return the value of an object property. Return value is in "value".
643  *      Objects and strings are not copied and reference counts are not modified.
644  *      Callers should NOT call mprDestroyVar. Returns: -1 on errors or if the 
645  *      variable is not found.
646  */
647
648 int ejsReadVar(EjsId eid, const char *var, MprVar *value)
649 {
650         Ejs                     *ep;
651         MprVar          *vp;
652
653         mprAssert(var && *var);
654         mprAssert(value);
655
656         if ((ep = ejsPtr(eid)) == NULL) {
657                 mprAssert(ep);
658                 return -1;
659         }
660
661         if (ejsGetVarCore(ep, var, 0, &vp, 0) < 0) {
662                 return -1;
663         }
664
665         return mprReadProperty(vp, value);
666 }
667
668 /******************************************************************************/
669 /*
670  *      Set a variable that may be an arbitrarily complex object or array reference.
671  *      Will always define in the top most variable frame.
672  */
673
674 int ejsWriteVar(EjsId eid, const char *var, MprVar *value)
675 {
676         Ejs                     *ep;
677         MprVar          *vp;
678
679         mprAssert(var && *var);
680
681         if ((ep = ejsPtr(eid)) == NULL) {
682                 mprAssert(ep);
683                 return -1;
684         }
685
686         if (ejsGetVarCore(ep, var, 0, &vp, EJS_FLAGS_CREATE) < 0) {
687                 return -1;
688         }
689         mprAssert(vp);
690
691         /*
692          *      Only copy the value. Don't overwrite the object's name
693          */
694         mprWriteProperty(vp, value);
695
696         return 0;
697 }
698
699 /******************************************************************************/
700 /*
701  *      Set a variable that may be an arbitrarily complex object or array reference.
702  *      Will always define in the top most variable frame.
703  */
704
705 int ejsWriteVarValue(EjsId eid, const char *var, MprVar value)
706 {
707         return ejsWriteVar(eid, var, &value);
708 }
709
710 /******************************************************************************/
711 /*
712  *      Delete a variable
713  */
714
715 int ejsDeleteVar(EjsId eid, const char *var)
716 {
717         Ejs                     *ep;
718         MprVar          *vp;
719         MprVar          *obj;
720
721         if ((ep = ejsPtr(eid)) == NULL) {
722                 mprAssert(ep);
723                 return -1;
724         }
725         if (ejsGetVarCore(ep, var, &obj, &vp, 0) < 0) {
726                 return -1;
727         }
728         mprDeleteProperty(obj, vp->name);
729         return 0;
730 }
731
732 /******************************************************************************/
733 /*
734  *      Set the expression return value
735  */
736
737 void ejsSetReturnValue(EjsId eid, MprVar value)
738 {
739         Ejs             *ep;
740
741         if ((ep = ejsPtr(eid)) == NULL) {
742                 mprAssert(ep);
743                 return;
744         }
745         mprCopyVar(&ep->result, &value, MPR_SHALLOW_COPY);
746 }
747
748 /******************************************************************************/
749 /*
750  *      Set the expression return value to a string value
751  */
752
753 void ejsSetReturnString(EjsId eid, const char *str)
754 {
755         Ejs             *ep;
756
757         if ((ep = ejsPtr(eid)) == NULL) {
758                 mprAssert(ep);
759                 return;
760         }
761         mprCopyVarValue(&ep->result, mprCreateStringVar(str, 0), MPR_SHALLOW_COPY);
762 }
763
764 /******************************************************************************/
765 /*
766  *      Get the expression return value
767  */
768
769 MprVar *ejsGetReturnValue(EjsId eid)
770 {
771         Ejs             *ep;
772
773         if ((ep = ejsPtr(eid)) == NULL) {
774                 mprAssert(ep);
775                 return 0;
776         }
777         return &ep->result;
778 }
779
780 /******************************************************************************/
781 /*
782  *      Define a C function. If eid < 0, then update the master object with this
783  *      function. NOTE: in this case, functionName must be simple without any "." or
784  *      "[]" elements. If eid >= 0, add to the specified script engine. In this
785  *      case, functionName can be an arbitrary object reference and can contain "."
786  *      or "[]".  
787  */
788
789 void ejsDefineCFunction(EjsId eid, const char *functionName, MprCFunction fn, 
790         void *thisPtr, int flags)
791 {
792         if (eid < 0) {
793                 ejsLock();
794                 mprCreatePropertyValue(&master, functionName, 
795                         mprCreateCFunctionVar(fn, thisPtr, flags));
796                 ejsUnlock();
797         } else {
798                 ejsWriteVarValue(eid, functionName, 
799                         mprCreateCFunctionVar(fn, thisPtr, flags));
800         }
801 }
802
803 /******************************************************************************/
804 /*
805  *      Define a C function with String arguments
806  */
807
808 void ejsDefineStringCFunction(EjsId eid, const char *functionName, 
809         MprStringCFunction fn, void *thisPtr, int flags)
810 {
811         if (eid < 0) {
812                 ejsLock();
813                 mprCreatePropertyValue(&master, functionName, 
814                         mprCreateStringCFunctionVar(fn, thisPtr, flags));
815                 ejsUnlock();
816         } else {
817                 ejsWriteVarValue(eid, functionName, 
818                         mprCreateStringCFunctionVar(fn, thisPtr, flags));
819         }
820 }
821
822 /******************************************************************************/
823 /*
824  *      Define a JavaScript function. Args should be comma separated.
825  *      Body should not contain braces.
826  */
827
828 void ejsDefineFunction(EjsId eid, const char *functionName, char *args, 
829         char *body)
830 {
831         MprVar          v;
832
833         v = mprCreateFunctionVar(args, body, 0);
834         if (eid < 0) {
835                 ejsLock();
836                 mprCreateProperty(&master, functionName, &v);
837                 ejsUnlock();
838         } else {
839                 ejsWriteVar(eid, functionName, &v);
840         }
841         mprDestroyVar(&v);
842 }
843
844 /******************************************************************************/
845
846 void *ejsGetThisPtr(EjsId eid)
847 {
848         Ejs             *ep;
849
850         if ((ep = ejsPtr(eid)) == NULL) {
851                 mprAssert(ep);
852                 return 0;
853         }
854         return ep->thisPtr;
855 }
856
857 /******************************************************************************/
858 /*
859  *      Find a variable given a variable name and return the parent object and 
860  *      the variable itself, the variable . This routine supports variable names
861  *      that may be objects or arrays but may NOT have expressions in the array
862  *      indicies. Returns -1 on errors or if the variable is not found.
863  */
864
865 int ejsGetVarCore(Ejs *ep, const char *vname, MprVar **obj, 
866         MprVar **varValue, int flags)
867 {
868         MprVar          *currentObj;
869         MprVar          *currentVar;
870         char            tokBuf[EJS_MAX_ID];
871         char            *propertyName, *token, *next, *cp, *varName;
872
873         if (obj) {
874                 *obj = 0;
875         }
876         if (varValue) {
877                 *varValue = 0;
878         }
879         currentObj = ejsFindObj(ep, 0, vname, flags);
880         currentVar = 0;
881         propertyName = 0;
882
883         next = varName = mprStrdup(vname);
884
885         token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
886
887         while (currentObj != 0 && token != 0 && *token) {
888                 
889                 if (*token == '[') {
890                         token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
891
892                         propertyName = token;
893                         if (*propertyName == '\"') {
894                                 propertyName++;
895                                 if ((cp = strchr(propertyName, '\"')) != 0) {
896                                         *cp = '\0';
897                                 }
898                         } else if (*propertyName == '\'') {
899                                 propertyName++;
900                                 if ((cp = strchr(propertyName, '\'')) != 0) {
901                                         *cp = '\0';
902                                 }
903                         }
904
905                         currentObj = currentVar;
906                         currentVar = ejsFindProperty(ep, 0, currentObj, propertyName, 0);
907
908                         token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
909                         if (*token != ']') {
910                                 mprFree(varName);
911                                 return -1;
912                         }
913
914                 } else if (*token == '.') {
915                         token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
916                         if (!isalpha((int) token[0]) && 
917                                         token[0] != '_' && token[0] != '$') {
918                                 mprFree(varName);
919                                 return -1;
920                         }
921
922                         propertyName = token;
923                         currentObj = currentVar;
924                         currentVar = ejsFindProperty(ep, 0, currentObj, token, 0);
925
926                 } else {
927                         currentVar = ejsFindProperty(ep, 0, currentObj, token, 0);
928                 }
929                 token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
930         }
931         mprFree(varName);
932
933         if (currentVar == 0 && currentObj >= 0 && flags & EJS_FLAGS_CREATE) {
934                 currentVar = mprCreatePropertyValue(currentObj, propertyName, 
935                         mprCreateUndefinedVar());
936         }
937         if (obj) {
938                 *obj = currentObj;
939         }
940         
941         /*
942          *      Don't use mprCopyVar as it will copy the data
943          */
944         if (varValue) {
945                 *varValue = currentVar;
946         }
947         return currentVar ? 0 : -1;
948 }
949
950 /******************************************************************************/
951 /*
952  *      Get the next token as part of a variable specification. This will return
953  *      a pointer to the next token and will return a pointer to the next token 
954  *      (after this one) in "next". The tokBuf holds the parsed token.
955  */
956 static char *getNextVarToken(char **next, char *tokBuf, int tokBufLen)
957 {
958         char    *start, *cp;
959         int             len;
960
961         start = *next;
962         while (isspace((int) *start) || *start == '\n' || *start == '\r') {
963                 start++;
964         }
965         cp = start;
966
967         if (*cp == '.' || *cp == '[' || *cp == ']') {
968                 cp++;
969         } else {
970                 while (*cp && *cp != '.' && *cp != '[' && *cp != ']' && 
971                                 !isspace((int) *cp) && *cp != '\n' && *cp != '\r') {
972                         cp++;
973                 }
974         }
975         len = mprMemcpy(tokBuf, tokBufLen - 1, start, cp - start);
976         tokBuf[len] = '\0';
977         
978         *next = cp;
979         return tokBuf;
980 }
981
982 /******************************************************************************/
983 /*
984  *      Get the EJS structure pointer
985  */
986
987 Ejs *ejsPtr(EjsId eid)
988 {
989         Ejs             *handle;
990         int             intId;
991
992         intId = (int) eid;
993
994         ejsLock();
995         mprAssert(0 <= intId && intId < ejsList->max);
996
997         if (intId < 0 || intId >= ejsList->max || ejsList->handles[intId] == NULL) {
998                 mprAssert(0);
999                 ejsUnlock();
1000                 return NULL;
1001         }
1002         handle = ejsList->handles[intId];
1003         ejsUnlock();
1004         return handle;
1005 }
1006
1007 /******************************************************************************/
1008 /*
1009  *      Utility routine to crack JavaScript arguments. Return the number of args
1010  *      seen. This routine only supports %s and %d type args.
1011  *
1012  *      Typical usage:
1013  *
1014  *              if (ejsParseArgs(argc, argv, "%s %d", &name, &age) < 2) {
1015  *                      mprError("Insufficient args\n");
1016  *                      return -1;
1017  *              }
1018  */ 
1019
1020 int ejsParseArgs(int argc, char **argv, char *fmt, ...)
1021 {
1022         va_list vargs;
1023         bool    *bp;
1024         char    *cp, **sp, *s;
1025         int             *ip, argn;
1026
1027         va_start(vargs, fmt);
1028
1029         if (argv == 0) {
1030                 return 0;
1031         }
1032
1033         for (argn = 0, cp = fmt; cp && *cp && argn < argc && argv[argn]; ) {
1034                 if (*cp++ != '%') {
1035                         continue;
1036                 }
1037
1038                 s = argv[argn];
1039                 switch (*cp) {
1040                 case 'b':
1041                         bp = va_arg(vargs, bool*);
1042                         if (bp) {
1043                                 if (strcmp(s, "true") == 0 || s[0] == '1') {
1044                                         *bp = 1;
1045                                 } else {
1046                                         *bp = 0;
1047                                 }
1048                         } else {
1049                                 *bp = 0;
1050                         }
1051                         break;
1052
1053                 case 'd':
1054                         ip = va_arg(vargs, int*);
1055                         *ip = atoi(s);
1056                         break;
1057
1058                 case 's':
1059                         sp = va_arg(vargs, char**);
1060                         *sp = s;
1061                         break;
1062
1063                 default:
1064                         mprAssert(0);
1065                 }
1066                 argn++;
1067         }
1068
1069         va_end(vargs);
1070         return argn;
1071 }
1072
1073 /******************************************************************************/
1074
1075 #else
1076 void ejsDummy() {}
1077
1078 /******************************************************************************/
1079 #endif /* BLD_FEATURE_EJS */
1080
1081 /******************************************************************************/
1082 /*
1083  * Local variables:
1084  * tab-width: 4
1085  * c-basic-offset: 4
1086  * End:
1087  * vim:tw=78
1088  * vim600: sw=4 ts=4 fdm=marker
1089  * vim<600: sw=4 ts=4
1090  */