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