r8397: merged an upstream fix for the expression bug tpot found yesterday
[bbaumbach/samba-autobuild/.git] / source4 / lib / 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 /****************************** 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", (int) 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 static void ejsErrorCore(Ejs* ep, const char *fmt, va_list args) 
472         PRINTF_ATTRIBUTE(2, 0);
473
474 static void ejsErrorCore(Ejs* ep, const char *fmt, va_list args)
475 {
476         EjsInput        *ip;
477         char            *errbuf, *msgbuf;
478
479         mprAssert(ep);
480         mprAssert(args);
481
482         msgbuf = NULL;
483         mprAllocVsprintf(&msgbuf, MPR_MAX_STRING, fmt, args);
484
485         if (ep) {
486                 ip = ep->input;
487                 if (ip) {
488                         mprAllocSprintf(&errbuf, MPR_MAX_STRING,
489                                 "%s\nError on line %d. Offending line: %s\n\n",
490                                 msgbuf, ip->lineNumber, ip->line);
491                 } else {
492                         mprAllocSprintf(&errbuf, MPR_MAX_STRING, "%s\n", msgbuf);
493                 }
494                 mprFree(ep->error);
495                 ep->error = errbuf;
496         }
497         mprFree(msgbuf);
498 }
499
500 /******************************************************************************/
501 /*
502  *      Internal use function to set the error message
503  */
504
505 void ejsError(Ejs* ep, const char* fmt, ...)
506 {
507         va_list         args;
508
509         va_start(args, fmt);
510         ejsErrorCore(ep, fmt, args);
511         va_end(args);
512 }
513
514 /******************************************************************************/
515 /*
516  *      Public routine to set the error message
517  */
518
519 void ejsSetErrorMsg(EjsId eid, const char* fmt, ...)
520 {
521         va_list         args;
522         Ejs                     *ep;
523
524         if ((ep = ejsPtr(eid)) == NULL) {
525                 mprAssert(ep);
526                 return;
527         }
528         va_start(args, fmt);
529         ejsErrorCore(ep, fmt, args);
530         va_end(args);
531 }
532
533 /******************************************************************************/
534 /*
535  *      Get the current line number
536  */
537
538 int ejsGetLineNumber(EjsId eid)
539 {
540         Ejs             *ep;
541
542         if ((ep = ejsPtr(eid)) == NULL) {
543                 mprAssert(ep);
544                 return -1;
545         }
546         return ep->input->lineNumber;
547 }
548
549 /******************************************************************************/
550 /*
551  *      Return the local object
552  */
553
554 MprVar *ejsGetLocalObject(EjsId eid)
555 {
556         Ejs             *ep;
557
558         if ((ep = ejsPtr(eid)) == NULL) {
559                 mprAssert(ep);
560                 return 0; 
561         }
562         return ep->local;
563 }
564
565 /******************************************************************************/
566 /*
567  *      Return the global object
568  */
569
570 MprVar *ejsGetGlobalObject(EjsId eid)
571 {
572         Ejs             *ep;
573
574         if ((ep = ejsPtr(eid)) == NULL) {
575                 mprAssert(ep);
576                 return 0;
577         }
578         return ep->global;
579 }
580
581 /******************************************************************************/
582 /*
583  *      Copy the value of an object property. Return value is in "value".
584  *      If deepCopy is true, copy all object/strings. Otherwise, object reference
585  *      counts are incremented. Callers must always call mprDestroyVar on the 
586  *      return value to prevent leaks.
587  *
588  *      Returns: -1 on errors or if the variable is not found.
589  */
590
591 int ejsCopyVar(EjsId eid, const char *var, MprVar *value, bool deepCopy)
592 {
593         Ejs                     *ep;
594         MprVar          *vp;
595
596         mprAssert(var && *var);
597         mprAssert(value);
598
599         if ((ep = ejsPtr(eid)) == NULL) {
600                 mprAssert(ep);
601                 return -1;
602         }
603
604         if (ejsGetVarCore(ep, var, 0, &vp, 0) < 0) {
605                 return -1;
606         }
607
608         return mprCopyProperty(value, vp, deepCopy);
609 }
610
611 /******************************************************************************/
612 /*
613  *      Return the value of an object property. Return value is in "value".
614  *      Objects and strings are not copied and reference counts are not modified.
615  *      Callers should NOT call mprDestroyVar. Returns: -1 on errors or if the 
616  *      variable is not found.
617  */
618
619 int ejsReadVar(EjsId eid, const char *var, MprVar *value)
620 {
621         Ejs                     *ep;
622         MprVar          *vp;
623
624         mprAssert(var && *var);
625         mprAssert(value);
626
627         if ((ep = ejsPtr(eid)) == NULL) {
628                 mprAssert(ep);
629                 return -1;
630         }
631
632         if (ejsGetVarCore(ep, var, 0, &vp, 0) < 0) {
633                 return -1;
634         }
635
636         return mprReadProperty(vp, value);
637 }
638
639 /******************************************************************************/
640 /*
641  *      Set a variable that may be an arbitrarily complex object or array reference.
642  *      Will always define in the top most variable frame.
643  */
644
645 int ejsWriteVar(EjsId eid, const char *var, MprVar *value)
646 {
647         Ejs                     *ep;
648         MprVar          *vp;
649
650         mprAssert(var && *var);
651
652         if ((ep = ejsPtr(eid)) == NULL) {
653                 mprAssert(ep);
654                 return -1;
655         }
656
657         if (ejsGetVarCore(ep, var, 0, &vp, EJS_FLAGS_CREATE) < 0) {
658                 return -1;
659         }
660         mprAssert(vp);
661
662         /*
663          *      Only copy the value. Don't overwrite the object's name
664          */
665         mprWriteProperty(vp, value);
666
667         return 0;
668 }
669
670 /******************************************************************************/
671 /*
672  *      Set a variable that may be an arbitrarily complex object or array reference.
673  *      Will always define in the top most variable frame.
674  */
675
676 int ejsWriteVarValue(EjsId eid, const char *var, MprVar value)
677 {
678         return ejsWriteVar(eid, var, &value);
679 }
680
681 /******************************************************************************/
682 /*
683  *      Delete a variable
684  */
685
686 int ejsDeleteVar(EjsId eid, const char *var)
687 {
688         Ejs                     *ep;
689         MprVar          *vp;
690         MprVar          *obj;
691
692         if ((ep = ejsPtr(eid)) == NULL) {
693                 mprAssert(ep);
694                 return -1;
695         }
696         if (ejsGetVarCore(ep, var, &obj, &vp, 0) < 0) {
697                 return -1;
698         }
699         mprDeleteProperty(obj, vp->name);
700         return 0;
701 }
702
703 /******************************************************************************/
704 /*
705  *      Set the expression return value
706  */
707
708 void ejsSetReturnValue(EjsId eid, MprVar value)
709 {
710         Ejs             *ep;
711
712         if ((ep = ejsPtr(eid)) == NULL) {
713                 mprAssert(ep);
714                 return;
715         }
716         mprCopyVar(&ep->result, &value, MPR_SHALLOW_COPY);
717 }
718
719 /******************************************************************************/
720 /*
721  *      Set the expression return value to a string value
722  */
723
724 void ejsSetReturnString(EjsId eid, const char *str)
725 {
726         Ejs             *ep;
727
728         if ((ep = ejsPtr(eid)) == NULL) {
729                 mprAssert(ep);
730                 return;
731         }
732         mprCopyVarValue(&ep->result, mprCreateStringVar(str, 0), MPR_SHALLOW_COPY);
733 }
734
735 /******************************************************************************/
736 /*
737  *      Get the expression return value
738  */
739
740 MprVar *ejsGetReturnValue(EjsId eid)
741 {
742         Ejs             *ep;
743
744         if ((ep = ejsPtr(eid)) == NULL) {
745                 mprAssert(ep);
746                 return 0;
747         }
748         return &ep->result;
749 }
750
751 /******************************************************************************/
752 /*
753  *      Define a C function. If eid < 0, then update the master object with this
754  *      function. NOTE: in this case, functionName must be simple without any "." or
755  *      "[]" elements. If eid >= 0, add to the specified script engine. In this
756  *      case, functionName can be an arbitrary object reference and can contain "."
757  *      or "[]".  
758  */
759
760 void ejsDefineCFunction(EjsId eid, const char *functionName, MprCFunction fn, 
761         void *thisPtr, int flags)
762 {
763         if (eid < 0) {
764                 ejsLock();
765                 mprCreatePropertyValue(&master, functionName, 
766                         mprCreateCFunctionVar(fn, thisPtr, flags));
767                 ejsUnlock();
768         } else {
769                 ejsWriteVarValue(eid, functionName, 
770                         mprCreateCFunctionVar(fn, thisPtr, flags));
771         }
772 }
773
774 /******************************************************************************/
775 /*
776  *      Define a C function with String arguments
777  */
778
779 void ejsDefineStringCFunction(EjsId eid, const char *functionName, 
780         MprStringCFunction fn, void *thisPtr, int flags)
781 {
782         if (eid < 0) {
783                 ejsLock();
784                 mprCreatePropertyValue(&master, functionName, 
785                         mprCreateStringCFunctionVar(fn, thisPtr, flags));
786                 ejsUnlock();
787         } else {
788                 ejsWriteVarValue(eid, functionName, 
789                         mprCreateStringCFunctionVar(fn, thisPtr, flags));
790         }
791 }
792
793 /******************************************************************************/
794 /*
795  *      Define a JavaScript function. Args should be comma separated.
796  *      Body should not contain braces.
797  */
798
799 void ejsDefineFunction(EjsId eid, const char *functionName, char *args, 
800         char *body)
801 {
802         MprVar          v;
803
804         v = mprCreateFunctionVar(args, body, 0);
805         if (eid < 0) {
806                 ejsLock();
807                 mprCreateProperty(&master, functionName, &v);
808                 ejsUnlock();
809         } else {
810                 ejsWriteVar(eid, functionName, &v);
811         }
812         mprDestroyVar(&v);
813 }
814
815 /******************************************************************************/
816
817 void *ejsGetThisPtr(EjsId eid)
818 {
819         Ejs             *ep;
820
821         if ((ep = ejsPtr(eid)) == NULL) {
822                 mprAssert(ep);
823                 return 0;
824         }
825         return ep->thisPtr;
826 }
827
828 /******************************************************************************/
829 /*
830  *      Find a variable given a variable name and return the parent object and 
831  *      the variable itself, the variable . This routine supports variable names
832  *      that may be objects or arrays but may NOT have expressions in the array
833  *      indicies. Returns -1 on errors or if the variable is not found.
834  */
835
836 int ejsGetVarCore(Ejs *ep, const char *vname, MprVar **obj, 
837         MprVar **varValue, int flags)
838 {
839         MprVar          *currentObj;
840         MprVar          *currentVar;
841         char            tokBuf[EJS_MAX_ID];
842         char            *propertyName, *token, *next, *cp, *varName;
843
844         if (obj) {
845                 *obj = 0;
846         }
847         if (varValue) {
848                 *varValue = 0;
849         }
850         currentObj = ejsFindObj(ep, 0, vname, flags);
851         currentVar = 0;
852         propertyName = 0;
853
854         next = varName = mprStrdup(vname);
855
856         token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
857
858         while (currentObj != 0 && token != 0 && *token) {
859                 
860                 if (*token == '[') {
861                         token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
862
863                         propertyName = token;
864                         if (*propertyName == '\"') {
865                                 propertyName++;
866                                 if ((cp = strchr(propertyName, '\"')) != 0) {
867                                         *cp = '\0';
868                                 }
869                         } else if (*propertyName == '\'') {
870                                 propertyName++;
871                                 if ((cp = strchr(propertyName, '\'')) != 0) {
872                                         *cp = '\0';
873                                 }
874                         }
875
876                         currentObj = currentVar;
877                         currentVar = ejsFindProperty(ep, 0, currentObj, propertyName, 0);
878
879                         token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
880                         if (*token != ']') {
881                                 mprFree(varName);
882                                 return -1;
883                         }
884
885                 } else if (*token == '.') {
886                         token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
887                         if (!isalpha((int) token[0]) && 
888                                         token[0] != '_' && token[0] != '$') {
889                                 mprFree(varName);
890                                 return -1;
891                         }
892
893                         propertyName = token;
894                         currentObj = currentVar;
895                         currentVar = ejsFindProperty(ep, 0, currentObj, token, 0);
896
897                 } else {
898                         currentVar = ejsFindProperty(ep, 0, currentObj, token, 0);
899                 }
900                 token = getNextVarToken(&next, tokBuf, sizeof(tokBuf));
901         }
902         mprFree(varName);
903
904         if (currentVar == 0 && currentObj >= 0 && flags & EJS_FLAGS_CREATE) {
905                 currentVar = mprCreatePropertyValue(currentObj, propertyName, 
906                         mprCreateUndefinedVar());
907         }
908         if (obj) {
909                 *obj = currentObj;
910         }
911         
912         /*
913          *      Don't use mprCopyVar as it will copy the data
914          */
915         if (varValue) {
916                 *varValue = currentVar;
917         }
918         return currentVar ? 0 : -1;
919 }
920
921 /******************************************************************************/
922 /*
923  *      Get the next token as part of a variable specification. This will return
924  *      a pointer to the next token and will return a pointer to the next token 
925  *      (after this one) in "next". The tokBuf holds the parsed token.
926  */
927 static char *getNextVarToken(char **next, char *tokBuf, int tokBufLen)
928 {
929         char    *start, *cp;
930         int             len;
931
932         start = *next;
933         while (isspace((int) *start) || *start == '\n' || *start == '\r') {
934                 start++;
935         }
936         cp = start;
937
938         if (*cp == '.' || *cp == '[' || *cp == ']') {
939                 cp++;
940         } else {
941                 while (*cp && *cp != '.' && *cp != '[' && *cp != ']' && 
942                                 !isspace((int) *cp) && *cp != '\n' && *cp != '\r') {
943                         cp++;
944                 }
945         }
946         len = mprMemcpy(tokBuf, tokBufLen - 1, start, cp - start);
947         tokBuf[len] = '\0';
948         
949         *next = cp;
950         return tokBuf;
951 }
952
953 /******************************************************************************/
954 /*
955  *      Get the EJS structure pointer
956  */
957
958 Ejs *ejsPtr(EjsId eid)
959 {
960         Ejs             *handle;
961         int             intId;
962
963         intId = (int) eid;
964
965         ejsLock();
966         mprAssert(0 <= intId && intId < ejsList->max);
967
968         if (intId < 0 || intId >= ejsList->max || ejsList->handles[intId] == NULL) {
969                 mprAssert(0);
970                 ejsUnlock();
971                 return NULL;
972         }
973         handle = ejsList->handles[intId];
974         ejsUnlock();
975         return handle;
976 }
977
978 /******************************************************************************/
979 /*
980  *      Utility routine to crack JavaScript arguments. Return the number of args
981  *      seen. This routine only supports %s and %d type args.
982  *
983  *      Typical usage:
984  *
985  *              if (ejsParseArgs(argc, argv, "%s %d", &name, &age) < 2) {
986  *                      mprError("Insufficient args\n");
987  *                      return -1;
988  *              }
989  */ 
990
991 int ejsParseArgs(int argc, char **argv, char *fmt, ...)
992 {
993         va_list vargs;
994         bool    *bp;
995         char    *cp, **sp, *s;
996         int             *ip, argn;
997
998         va_start(vargs, fmt);
999
1000         if (argv == 0) {
1001                 return 0;
1002         }
1003
1004         for (argn = 0, cp = fmt; cp && *cp && argn < argc && argv[argn]; ) {
1005                 if (*cp++ != '%') {
1006                         continue;
1007                 }
1008
1009                 s = argv[argn];
1010                 switch (*cp) {
1011                 case 'b':
1012                         bp = va_arg(vargs, bool*);
1013                         if (bp) {
1014                                 if (strcmp(s, "true") == 0 || s[0] == '1') {
1015                                         *bp = 1;
1016                                 } else {
1017                                         *bp = 0;
1018                                 }
1019                         } else {
1020                                 *bp = 0;
1021                         }
1022                         break;
1023
1024                 case 'd':
1025                         ip = va_arg(vargs, int*);
1026                         *ip = atoi(s);
1027                         break;
1028
1029                 case 's':
1030                         sp = va_arg(vargs, char**);
1031                         *sp = s;
1032                         break;
1033
1034                 default:
1035                         mprAssert(0);
1036                 }
1037                 argn++;
1038         }
1039
1040         va_end(vargs);
1041         return argn;
1042 }
1043
1044 /******************************************************************************/
1045
1046 #else
1047 void ejsDummy() {}
1048
1049 /******************************************************************************/
1050 #endif /* BLD_FEATURE_EJS */
1051
1052 /******************************************************************************/
1053 /*
1054  * Local variables:
1055  * tab-width: 4
1056  * c-basic-offset: 4
1057  * End:
1058  * vim:tw=78
1059  * vim600: sw=4 ts=4 fdm=marker
1060  * vim<600: sw=4 ts=4
1061  */