source: rtems/c/src/libnetworking/rtems_webserver/ejparse.c @ cd9dac0

Last change on this file since cd9dac0 was a6b4c0df, checked in by Joel Sherrill <joel.sherrill@…>, on 09/01/00 at 10:57:21

2000-08-30 Joel Sherrill <joel@…>

  • Merged version 2.1 of GoAhead? webserver. This update was submitted by Antti P Miettinen <antti.p.miettinen@…>.
  • NOTES, base64.c, ejIntrn.h, emfdb.c, emfdb.h, md5.h, md5c.c, um.c, um.h: New files.
  • wbase64.c: Removed.
  • Makefile.am, asp.c, balloc.c, default.c, ej.h, ejlex.c, ejparse.c, form.c, h.c, handler.c, mime.c, misc.c, ringq.c, rom.c, security.c, socket.c, sym.c, uemf.c, uemf.h, url.c, value.c, webcomp.c, webmain.c, webpage.c, webrom.c, webs.c, webs.h, websuemf.c, wsIntrn.h: Modified.
  • Property mode set to 100644
File size: 35.6 KB
Line 
1/*
2 * ejparse.c -- Ejscript(TM) Parser
3 *
4 * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved.
5 *
6 * See the file "license.txt" for usage and redistribution license requirements
7 */
8
9/******************************** Description *********************************/
10
11/*
12 *      Ejscript parser. This implementes a subset of the JavaScript language.
13 *      Multiple Ejscript parsers can be opened at a time.
14 */
15
16/********************************** Includes **********************************/
17
18#include        "ejIntrn.h"
19
20#if CE
21        #include        "CE/wincompat.h"
22#endif
23
24/********************************** Local Data ********************************/
25
26ej_t                    **ejHandles;                                                    /* List of ej handles */
27int                             ejMax = -1;                                                             /* Maximum size of      */
28
29/****************************** Forward Declarations **************************/
30
31#ifndef B_STATS
32#define setString(a,b,c)         setstring(b,c)
33#endif
34
35static ej_t             *ejPtr(int eid);
36static void             clearString(char_t **ptr);
37static void             setString(B_ARGS_DEC, char_t **ptr, char_t *s);
38static void             appendString(char_t **ptr, char_t *s);
39static int              parse(ej_t *ep, int state, int flags);
40static int              parseStmt(ej_t *ep, int state, int flags);
41static int              parseDeclaration(ej_t *ep, int state, int flags);
42static int              parseArgs(ej_t *ep, int state, int flags);
43static int              parseCond(ej_t *ep, int state, int flags);
44static int              parseExpr(ej_t *ep, int state, int flags);
45static int              evalExpr(ej_t *ep, char_t *lhs, int rel, char_t *rhs);
46static int              evalCond(ej_t *ep, char_t *lhs, int rel, char_t *rhs);
47static int              evalFunction(ej_t *ep);
48static void             freeFunc(ejfunc_t *func);
49static void             ejRemoveNewlines(ej_t *ep, int state);
50
51/************************************* Code ***********************************/
52/*
53 *      Initialize a Ejscript engine
54 */
55
56int ejOpenEngine(sym_fd_t variables, sym_fd_t functions)
57{
58        ej_t    *ep;
59        int             eid, vid;
60
61        if ((eid = hAllocEntry((void***) &ejHandles, &ejMax, sizeof(ej_t))) < 0) {
62                return -1;
63        }
64        ep = ejHandles[eid];
65        ep->eid = eid;
66
67/*
68 *      Create a top level symbol table if one is not provided for variables and
69 *      functions. Variables may create other symbol tables for block level
70 *      declarations so we use hAlloc to manage a list of variable tables.
71 */
72        if ((vid = hAlloc((void***) &ep->variables)) < 0) {
73                ejMax = hFree((void***) &ejHandles, ep->eid);
74                return -1;
75        }
76        if (vid >= ep->variableMax) {
77                ep->variableMax = vid + 1;
78        }
79
80        if (variables == -1) {
81                ep->variables[vid] = symOpen(64) + EJ_OFFSET;
82                ep->flags |= FLAGS_VARIABLES;
83        } else {
84                ep->variables[vid] = variables + EJ_OFFSET;
85        }
86
87        if (functions == -1) {
88                ep->functions = symOpen(64);
89                ep->flags |= FLAGS_FUNCTIONS;
90        } else {
91                ep->functions = functions;
92        }
93
94        ejLexOpen(ep);
95
96/*
97 *      Define standard constants
98 */
99        ejSetGlobalVar(ep->eid, T("null"), NULL);
100
101#if EMF
102        ejEmfOpen(ep->eid);
103#endif
104        return ep->eid;
105}
106
107/******************************************************************************/
108/*
109 *      Close
110 */
111
112void ejCloseEngine(int eid)
113{
114        ej_t    *ep;
115        int             i;
116
117        if ((ep = ejPtr(eid)) == NULL) {
118                return;
119        }
120
121#if EMF
122        ejEmfClose(eid);
123#endif
124
125        bfreeSafe(B_L, ep->error);
126        ep->error = NULL;
127        bfreeSafe(B_L, ep->result);
128        ep->result = NULL;
129
130        ejLexClose(ep);
131
132        for (i = ep->variableMax - 1; i >= 0; i--) {
133                if (ep->flags & FLAGS_VARIABLES) {
134                        symClose(ep->variables[i] - EJ_OFFSET);
135                }
136                ep->variableMax = hFree((void***) &ep->variables, i);
137        }
138
139        if (ep->flags & FLAGS_FUNCTIONS) {
140                symClose(ep->functions);
141        }
142
143        ejMax = hFree((void***) &ejHandles, ep->eid);
144        bfree(B_L, ep);
145}
146
147#ifndef __NO_EJ_FILE
148/******************************************************************************/
149/*
150 *      Evaluate a Ejscript file
151 */
152
153char_t *ejEvalFile(int eid, char_t *path, char_t **emsg)
154{
155        gstat_t sbuf;
156        ej_t    *ep;
157        char_t  *script, *rs;
158        char    *fileBuf;
159        int             fd;
160
161        a_assert(path && *path);
162
163        if (emsg) {
164                *emsg = NULL;
165        }
166
167        if ((ep = ejPtr(eid)) == NULL) {
168                return NULL;
169        }
170
171        if ((fd = gopen(path, O_RDONLY | O_BINARY, 0666)) < 0) {
172                ejError(ep, T("Bad handle %d"), eid);
173                return NULL;
174        }
175       
176        if (gstat(path, &sbuf) < 0) {
177                gclose(fd);
178                ejError(ep, T("Cant stat %s"), path);
179                return NULL;
180        }
181       
182        if ((fileBuf = balloc(B_L, sbuf.st_size + 1)) == NULL) {
183                gclose(fd);
184                ejError(ep, T("Cant malloc %d"), sbuf.st_size);
185                return NULL;
186        }
187       
188        if (gread(fd, fileBuf, sbuf.st_size) != (int)sbuf.st_size) {
189                gclose(fd);
190                bfree(B_L, fileBuf);
191                ejError(ep, T("Error reading %s"), path);
192                return NULL;
193        }
194       
195        fileBuf[sbuf.st_size] = '\0';
196        gclose(fd);
197
198        if ((script = ballocAscToUni(fileBuf, sbuf.st_size)) == NULL) {
199                bfree(B_L, fileBuf);
200                ejError(ep, T("Cant malloc %d"), sbuf.st_size + 1);
201                return NULL;
202        }
203        bfree(B_L, fileBuf);
204
205        rs = ejEvalBlock(eid, script, emsg);
206
207        bfree(B_L, script);
208        return rs;
209}
210#endif /* __NO_EJ_FILE */
211
212/******************************************************************************/
213/*
214 *      Create a new variable scope block so that consecutive ejEval calls may
215 *      be made with the same varible scope. This space MUST be closed with
216 *      ejCloseBlock when the evaluations are complete.
217 */
218
219int ejOpenBlock(int eid)
220{
221        ej_t    *ep;
222        int             vid;
223
224        if((ep = ejPtr(eid)) == NULL) {
225                return -1;
226        }
227
228        if ((vid = hAlloc((void***) &ep->variables)) < 0) {
229                return -1;
230        }
231
232        if (vid >= ep->variableMax) {
233                ep->variableMax = vid + 1;
234        }
235        ep->variables[vid] = symOpen(64) + EJ_OFFSET;
236        return vid;
237
238}
239
240/******************************************************************************/
241/*
242 *      Close a variable scope block. The vid parameter is the return value from
243 *      the call to ejOpenBlock
244 */
245
246int ejCloseBlock(int eid, int vid)
247{
248        ej_t    *ep;
249
250        if((ep = ejPtr(eid)) == NULL) {
251                return -1;
252        }
253        symClose(ep->variables[vid] - EJ_OFFSET);
254        ep->variableMax = hFree((void***) &ep->variables, vid);
255        return 0;
256
257}
258
259/******************************************************************************/
260/*
261 *      Create a new variable scope block and evaluate a script. All variables
262 *      created during this context will be automatically deleted when complete.
263 */
264
265char_t *ejEvalBlock(int eid, char_t *script, char_t **emsg)
266{
267        char_t* returnVal;
268        int             vid;
269
270        a_assert(script);
271
272        vid = ejOpenBlock(eid);
273        returnVal = ejEval(eid, script, emsg);
274        ejCloseBlock(eid, vid);
275
276        return returnVal;
277}
278
279/******************************************************************************/
280/*
281 *      Parse and evaluate a Ejscript. The caller may provide a symbol table to
282 *      use for variables and function definitions. Return char_t pointer on
283 *      success otherwise NULL pointer is returned.
284 */
285
286char_t *ejEval(int eid, char_t *script, char_t **emsg)
287{
288        ej_t    *ep;
289        ejinput_t       *oldBlock;
290        int             state;
291        void    *endlessLoopTest;
292        int             loopCounter;
293       
294       
295        a_assert(script);
296
297        if (emsg) {
298                *emsg = NULL;
299        }
300
301        if ((ep = ejPtr(eid)) == NULL) {
302                return NULL;
303        }
304
305        setString(B_L, &ep->result, T(""));
306
307/*
308 *      Allocate a new evaluation block, and save the old one
309 */
310        oldBlock = ep->input;
311        ejLexOpenScript(ep, script);
312
313/*
314 *      Do the actual parsing and evaluation
315 */
316        loopCounter = 0;
317        endlessLoopTest = NULL;
318
319        do {
320                state = parse(ep, STATE_BEGIN, FLAGS_EXE);
321
322                if (state == STATE_RET) {
323                        state = STATE_EOF;
324                }
325/*
326 *              prevent parser from going into infinite loop.  If parsing the same
327 *              line 10 times then fail and report Syntax error.  Most normal error
328 *              are caught in the parser itself.
329 */
330                if (endlessLoopTest == ep->input->script.servp) {
331                        if (loopCounter++ > 10) {
332                                state = STATE_ERR;
333                                ejError(ep, T("Syntax error"));
334                        }
335                } else {
336                        endlessLoopTest = ep->input->script.servp;
337                        loopCounter = 0;
338                }
339        } while (state != STATE_EOF && state != STATE_ERR);
340
341        ejLexCloseScript(ep);
342
343/*
344 *      Return any error string to the user
345 */
346        if (state == STATE_ERR && emsg) {
347                *emsg = bstrdup(B_L, ep->error);
348        }
349
350/*
351 *      Restore the old evaluation block
352 */
353        ep->input = oldBlock;
354
355        if (state == STATE_EOF) {
356                return ep->result;
357        }
358
359        if (state == STATE_ERR) {
360                return NULL;
361        }
362
363        return ep->result;
364}
365
366/******************************************************************************/
367/*
368 *      Recursive descent parser for Ejscript
369 */
370
371static int parse(ej_t *ep, int state, int flags)
372{
373        a_assert(ep);
374
375        switch (state) {
376/*
377 *      Any statement, function arguments or conditional expressions
378 */
379        case STATE_STMT:
380                if ((state = parseStmt(ep, state, flags)) != STATE_STMT_DONE &&
381                        state != STATE_EOF && state != STATE_STMT_BLOCK_DONE &&
382                        state != STATE_RET) {
383                        state = STATE_ERR;
384                }
385                break;
386
387        case STATE_DEC:
388                if ((state = parseStmt(ep, state, flags)) != STATE_DEC_DONE &&
389                        state != STATE_EOF) {
390                        state = STATE_ERR;
391                }
392                break;
393
394        case STATE_EXPR:
395                if ((state = parseStmt(ep, state, flags)) != STATE_EXPR_DONE &&
396                        state != STATE_EOF) {
397                        state = STATE_ERR;
398                }
399                break;
400
401/*
402 *      Variable declaration list
403 */
404        case STATE_DEC_LIST:
405                state = parseDeclaration(ep, state, flags);
406                break;
407
408/*
409 *      Function argument string
410 */
411        case STATE_ARG_LIST:
412                state = parseArgs(ep, state, flags);
413                break;
414
415/*
416 *      Logical condition list (relational operations separated by &&, ||)
417 */
418        case STATE_COND:
419                state = parseCond(ep, state, flags);
420                break;
421
422/*
423 *      Expression list
424 */
425        case STATE_RELEXP:
426                state = parseExpr(ep, state, flags);
427                break;
428        }
429
430        if (state == STATE_ERR && ep->error == NULL) {
431                ejError(ep, T("Syntax error"));
432        }
433        return state;
434}
435
436/******************************************************************************/
437/*
438 *      Parse any statement including functions and simple relational operations
439 */
440
441static int parseStmt(ej_t *ep, int state, int flags)
442{
443        ejfunc_t        func;
444        ejfunc_t        *saveFunc;
445        ejinput_t       condScript, endScript, bodyScript, incrScript;
446        char_t          *value, *identifier;
447        int                     done, expectSemi, thenFlags, elseFlags, tid, cond, forFlags;
448        int                     ejVarType;
449
450        a_assert(ep);
451
452/*
453 *      Set these to NULL, else we try to free them if an error occurs.
454 */
455        endScript.putBackToken = NULL;
456        bodyScript.putBackToken = NULL;
457        incrScript.putBackToken = NULL;
458        condScript.putBackToken = NULL;
459
460        expectSemi = 0;
461        saveFunc = NULL;
462
463        for (done = 0; !done; ) {
464                tid = ejLexGetToken(ep, state);
465
466                switch (tid) {
467                default:
468                        ejLexPutbackToken(ep, TOK_EXPR, ep->token);
469                        done++;
470                        break;
471
472                case TOK_ERR:
473                        state = STATE_ERR;
474                        done++;
475                        break;
476
477                case TOK_EOF:
478                        state = STATE_EOF;
479                        done++;
480                        break;
481
482                case TOK_NEWLINE:
483                        break;
484
485                case TOK_SEMI:
486/*
487 *                      This case is when we discover no statement and just a lone ';'
488 */
489                        if (state != STATE_STMT) {
490                                ejLexPutbackToken(ep, tid, ep->token);
491                        }
492                        done++;
493                        break;
494
495                case TOK_ID:
496/*
497 *                      This could either be a reference to a variable or an assignment
498 */
499                        identifier = NULL;
500                        setString(B_L, &identifier, ep->token);
501/*
502 *                      Peek ahead to see if this is an assignment
503 */
504                        tid = ejLexGetToken(ep, state);
505                        if (tid == TOK_ASSIGNMENT) {
506                                if (parse(ep, STATE_RELEXP, flags) != STATE_RELEXP_DONE) {
507                                        clearString(&identifier);
508                                        goto error;
509                                }
510                                if (flags & FLAGS_EXE) {
511                                        if ( state == STATE_DEC ) {
512                                                ejSetLocalVar(ep->eid, identifier, ep->result);
513                                        } else {
514                                                ejVarType = ejGetVar(ep->eid, identifier, &value);
515                                                if (ejVarType > 0) {
516                                                        ejSetLocalVar(ep->eid, identifier, ep->result);
517                                                } else {
518                                                        ejSetGlobalVar(ep->eid, identifier, ep->result);
519                                                }
520                                        }
521                                }
522
523                        } else if (tid == TOK_INC_DEC ) {
524                                value = NULL;
525                                if (flags & FLAGS_EXE) {
526                                        ejVarType = ejGetVar(ep->eid, identifier, &value);
527                                        if (ejVarType < 0) {
528                                                ejError(ep, T("Undefined variable %s\n"), identifier);
529                                                goto error;
530                                        }
531                                        setString(B_L, &ep->result, value);
532                                        if (evalExpr(ep, value, (int) *ep->token, T("1")) < 0) {
533                                                state = STATE_ERR;
534                                                break;
535                                        }
536
537                                        if (ejVarType > 0) {
538                                                ejSetLocalVar(ep->eid, identifier, ep->result);
539                                        } else {
540                                                ejSetGlobalVar(ep->eid, identifier, ep->result);
541                                        }
542                                }
543
544                        } else {
545/*
546 *                              If we are processing a declaration, allow undefined vars
547 */
548                                value = NULL;
549                                if (state == STATE_DEC) {
550                                        if (ejGetVar(ep->eid, identifier, &value) > 0) {
551                                                ejError(ep, T("Variable already declared"),
552                                                        identifier);
553                                                clearString(&identifier);
554                                                goto error;
555                                        }
556                                        ejSetLocalVar(ep->eid, identifier, NULL);
557                                } else {
558                                        if ( flags & FLAGS_EXE ) {
559                                                if (ejGetVar(ep->eid, identifier, &value) < 0) {
560                                                        ejError(ep, T("Undefined variable %s\n"),
561                                                                identifier);
562                                                        clearString(&identifier);
563                                                        goto error;
564                                                }
565                                        }
566                                }
567                                setString(B_L, &ep->result, value);
568                                ejLexPutbackToken(ep, tid, ep->token);
569                        }
570                        clearString(&identifier);
571
572                        if (state == STATE_STMT) {
573                                expectSemi++;
574                        }
575                        done++;
576                        break;
577
578                case TOK_LITERAL:
579/*
580 *                      Set the result to the literal (number or string constant)
581 */
582                        setString(B_L, &ep->result, ep->token);
583                        if (state == STATE_STMT) {
584                                expectSemi++;
585                        }
586                        done++;
587                        break;
588
589                case TOK_FUNCTION:
590/*
591 *                      We must save any current ep->func value for the current stack frame
592 */
593                        if (ep->func) {
594                                saveFunc = ep->func;
595                        }
596                        memset(&func, 0, sizeof(ejfunc_t));
597                        setString(B_L, &func.fname, ep->token);
598                        ep->func = &func;
599
600                        setString(B_L, &ep->result, T(""));
601                        if (ejLexGetToken(ep, state) != TOK_LPAREN) {
602                                freeFunc(&func);
603                                goto error;
604                        }
605
606                        if (parse(ep, STATE_ARG_LIST, flags) != STATE_ARG_LIST_DONE) {
607                                freeFunc(&func);
608                                ep->func = saveFunc;
609                                goto error;
610                        }
611/*
612 *                      Evaluate the function if required
613 */
614                        if (flags & FLAGS_EXE && evalFunction(ep) < 0) {
615                                freeFunc(&func);
616                                ep->func = saveFunc;
617                                goto error;
618                        }
619
620                        freeFunc(&func);
621                        ep->func = saveFunc;
622
623                        if (ejLexGetToken(ep, state) != TOK_RPAREN) {
624                                goto error;
625                        }
626                        if (state == STATE_STMT) {
627                                expectSemi++;
628                        }
629                        done++;
630                        break;
631
632                case TOK_IF:
633                        if (state != STATE_STMT) {
634                                goto error;
635                        }
636                        if (ejLexGetToken(ep, state) != TOK_LPAREN) {
637                                goto error;
638                        }
639/*
640 *                      Evaluate the entire condition list "(condition)"
641 */
642                        if (parse(ep, STATE_COND, flags) != STATE_COND_DONE) {
643                                goto error;
644                        }
645                        if (ejLexGetToken(ep, state) != TOK_RPAREN) {
646                                goto error;
647                        }
648/*
649 *                      This is the "then" case. We need to always parse both cases and
650 *                      execute only the relevant case.
651 */
652                        if (*ep->result == '1') {
653                                thenFlags = flags;
654                                elseFlags = flags & ~FLAGS_EXE;
655                        } else {
656                                thenFlags = flags & ~FLAGS_EXE;
657                                elseFlags = flags;
658                        }
659/*
660 *                      Process the "then" case.  Allow for RETURN statement
661 */
662                        switch (parse(ep, STATE_STMT, thenFlags)) {
663                        case STATE_RET:
664                                return STATE_RET;
665                        case STATE_STMT_DONE:
666                                break;
667                        default:
668                                goto error;
669                        }
670/*
671 *                      check to see if there is an "else" case
672 */
673                        ejRemoveNewlines(ep, state);
674                        tid = ejLexGetToken(ep, state);
675                        if (tid != TOK_ELSE) {
676                                ejLexPutbackToken(ep, tid, ep->token);
677                                done++;
678                                break;
679                        }
680/*
681 *                      Process the "else" case.  Allow for return.
682 */
683                        switch (parse(ep, STATE_STMT, elseFlags)) {
684                        case STATE_RET:
685                                return STATE_RET;
686                        case STATE_STMT_DONE:
687                                break;
688                        default:
689                                goto error;
690                        }
691                        done++;
692                        break;
693
694                case TOK_FOR:
695/*
696 *                      Format for the expression is:
697 *
698 *                              for (initial; condition; incr) {
699 *                                      body;
700 *                              }
701 */
702                        if (state != STATE_STMT) {
703                                goto error;
704                        }
705                        if (ejLexGetToken(ep, state) != TOK_LPAREN) {
706                                goto error;
707                        }
708
709/*
710 *                      Evaluate the for loop initialization statement
711 */
712                        if (parse(ep, STATE_EXPR, flags) != STATE_EXPR_DONE) {
713                                goto error;
714                        }
715                        if (ejLexGetToken(ep, state) != TOK_SEMI) {
716                                goto error;
717                        }
718
719/*
720 *                      The first time through, we save the current input context just
721 *                      to each step: prior to the conditional, the loop increment and the
722 *                      loop body.
723 */
724                        ejLexSaveInputState(ep, &condScript);
725                        if (parse(ep, STATE_COND, flags) != STATE_COND_DONE) {
726                                goto error;
727                        }
728                        cond = (*ep->result != '0');
729
730                        if (ejLexGetToken(ep, state) != TOK_SEMI) {
731                                goto error;
732                        }
733
734/*
735 *                      Don't execute the loop increment statement or the body first time
736 */
737                        forFlags = flags & ~FLAGS_EXE;
738                        ejLexSaveInputState(ep, &incrScript);
739                        if (parse(ep, STATE_EXPR, forFlags) != STATE_EXPR_DONE) {
740                                goto error;
741                        }
742                        if (ejLexGetToken(ep, state) != TOK_RPAREN) {
743                                goto error;
744                        }
745
746/*
747 *                      Parse the body and remember the end of the body script
748 */
749                        ejLexSaveInputState(ep, &bodyScript);
750                        if (parse(ep, STATE_STMT, forFlags) != STATE_STMT_DONE) {
751                                goto error;
752                        }
753                        ejLexSaveInputState(ep, &endScript);
754
755/*
756 *                      Now actually do the for loop. Note loop has been rotated
757 */
758                        while (cond && (flags & FLAGS_EXE) ) {
759/*
760 *                              Evaluate the body
761 */
762                                ejLexRestoreInputState(ep, &bodyScript);
763
764                                switch (parse(ep, STATE_STMT, flags)) {
765                                case STATE_RET:
766                                        return STATE_RET;
767                                case STATE_STMT_DONE:
768                                        break;
769                                default:
770                                        goto error;
771                                }
772/*
773 *                              Evaluate the increment script
774 */
775                                ejLexRestoreInputState(ep, &incrScript);
776                                if (parse(ep, STATE_EXPR, flags) != STATE_EXPR_DONE) {
777                                        goto error;
778                                }
779/*
780 *                              Evaluate the condition
781 */
782                                ejLexRestoreInputState(ep, &condScript);
783                                if (parse(ep, STATE_COND, flags) != STATE_COND_DONE) {
784                                        goto error;
785                                }
786                                cond = (*ep->result != '0');
787                        }
788                        ejLexRestoreInputState(ep, &endScript);
789                        done++;
790                        break;
791
792                case TOK_VAR:
793                        if (parse(ep, STATE_DEC_LIST, flags) != STATE_DEC_LIST_DONE) {
794                                goto error;
795                        }
796                        done++;
797                        break;
798
799                case TOK_COMMA:
800                        ejLexPutbackToken(ep, TOK_EXPR, ep->token);
801                        done++;
802                        break;
803
804                case TOK_LPAREN:
805                        if (state == STATE_EXPR) {
806                                if (parse(ep, STATE_RELEXP, flags) != STATE_RELEXP_DONE) {
807                                        goto error;
808                                }
809                                if (ejLexGetToken(ep, state) != TOK_RPAREN) {
810                                        goto error;
811                                }
812                                return STATE_EXPR_DONE;
813                        }
814                        done++;
815                        break;
816
817                case TOK_RPAREN:
818                        ejLexPutbackToken(ep, tid, ep->token);
819                        return STATE_EXPR_DONE;
820
821                case TOK_LBRACE:
822/*
823 *                      This handles any code in braces except "if () {} else {}"
824 */
825                        if (state != STATE_STMT) {
826                                goto error;
827                        }
828
829/*
830 *                      Parse will return STATE_STMT_BLOCK_DONE when the RBRACE is seen
831 */
832                        do {
833                                state = parse(ep, STATE_STMT, flags);
834                        } while (state == STATE_STMT_DONE);
835
836/*
837 *                      Allow return statement.
838 */
839                        if (state == STATE_RET) {
840                                return state;
841                        }
842
843                        if (ejLexGetToken(ep, state) != TOK_RBRACE) {
844                                goto error;
845                        }
846                        return STATE_STMT_DONE;
847
848                case TOK_RBRACE:
849                        if (state == STATE_STMT) {
850                                ejLexPutbackToken(ep, tid, ep->token);
851                                return STATE_STMT_BLOCK_DONE;
852                        }
853                        goto error;
854
855                case TOK_RETURN:
856                        if (parse(ep, STATE_RELEXP, flags) != STATE_RELEXP_DONE) {
857                                goto error;
858                        }
859                        if (flags & FLAGS_EXE) {
860                                while ( ejLexGetToken(ep, state) != TOK_EOF );
861                                done++;
862                                return STATE_RET;
863                        }
864                        break;
865                }
866        }
867
868        if (expectSemi) {
869                tid = ejLexGetToken(ep, state);
870                if (tid != TOK_SEMI && tid != TOK_NEWLINE) {
871                        goto error;
872                }
873
874/*
875 *              Skip newline after semi-colon
876 */
877                ejRemoveNewlines(ep, state);
878        }
879
880/*
881 *      Free resources and return the correct status
882 */
883doneParse:
884        if (tid == TOK_FOR) {
885                ejLexFreeInputState(ep, &condScript);
886                ejLexFreeInputState(ep, &incrScript);
887                ejLexFreeInputState(ep, &endScript);
888                ejLexFreeInputState(ep, &bodyScript);
889        }
890
891        if (state == STATE_STMT) {
892                return STATE_STMT_DONE;
893        } else if (state == STATE_DEC) {
894                return STATE_DEC_DONE;
895        } else if (state == STATE_EXPR) {
896                return STATE_EXPR_DONE;
897        } else if (state == STATE_EOF) {
898                return state;
899        } else {
900                return STATE_ERR;
901        }
902
903/*
904 *      Common error exit
905 */
906error:
907        state = STATE_ERR;
908        goto doneParse;
909}
910
911/******************************************************************************/
912/*
913 *      Parse variable declaration list
914 */
915
916static int parseDeclaration(ej_t *ep, int state, int flags)
917{
918        int             tid;
919
920        a_assert(ep);
921
922/*
923 *      Declarations can be of the following forms:
924 *                      var x;
925 *                      var x, y, z;
926 *                      var x = 1 + 2 / 3, y = 2 + 4;
927 *
928 *      We set the variable to NULL if there is no associated assignment.
929 */
930
931        do {
932                if ((tid = ejLexGetToken(ep, state)) != TOK_ID) {
933                        return STATE_ERR;
934                }
935                ejLexPutbackToken(ep, tid, ep->token);
936
937/*
938 *              Parse the entire assignment or simple identifier declaration
939 */
940                if (parse(ep, STATE_DEC, flags) != STATE_DEC_DONE) {
941                        return STATE_ERR;
942                }
943
944/*
945 *              Peek at the next token, continue if comma seen
946 */
947                tid = ejLexGetToken(ep, state);
948                if (tid == TOK_SEMI) {
949                        return STATE_DEC_LIST_DONE;
950                } else if (tid != TOK_COMMA) {
951                        return STATE_ERR;
952                }
953        } while (tid == TOK_COMMA);
954
955        if (tid != TOK_SEMI) {
956                return STATE_ERR;
957        }
958        return STATE_DEC_LIST_DONE;
959}
960
961/******************************************************************************/
962/*
963 *      Parse function arguments
964 */
965
966static int parseArgs(ej_t *ep, int state, int flags)
967{
968        int             tid, aid;
969
970        a_assert(ep);
971
972        do {
973                state = parse(ep, STATE_RELEXP, flags);
974                if (state == STATE_EOF || state == STATE_ERR) {
975                        return state;
976                }
977                if (state == STATE_RELEXP_DONE) {
978                        aid = hAlloc((void***) &ep->func->args);
979                        ep->func->args[aid] = bstrdup(B_L, ep->result);
980                        ep->func->nArgs++;
981                }
982/*
983 *              Peek at the next token, continue if more args (ie. comma seen)
984 */
985                tid = ejLexGetToken(ep, state);
986                if (tid != TOK_COMMA) {
987                        ejLexPutbackToken(ep, tid, ep->token);
988                }
989        } while (tid == TOK_COMMA);
990
991        if (tid != TOK_RPAREN && state != STATE_RELEXP_DONE) {
992                return STATE_ERR;
993        }
994        return STATE_ARG_LIST_DONE;
995}
996
997/******************************************************************************/
998/*
999 *      Parse conditional expression (relational ops separated by ||, &&)
1000 */
1001
1002static int parseCond(ej_t *ep, int state, int flags)
1003{
1004        char_t  *lhs, *rhs;
1005        int             tid, operator;
1006
1007        a_assert(ep);
1008
1009        setString(B_L, &ep->result, T(""));
1010        rhs = lhs = NULL;
1011        operator = 0;
1012
1013        do {
1014/*
1015 *      Recurse to handle one side of a conditional. Accumulate the
1016 *      left hand side and the final result in ep->result.
1017 */
1018                state = parse(ep, STATE_RELEXP, flags);
1019                if (state != STATE_RELEXP_DONE) {
1020                        state = STATE_ERR;
1021                        break;
1022                }
1023
1024                if (operator > 0) {
1025                        setString(B_L, &rhs, ep->result);
1026                        if (evalCond(ep, lhs, operator, rhs) < 0) {
1027                                state = STATE_ERR;
1028                                break;
1029                        }
1030                }
1031                setString(B_L, &lhs, ep->result);
1032
1033                tid = ejLexGetToken(ep, state);
1034                if (tid == TOK_LOGICAL) {
1035                        operator = (int) *ep->token;
1036
1037                } else if (tid == TOK_RPAREN || tid == TOK_SEMI) {
1038                        ejLexPutbackToken(ep, tid, ep->token);
1039                        state = STATE_COND_DONE;
1040                        break;
1041
1042                } else {
1043                        ejLexPutbackToken(ep, tid, ep->token);
1044                }
1045
1046        } while (state == STATE_RELEXP_DONE);
1047
1048        if (lhs) {
1049                bfree(B_L, lhs);
1050        }
1051
1052        if (rhs) {
1053                bfree(B_L, rhs);
1054        }
1055        return state;
1056}
1057
1058/******************************************************************************/
1059/*
1060 *      Parse expression (leftHandSide operator rightHandSide)
1061 */
1062
1063static int parseExpr(ej_t *ep, int state, int flags)
1064{
1065        char_t  *lhs, *rhs;
1066        int             rel, tid;
1067
1068        a_assert(ep);
1069
1070        setString(B_L, &ep->result, T(""));
1071        rhs = lhs = NULL;
1072        rel = 0;
1073        tid = 0;
1074
1075        do {
1076/*
1077 *      This loop will handle an entire expression list. We call parse
1078 *      to evalutate each term which returns the result in ep->result.
1079 */
1080                if (tid == TOK_LOGICAL) {
1081                        if ((state = parse(ep, STATE_RELEXP, flags)) != STATE_RELEXP_DONE) {
1082                                state = STATE_ERR;
1083                                break;
1084                        }
1085                } else {
1086                        if ((state = parse(ep, STATE_EXPR, flags)) != STATE_EXPR_DONE) {
1087                                state = STATE_ERR;
1088                                break;
1089                        }
1090                }
1091
1092                if (rel > 0) {
1093                        setString(B_L, &rhs, ep->result);
1094                        if (tid == TOK_LOGICAL) {
1095                                if (evalCond(ep, lhs, rel, rhs) < 0) {
1096                                        state = STATE_ERR;
1097                                        break;
1098                                }
1099                        } else {
1100                                if (evalExpr(ep, lhs, rel, rhs) < 0) {
1101                                        state = STATE_ERR;
1102                                        break;
1103                                }
1104                        }
1105                }
1106                setString(B_L, &lhs, ep->result);
1107
1108                if ((tid = ejLexGetToken(ep, state)) == TOK_EXPR ||
1109                         tid == TOK_INC_DEC || tid == TOK_LOGICAL) {
1110                        rel = (int) *ep->token;
1111
1112                } else {
1113                        ejLexPutbackToken(ep, tid, ep->token);
1114                        state = STATE_RELEXP_DONE;
1115                }
1116
1117        } while (state == STATE_EXPR_DONE);
1118
1119        if (rhs) {
1120                bfree(B_L, rhs);
1121        }
1122
1123        if (lhs) {
1124                bfree(B_L, lhs);
1125        }
1126
1127        return state;
1128}
1129
1130/******************************************************************************/
1131/*
1132 *      Evaluate a condition. Implements &&, ||, !
1133 */
1134
1135static int evalCond(ej_t *ep, char_t *lhs, int rel, char_t *rhs)
1136{
1137        char_t  buf[16];
1138        int             l, r, lval;
1139
1140        a_assert(lhs);
1141        a_assert(rhs);
1142        a_assert(rel > 0);
1143
1144        lval = 0;
1145        if (gisdigit((int)*lhs) && gisdigit((int)*rhs)) {
1146                l = gatoi(lhs);
1147                r = gatoi(rhs);
1148                switch (rel) {
1149                case COND_AND:
1150                        lval = l && r;
1151                        break;
1152                case COND_OR:
1153                        lval = l || r;
1154                        break;
1155                default:
1156                        ejError(ep, T("Bad operator %d"), rel);
1157                        return -1;
1158                }
1159        } else {
1160                if (!gisdigit((int)*lhs)) {
1161                        ejError(ep, T("Conditional must be numeric"), lhs);
1162                } else {
1163                        ejError(ep, T("Conditional must be numeric"), rhs);
1164                }
1165        }
1166
1167        stritoa(lval, buf, sizeof(buf));
1168        setString(B_L, &ep->result, buf);
1169        return 0;
1170}
1171
1172/******************************************************************************/
1173/*
1174 *      Evaluate an operation
1175 */
1176
1177static int evalExpr(ej_t *ep, char_t *lhs, int rel, char_t *rhs)
1178{
1179        char_t  *cp, buf[16];
1180        int             numeric, l, r, lval;
1181
1182        a_assert(lhs);
1183        a_assert(rhs);
1184        a_assert(rel > 0);
1185
1186/*
1187 *      All of the characters in the lhs and rhs must be numeric
1188 */
1189        numeric = 1;
1190        for (cp = lhs; *cp; cp++) {
1191                if (!gisdigit((int)*cp)) {
1192                        numeric = 0;
1193                        break;
1194                }
1195        }
1196
1197        if (numeric) {
1198                for (cp = rhs; *cp; cp++) {
1199                        if (!gisdigit((int)*cp)) {
1200                                numeric = 0;
1201                                break;
1202                        }
1203                }
1204        }
1205
1206        if (numeric) {
1207                l = gatoi(lhs);
1208                r = gatoi(rhs);
1209                switch (rel) {
1210                case EXPR_PLUS:
1211                        lval = l + r;
1212                        break;
1213                case EXPR_INC:
1214                        lval = l + 1;
1215                        break;
1216                case EXPR_MINUS:
1217                        lval = l - r;
1218                        break;
1219                case EXPR_DEC:
1220                        lval = l - 1;
1221                        break;
1222                case EXPR_MUL:
1223                        lval = l * r;
1224                        break;
1225                case EXPR_DIV:
1226                        if (r != 0) {
1227                                lval = l / r;
1228                        } else {
1229                                lval = 0;
1230                        }
1231                        break;
1232                case EXPR_MOD:
1233                        if (r != 0) {
1234                                lval = l % r;
1235                        } else {
1236                                lval = 0;
1237                        }
1238                        break;
1239                case EXPR_LSHIFT:
1240                        lval = l << r;
1241                        break;
1242                case EXPR_RSHIFT:
1243                        lval = l >> r;
1244                        break;
1245                case EXPR_EQ:
1246                        lval = l == r;
1247                        break;
1248                case EXPR_NOTEQ:
1249                        lval = l != r;
1250                        break;
1251                case EXPR_LESS:
1252                        lval = (l < r) ? 1 : 0;
1253                        break;
1254                case EXPR_LESSEQ:
1255                        lval = (l <= r) ? 1 : 0;
1256                        break;
1257                case EXPR_GREATER:
1258                        lval = (l > r) ? 1 : 0;
1259                        break;
1260                case EXPR_GREATEREQ:
1261                        lval = (l >= r) ? 1 : 0;
1262                        break;
1263                case EXPR_BOOL_COMP:
1264                        lval = (r == 0) ? 1 : 0;
1265                        break;
1266                default:
1267                        ejError(ep, T("Bad operator %d"), rel);
1268                        return -1;
1269                }
1270
1271        } else {
1272                switch (rel) {
1273                case EXPR_PLUS:
1274                        clearString(&ep->result);
1275                        appendString(&ep->result, lhs);
1276                        appendString(&ep->result, rhs);
1277                        return 0;
1278                case EXPR_LESS:
1279                        lval = gstrcmp(lhs, rhs) < 0;
1280                        break;
1281                case EXPR_LESSEQ:
1282                        lval = gstrcmp(lhs, rhs) <= 0;
1283                        break;
1284                case EXPR_GREATER:
1285                        lval = gstrcmp(lhs, rhs) > 0;
1286                        break;
1287                case EXPR_GREATEREQ:
1288                        lval = gstrcmp(lhs, rhs) >= 0;
1289                        break;
1290                case EXPR_EQ:
1291                        lval = gstrcmp(lhs, rhs) == 0;
1292                        break;
1293                case EXPR_NOTEQ:
1294                        lval = gstrcmp(lhs, rhs) != 0;
1295                        break;
1296                case EXPR_INC:
1297                case EXPR_DEC:
1298                case EXPR_MINUS:
1299                case EXPR_DIV:
1300                case EXPR_MOD:
1301                case EXPR_LSHIFT:
1302                case EXPR_RSHIFT:
1303                default:
1304                        ejError(ep, T("Bad operator"));
1305                        return -1;
1306                }
1307        }
1308
1309        stritoa(lval, buf, sizeof(buf));
1310        setString(B_L, &ep->result, buf);
1311        return 0;
1312}
1313
1314/******************************************************************************/
1315/*
1316 *      Evaluate a function
1317 */
1318
1319static int evalFunction(ej_t *ep)
1320{
1321        sym_t   *sp;
1322        int             (*fn)(int eid, void *handle, int argc, char_t **argv);
1323
1324        if ((sp = symLookup(ep->functions, ep->func->fname)) == NULL) {
1325                ejError(ep, T("Undefined procedure %s"), ep->func->fname);
1326                return -1;
1327        }
1328
1329        fn = (int (*)(int, void*, int, char_t**)) sp->content.value.integer;
1330        if (fn == NULL) {
1331                ejError(ep, T("Undefined procedure %s"), ep->func->fname);
1332                return -1;
1333        }
1334
1335        return (*fn)(ep->eid, (void*) ep->userHandle, ep->func->nArgs,
1336                ep->func->args);
1337}
1338
1339/******************************************************************************/
1340/*
1341 *      Output a parse ej_error message
1342 */
1343
1344void ejError(ej_t* ep, char_t* fmt, ...)
1345{
1346        va_list         args;
1347        ejinput_t       *ip;
1348        char_t          *errbuf, *msgbuf;
1349
1350        a_assert(ep);
1351        a_assert(fmt);
1352        ip = ep->input;
1353
1354        va_start(args, fmt);
1355        msgbuf = NULL;
1356        fmtValloc(&msgbuf, E_MAX_ERROR, fmt, args);
1357        va_end(args);
1358
1359        if (ep && ip) {
1360                fmtAlloc(&errbuf, E_MAX_ERROR, T("%s\n At line %d, line => \n\n%s\n"),
1361                        msgbuf, ip->lineNumber, ip->line);
1362                bfreeSafe(B_L, ep->error);
1363                ep->error = errbuf;
1364        }
1365        bfreeSafe(B_L, msgbuf);
1366}
1367
1368/******************************************************************************/
1369/*
1370 *      Clear a string value
1371 */
1372
1373static void clearString(char_t **ptr)
1374{
1375        a_assert(ptr);
1376
1377        if (*ptr) {
1378                bfree(B_L, *ptr);
1379        }
1380        *ptr = NULL;
1381}
1382
1383/******************************************************************************/
1384/*
1385 *      Set a string value
1386 */
1387
1388static void setString(B_ARGS_DEC, char_t **ptr, char_t *s)
1389{
1390        a_assert(ptr);
1391
1392        if (*ptr) {
1393                bfree(B_ARGS, *ptr);
1394        }
1395        *ptr = bstrdup(B_ARGS, s);
1396}
1397
1398/******************************************************************************/
1399/*
1400 *      Append to the pointer value
1401 */
1402
1403static void appendString(char_t **ptr, char_t *s)
1404{
1405        int     len, oldlen;
1406
1407        a_assert(ptr);
1408
1409        if (*ptr) {
1410                len = gstrlen(s);
1411                oldlen = gstrlen(*ptr);
1412                *ptr = brealloc(B_L, *ptr, (len + oldlen + 1) * sizeof(char_t));
1413                gstrcpy(&(*ptr)[oldlen], s);
1414        } else {
1415                *ptr = bstrdup(B_L, s);
1416        }
1417}
1418
1419/******************************************************************************/
1420/*
1421 *      Define a function
1422 */
1423
1424int ejSetGlobalFunction(int eid, char_t *name,
1425        int (*fn)(int eid, void *handle, int argc, char_t **argv))
1426{
1427        ej_t    *ep;
1428
1429        if ((ep = ejPtr(eid)) == NULL) {
1430                return -1;
1431        }
1432        return ejSetGlobalFunctionDirect(ep->functions, name, fn);
1433}
1434
1435/******************************************************************************/
1436/*
1437 *      Define a function directly into the function symbol table.
1438 */
1439
1440int ejSetGlobalFunctionDirect(sym_fd_t functions, char_t *name,
1441        int (*fn)(int eid, void *handle, int argc, char_t **argv))
1442{
1443        if (symEnter(functions, name, valueInteger((long) fn), 0) == NULL) {
1444                return -1;
1445        }
1446        return 0;
1447}
1448
1449/******************************************************************************/
1450/*
1451 *      Remove ("undefine") a function
1452 */
1453
1454int ejRemoveGlobalFunction(int eid, char_t *name)
1455{
1456        ej_t    *ep;
1457
1458        if ((ep = ejPtr(eid)) == NULL) {
1459                return -1;
1460        }
1461        return symDelete(ep->functions, name);
1462}
1463
1464/******************************************************************************/
1465/*
1466 *      Get a function definition
1467 */
1468
1469void *ejGetGlobalFunction(int eid, char_t *name)
1470{
1471        ej_t    *ep;
1472        sym_t   *sp;
1473        int             (*fn)(int eid, void *handle, int argc, char_t **argv);
1474
1475        if ((ep = ejPtr(eid)) == NULL) {
1476                return NULL;
1477        }
1478
1479        if ((sp = symLookup(ep->functions, name)) != NULL) {
1480                fn = (int (*)(int, void*, int, char_t**)) sp->content.value.integer;
1481                return (void*) fn;
1482        }
1483        return NULL;
1484}
1485
1486/******************************************************************************/
1487/*
1488 *      Utility routine to crack Ejscript arguments. Return the number of args
1489 *      seen. This routine only supports %s and %d type args.
1490 *
1491 *      Typical usage:
1492 *
1493 *              if (ejArgs(argc, argv, "%s %d", &name, &age) < 2) {
1494 *                      error("Insufficient args\n");
1495 *                      return -1;
1496 *              }
1497 */
1498
1499int ejArgs(int argc, char_t **argv, char_t *fmt, ...)
1500{
1501        va_list vargs;
1502        char_t  *cp, **sp;
1503        int             *ip;
1504        int             argn;
1505
1506        va_start(vargs, fmt);
1507
1508        if (argv == NULL) {
1509                return 0;
1510        }
1511
1512        for (argn = 0, cp = fmt; cp && *cp && argv[argn]; ) {
1513                if (*cp++ != '%') {
1514                        continue;
1515                }
1516
1517                switch (*cp) {
1518                case 'd':
1519                        ip = va_arg(vargs, int*);
1520                        *ip = gatoi(argv[argn]);
1521                        break;
1522
1523                case 's':
1524                        sp = va_arg(vargs, char_t**);
1525                        *sp = argv[argn];
1526                        break;
1527
1528                default:
1529/*
1530 *                      Unsupported
1531 */
1532                        a_assert(0);
1533                }
1534                argn++;
1535        }
1536
1537        va_end(vargs);
1538        return argn;
1539}
1540
1541/******************************************************************************/
1542/*
1543 *      Define the user handle
1544 */
1545
1546void ejSetUserHandle(int eid, int handle)
1547{
1548        ej_t    *ep;
1549
1550        if ((ep = ejPtr(eid)) == NULL) {
1551                return;
1552        }
1553        ep->userHandle = handle;
1554}
1555
1556/******************************************************************************/
1557/*
1558 *      Get the user handle
1559 */
1560
1561int ejGetUserHandle(int eid)
1562{
1563        ej_t    *ep;
1564
1565        if ((ep = ejPtr(eid)) == NULL) {
1566                return -1;
1567        }
1568        return ep->userHandle;
1569}
1570
1571/******************************************************************************/
1572/*
1573 *      Get the current line number
1574 */
1575
1576int ejGetLineNumber(int eid)
1577{
1578        ej_t    *ep;
1579
1580        if ((ep = ejPtr(eid)) == NULL) {
1581                return -1;
1582        }
1583        return ep->input->lineNumber;
1584}
1585
1586/******************************************************************************/
1587/*
1588 *      Set the result
1589 */
1590
1591void ejSetResult(int eid, char_t *s)
1592{
1593        ej_t    *ep;
1594
1595        if ((ep = ejPtr(eid)) == NULL) {
1596                return;
1597        }
1598        setString(B_L, &ep->result, s);
1599}
1600
1601/******************************************************************************/
1602/*
1603 *      Get the result
1604 */
1605
1606char_t *ejGetResult(int eid)
1607{
1608        ej_t    *ep;
1609
1610        if ((ep = ejPtr(eid)) == NULL) {
1611                return NULL;
1612        }
1613        return ep->result;
1614}
1615
1616/******************************************************************************/
1617/*
1618 *      Set a variable. Note: a variable with a value of NULL means declared but
1619 *      undefined. The value is defined in the top-most variable frame.
1620 */
1621
1622void ejSetVar(int eid, char_t *var, char_t *value)
1623{
1624        ej_t    *ep;
1625        value_t v;
1626
1627        a_assert(var && *var);
1628
1629        if ((ep = ejPtr(eid)) == NULL) {
1630                return;
1631        }
1632
1633        if (value == NULL) {
1634                v = valueString(value, 0);
1635        } else {
1636                v = valueString(value, VALUE_ALLOCATE);
1637        }
1638        symEnter(ep->variables[ep->variableMax - 1] - EJ_OFFSET, var, v, 0);
1639}
1640
1641/******************************************************************************/
1642/*
1643 *      Set a local variable. Note: a variable with a value of NULL means
1644 *      declared but undefined. The value is defined in the top-most variable frame.
1645 */
1646
1647void ejSetLocalVar(int eid, char_t *var, char_t *value)
1648{
1649        ej_t    *ep;
1650        value_t v;
1651
1652        a_assert(var && *var);
1653
1654        if ((ep = ejPtr(eid)) == NULL) {
1655                return;
1656        }
1657
1658        if (value == NULL) {
1659                v = valueString(value, 0);
1660        } else {
1661                v = valueString(value, VALUE_ALLOCATE);
1662        }
1663        symEnter(ep->variables[ep->variableMax - 1] - EJ_OFFSET, var, v, 0);
1664}
1665
1666/******************************************************************************/
1667/*
1668 *      Set a global variable. Note: a variable with a value of NULL means
1669 *      declared but undefined. The value is defined in the global variable frame.
1670 */
1671
1672void ejSetGlobalVar(int eid, char_t *var, char_t *value)
1673{
1674        ej_t    *ep;
1675        value_t v;
1676
1677        a_assert(var && *var);
1678
1679        if ((ep = ejPtr(eid)) == NULL) {
1680                return;
1681        }
1682
1683        if (value == NULL) {
1684                v = valueString(value, 0);
1685        } else {
1686                v = valueString(value, VALUE_ALLOCATE);
1687        }
1688        symEnter(ep->variables[0] - EJ_OFFSET, var, v, 0);
1689}
1690
1691/******************************************************************************/
1692/*
1693 *      Get a variable
1694 */
1695
1696int ejGetVar(int eid, char_t *var, char_t **value)
1697{
1698        ej_t    *ep;
1699        sym_t   *sp;
1700        int             i;
1701
1702        a_assert(var && *var);
1703        a_assert(value);
1704
1705        if ((ep = ejPtr(eid)) == NULL) {
1706                return -1;
1707        }
1708
1709        i = ep->variableMax - 1;
1710        if ((sp = symLookup(ep->variables[i] - EJ_OFFSET, var)) == NULL) {
1711                i = 0;
1712                if ((sp = symLookup(ep->variables[0] - EJ_OFFSET, var)) == NULL) {
1713                        return -1;
1714                }
1715        }
1716        a_assert(sp->content.type == string);
1717        *value = sp->content.value.string;
1718        return i;
1719}
1720
1721/******************************************************************************/
1722/*
1723 *      Get the variable symbol table
1724 */
1725
1726sym_fd_t ejGetVariableTable(int eid)
1727{
1728        ej_t    *ep;
1729
1730        if ((ep = ejPtr(eid)) == NULL) {
1731                return -1;
1732        }
1733        return *ep->variables;
1734}
1735
1736/******************************************************************************/
1737/*
1738 *      Get the functions symbol table
1739 */
1740
1741sym_fd_t ejGetFunctionTable(int eid)
1742{
1743        ej_t    *ep;
1744
1745        if ((ep = ejPtr(eid)) == NULL) {
1746                return -1;
1747        }
1748        return ep->functions;
1749}
1750
1751/******************************************************************************/
1752/*
1753 *      Free an argument list
1754 */
1755
1756static void freeFunc(ejfunc_t *func)
1757{
1758        int     i;
1759
1760        for (i = func->nArgs - 1; i >= 0; i--) {
1761                bfree(B_L, func->args[i]);
1762                func->nArgs = hFree((void***) &func->args, i);
1763        }
1764
1765        if (func->fname) {
1766                bfree(B_L, func->fname);
1767                func->fname = NULL;
1768        }
1769}
1770
1771/******************************************************************************/
1772/*
1773 *      Get Ejscript pointer
1774 */
1775
1776static ej_t *ejPtr(int eid)
1777{
1778        a_assert(0 <= eid && eid < ejMax);
1779
1780        if (eid < 0 || eid >= ejMax || ejHandles[eid] == NULL) {
1781                ejError(NULL, T("Bad handle %d"), eid);
1782                return NULL;
1783        }
1784        return ejHandles[eid];
1785}
1786
1787/******************************************************************************/
1788/*
1789 *      This function removes any new lines.  Used for else     cases, etc.
1790 */
1791static void ejRemoveNewlines(ej_t *ep, int state)
1792{
1793        int tid;
1794
1795        do {
1796                tid = ejLexGetToken(ep, state);
1797        } while (tid == TOK_NEWLINE);
1798
1799        ejLexPutbackToken(ep, tid, ep->token);
1800}
1801
1802/******************************************************************************/
Note: See TracBrowser for help on using the repository browser.