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