~ubuntu-branches/ubuntu/vivid/emscripten/vivid

« back to all changes in this revision

Viewing changes to tests/cubescript/command.cpp

  • Committer: Package Import Robot
  • Author(s): Sylvestre Ledru
  • Date: 2013-05-02 13:11:51 UTC
  • Revision ID: package-import@ubuntu.com-20130502131151-q8dvteqr1ef2x7xz
Tags: upstream-1.4.1~20130504~adb56cb
ImportĀ upstreamĀ versionĀ 1.4.1~20130504~adb56cb

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// command.cpp: implements the parsing and execution of a tiny script language which
 
2
// is largely backwards compatible with the quake console language.
 
3
 
 
4
// XXX Emscripten
 
5
  #define STANDALONE
 
6
 
 
7
  #include "limits.h"
 
8
  #include "stdarg.h"
 
9
  #include "string.h"
 
10
  #include "stdio.h"
 
11
  #include "stdlib.h"
 
12
  #include "ctype.h"
 
13
  #include "math.h"
 
14
  #include "time.h"
 
15
  #include <new>
 
16
 
 
17
  #include "tools.h"
 
18
  #include "command.h"
 
19
 
 
20
  // console
 
21
  enum
 
22
  {
 
23
      CON_INFO  = 1<<0,
 
24
      CON_WARN  = 1<<1,
 
25
      CON_ERROR = 1<<2,
 
26
      CON_DEBUG = 1<<3,
 
27
      CON_INIT  = 1<<4,
 
28
      CON_ECHO  = 1<<5
 
29
  };
 
30
  extern void conoutf(const char *s, ...);
 
31
  extern void conoutf(int type, const char *s, ...);
 
32
 
 
33
  // command
 
34
  extern int variable(const char *name, int min, int cur, int max, int *storage, void (*fun)(), int flags);
 
35
  extern float fvariable(const char *name, float min, float cur, float max, float *storage, void (*fun)(), int flags);
 
36
  extern char *svariable(const char *name, const char *cur, char **storage, void (*fun)(), int flags);
 
37
  extern void setvar(const char *name, int i, bool dofunc = true, bool doclamp = true);
 
38
  extern void setfvar(const char *name, float f, bool dofunc = true, bool doclamp = true);
 
39
  extern void setsvar(const char *name, const char *str, bool dofunc = true);
 
40
  extern void setvarchecked(ident *id, int val);
 
41
  extern void setfvarchecked(ident *id, float val);
 
42
  extern void setsvarchecked(ident *id, const char *val);
 
43
  extern void touchvar(const char *name);
 
44
  extern int getvar(const char *name);
 
45
  extern int getvarmin(const char *name);
 
46
  extern int getvarmax(const char *name);
 
47
  extern bool identexists(const char *name);
 
48
  extern ident *getident(const char *name);
 
49
  extern ident *newident(const char *name);
 
50
  extern bool addcommand(const char *name, void (*fun)(), const char *narg);
 
51
  extern int execute(const char *p);
 
52
  extern char *executeret(const char *p);
 
53
  extern bool execfile(const char *cfgfile, bool msg = true);
 
54
  extern void alias(const char *name, const char *action);
 
55
  extern const char *getalias(const char *name);
 
56
 
 
57
  // main
 
58
  extern void fatal(const char *s, ...);
 
59
 
 
60
  extern char *path(char *s);
 
61
  extern char *path(const char *s, bool copy);
 
62
  extern const char *parentdir(const char *directory);
 
63
  extern bool fileexists(const char *path, const char *mode);
 
64
  extern bool createdir(const char *path);
 
65
  extern size_t fixpackagedir(char *dir);
 
66
  extern void sethomedir(const char *dir);
 
67
  extern void addpackagedir(const char *dir);
 
68
  extern const char *findfile(const char *filename, const char *mode);
 
69
  extern stream *openrawfile(const char *filename, const char *mode);
 
70
  extern stream *openzipfile(const char *filename, const char *mode);
 
71
  extern stream *openfile(const char *filename, const char *mode);
 
72
  extern stream *opentempfile(const char *filename, const char *mode);
 
73
  extern char *loadfile(const char *fn, int *size);
 
74
  extern bool listdir(const char *dir, const char *ext, vector<char *> &files);
 
75
  extern int listfiles(const char *dir, const char *ext, vector<char *> &files);
 
76
  extern int listzipfiles(const char *dir, const char *ext, vector<char *> &files);
 
77
  extern void seedMT(uint seed);
 
78
  extern uint randomMT(void);
 
79
// XXX =========================
 
80
 
 
81
char *exchangestr(char *o, const char *n) { delete[] o; return newstring(n); }
 
82
 
 
83
typedef hashtable<const char *, ident> identtable;
 
84
 
 
85
identtable *idents = NULL;        // contains ALL vars/commands/aliases
 
86
 
 
87
bool overrideidents = false, persistidents = true;
 
88
 
 
89
void clearstack(ident &id)
 
90
{
 
91
    identstack *stack = id.stack;
 
92
    while(stack)
 
93
    {
 
94
        delete[] stack->action;
 
95
        identstack *tmp = stack;
 
96
        stack = stack->next;
 
97
        delete tmp;
 
98
    }
 
99
    id.stack = NULL;
 
100
}
 
101
 
 
102
void clear_command()
 
103
{
 
104
    enumerate(*idents, ident, i, if(i.type==ID_ALIAS) { DELETEA(i.name); DELETEA(i.action); if(i.stack) clearstack(i); });
 
105
    if(idents) idents->clear();
 
106
}
 
107
 
 
108
void clearoverride(ident &i)
 
109
{
 
110
    if(i.override==NO_OVERRIDE) return;
 
111
    switch(i.type)
 
112
    {
 
113
        case ID_ALIAS:
 
114
            if(i.action[0])
 
115
            {
 
116
                if(i.action != i.isexecuting) delete[] i.action;
 
117
                i.action = newstring("");
 
118
            }
 
119
            break;
 
120
        case ID_VAR:
 
121
            *i.storage.i = i.overrideval.i;
 
122
            i.changed();
 
123
            break;
 
124
        case ID_FVAR:
 
125
            *i.storage.f = i.overrideval.f;
 
126
            i.changed();
 
127
            break;
 
128
        case ID_SVAR:
 
129
            delete[] *i.storage.s;
 
130
            *i.storage.s = i.overrideval.s;
 
131
            i.changed();
 
132
            break;
 
133
    }
 
134
    i.override = NO_OVERRIDE;
 
135
}
 
136
 
 
137
void clearoverrides()
 
138
{
 
139
    enumerate(*idents, ident, i, clearoverride(i));
 
140
}
 
141
 
 
142
void pushident(ident &id, char *val)
 
143
{
 
144
    if(id.type != ID_ALIAS) return;
 
145
    identstack *stack = new identstack;
 
146
    stack->action = id.isexecuting==id.action ? newstring(id.action) : id.action;
 
147
    stack->next = id.stack;
 
148
    id.stack = stack;
 
149
    id.action = val;
 
150
}
 
151
 
 
152
void popident(ident &id)
 
153
{
 
154
    if(id.type != ID_ALIAS || !id.stack) return;
 
155
    if(id.action != id.isexecuting) delete[] id.action;
 
156
    identstack *stack = id.stack;
 
157
    id.action = stack->action;
 
158
    id.stack = stack->next;
 
159
    delete stack;
 
160
}
 
161
 
 
162
ident *newident(const char *name)
 
163
{
 
164
    ident *id = idents->access(name);
 
165
    if(!id)
 
166
    {
 
167
        ident init(ID_ALIAS, newstring(name), newstring(""), persistidents ? IDF_PERSIST : 0);
 
168
        id = &idents->access(init.name, init);
 
169
    }
 
170
    return id;
 
171
}
 
172
 
 
173
void pusha(const char *name, char *action)
 
174
{
 
175
    pushident(*newident(name), action);
 
176
}
 
177
 
 
178
void push(char *name, char *action)
 
179
{
 
180
    pusha(name, newstring(action));
 
181
}
 
182
 
 
183
void pop(char *name)
 
184
{
 
185
    ident *id = idents->access(name);
 
186
    if(id) popident(*id);
 
187
}
 
188
 
 
189
void resetvar(char *name)
 
190
{
 
191
    ident *id = idents->access(name);
 
192
    if(!id) return;
 
193
    if(id->flags&IDF_READONLY) conoutf(CON_ERROR, "variable %s is read-only", id->name);
 
194
    else clearoverride(*id);
 
195
}
 
196
 
 
197
COMMAND(push, "ss");
 
198
COMMAND(pop, "s");
 
199
COMMAND(resetvar, "s");
 
200
 
 
201
void aliasa(const char *name, char *action)
 
202
{
 
203
    ident *b = idents->access(name);
 
204
    if(!b)
 
205
    {
 
206
        ident b(ID_ALIAS, newstring(name), action, persistidents ? IDF_PERSIST : 0);
 
207
        if(overrideidents) b.override = OVERRIDDEN;
 
208
        idents->access(b.name, b);
 
209
    }
 
210
    else if(b->type != ID_ALIAS)
 
211
    {
 
212
        conoutf(CON_ERROR, "cannot redefine builtin %s with an alias", name);
 
213
        delete[] action;
 
214
    }
 
215
    else
 
216
    {
 
217
        if(b->action != b->isexecuting) delete[] b->action;
 
218
        b->action = action;
 
219
        if(overrideidents) b->override = OVERRIDDEN;
 
220
        else
 
221
        {
 
222
            if(b->override != NO_OVERRIDE) b->override = NO_OVERRIDE;
 
223
            if(persistidents)
 
224
            {
 
225
                if(!(b->flags & IDF_PERSIST)) b->flags |= IDF_PERSIST;
 
226
            }
 
227
            else if(b->flags & IDF_PERSIST) b->flags &= ~IDF_PERSIST;
 
228
        }
 
229
    }
 
230
}
 
231
 
 
232
void alias(const char *name, const char *action) { aliasa(name, newstring(action)); }
 
233
 
 
234
COMMAND(alias, "ss");
 
235
 
 
236
// variable's and commands are registered through globals, see cube.h
 
237
 
 
238
int variable(const char *name, int min, int cur, int max, int *storage, void (*fun)(), int flags)
 
239
{
 
240
    if(!idents) idents = new identtable;
 
241
    ident v(ID_VAR, name, min, cur, max, storage, (void *)fun, flags);
 
242
    idents->access(name, v);
 
243
    return cur;
 
244
}
 
245
 
 
246
float fvariable(const char *name, float min, float cur, float max, float *storage, void (*fun)(), int flags)
 
247
{
 
248
    if(!idents) idents = new identtable;
 
249
    ident v(ID_FVAR, name, min, cur, max, storage, (void *)fun, flags);
 
250
    idents->access(name, v);
 
251
    return cur;
 
252
}
 
253
 
 
254
char *svariable(const char *name, const char *cur, char **storage, void (*fun)(), int flags)
 
255
{
 
256
    if(!idents) idents = new identtable;
 
257
    ident v(ID_SVAR, name, newstring(cur), storage, (void *)fun, flags);
 
258
    idents->access(name, v);
 
259
    return v.val.s;
 
260
}
 
261
 
 
262
#define _GETVAR(id, vartype, name, retval) \
 
263
    ident *id = idents->access(name); \
 
264
    if(!id || id->type!=vartype) return retval;
 
265
#define GETVAR(id, name, retval) _GETVAR(id, ID_VAR, name, retval)
 
266
#define OVERRIDEVAR(errorval, saveval, resetval, clearval) \
 
267
    if(overrideidents || id->flags&IDF_OVERRIDE) \
 
268
    { \
 
269
        if(id->flags&IDF_PERSIST) \
 
270
        { \
 
271
            conoutf(CON_ERROR, "cannot override persistent variable %s", id->name); \
 
272
            errorval; \
 
273
        } \
 
274
        if(id->override==NO_OVERRIDE) { saveval; id->override = OVERRIDDEN; } \
 
275
        else { clearval; } \
 
276
    } \
 
277
    else \
 
278
    { \
 
279
        if(id->override!=NO_OVERRIDE) { resetval; id->override = NO_OVERRIDE; } \
 
280
        clearval; \
 
281
    }
 
282
 
 
283
void setvar(const char *name, int i, bool dofunc, bool doclamp)
 
284
{
 
285
    GETVAR(id, name, );
 
286
    OVERRIDEVAR(return, id->overrideval.i = *id->storage.i, , )
 
287
    if(doclamp) *id->storage.i = clamp(i, id->minval, id->maxval);
 
288
    else *id->storage.i = i;
 
289
    if(dofunc) id->changed();
 
290
}
 
291
void setfvar(const char *name, float f, bool dofunc, bool doclamp)
 
292
{
 
293
    _GETVAR(id, ID_FVAR, name, );
 
294
    OVERRIDEVAR(return, id->overrideval.f = *id->storage.f, , );
 
295
    if(doclamp) *id->storage.f = clamp(f, id->minvalf, id->maxvalf);
 
296
    else *id->storage.f = f;
 
297
    if(dofunc) id->changed();
 
298
}
 
299
void setsvar(const char *name, const char *str, bool dofunc)
 
300
{
 
301
    _GETVAR(id, ID_SVAR, name, );
 
302
    OVERRIDEVAR(return, id->overrideval.s = *id->storage.s, delete[] id->overrideval.s, delete[] *id->storage.s);
 
303
    *id->storage.s = newstring(str);
 
304
    if(dofunc) id->changed();
 
305
}
 
306
int getvar(const char *name)
 
307
{
 
308
    GETVAR(id, name, 0);
 
309
    return *id->storage.i;
 
310
}
 
311
int getvarmin(const char *name)
 
312
{
 
313
    GETVAR(id, name, 0);
 
314
    return id->minval;
 
315
}
 
316
int getvarmax(const char *name)
 
317
{
 
318
    GETVAR(id, name, 0);
 
319
    return id->maxval;
 
320
}
 
321
bool identexists(const char *name) { return idents->access(name)!=NULL; }
 
322
ident *getident(const char *name) { return idents->access(name); }
 
323
 
 
324
void touchvar(const char *name)
 
325
{
 
326
    ident *id = idents->access(name);
 
327
    if(id) switch(id->type)
 
328
    {
 
329
        case ID_VAR:
 
330
        case ID_FVAR:
 
331
        case ID_SVAR:
 
332
            id->changed();
 
333
            break;
 
334
    }
 
335
}
 
336
 
 
337
const char *getalias(const char *name)
 
338
{
 
339
    ident *i = idents->access(name);
 
340
    return i && i->type==ID_ALIAS ? i->action : "";
 
341
}
 
342
 
 
343
void setvarchecked(ident *id, int val)
 
344
{
 
345
    if(id->flags&IDF_READONLY) conoutf(CON_ERROR, "variable %s is read-only", id->name);
 
346
#ifndef STANDALONE
 
347
    else if(!(id->flags&IDF_OVERRIDE) || overrideidents || game::allowedittoggle())
 
348
#else
 
349
    else
 
350
#endif
 
351
    {
 
352
        OVERRIDEVAR(return, id->overrideval.i = *id->storage.i, , )
 
353
        if(val<id->minval || val>id->maxval)
 
354
        {
 
355
            val = val<id->minval ? id->minval : id->maxval;                // clamp to valid range
 
356
            conoutf(CON_ERROR,
 
357
                id->flags&IDF_HEX ?
 
358
                    (id->minval <= 255 ? "valid range for %s is %d..0x%X" : "valid range for %s is 0x%X..0x%X") :
 
359
                    "valid range for %s is %d..%d",
 
360
                id->name, id->minval, id->maxval);
 
361
        }
 
362
        *id->storage.i = val;
 
363
        id->changed();                                             // call trigger function if available
 
364
#ifndef STANDALONE
 
365
        if(id->flags&IDF_OVERRIDE && !overrideidents) game::vartrigger(id);
 
366
#endif
 
367
    }
 
368
}
 
369
 
 
370
void setfvarchecked(ident *id, float val)
 
371
{
 
372
    if(id->flags&IDF_READONLY) conoutf(CON_ERROR, "variable %s is read-only", id->name);
 
373
#ifndef STANDALONE
 
374
    else if(!(id->flags&IDF_OVERRIDE) || overrideidents || game::allowedittoggle())
 
375
#else
 
376
    else
 
377
#endif
 
378
    {
 
379
        OVERRIDEVAR(return, id->overrideval.f = *id->storage.f, , );
 
380
        if(val<id->minvalf || val>id->maxvalf)
 
381
        {
 
382
            val = val<id->minvalf ? id->minvalf : id->maxvalf;                // clamp to valid range
 
383
            conoutf(CON_ERROR, "valid range for %s is %s..%s", id->name, floatstr(id->minvalf), floatstr(id->maxvalf));
 
384
        }
 
385
        *id->storage.f = val;
 
386
        id->changed();
 
387
#ifndef STANDALONE
 
388
        if(id->flags&IDF_OVERRIDE && !overrideidents) game::vartrigger(id);
 
389
#endif
 
390
    }
 
391
}
 
392
 
 
393
void setsvarchecked(ident *id, const char *val)
 
394
{
 
395
    if(id->flags&IDF_READONLY) conoutf(CON_ERROR, "variable %s is read-only", id->name);
 
396
#ifndef STANDALONE
 
397
    else if(!(id->flags&IDF_OVERRIDE) || overrideidents || game::allowedittoggle())
 
398
#else
 
399
    else
 
400
#endif
 
401
    {
 
402
        OVERRIDEVAR(return, id->overrideval.s = *id->storage.s, delete[] id->overrideval.s, delete[] *id->storage.s);
 
403
        *id->storage.s = newstring(val);
 
404
        id->changed();
 
405
#ifndef STANDALONE
 
406
        if(id->flags&IDF_OVERRIDE && !overrideidents) game::vartrigger(id);
 
407
#endif
 
408
    }
 
409
}
 
410
 
 
411
bool addcommand(const char *name, void (*fun)(), const char *narg)
 
412
{
 
413
    if(!idents) idents = new identtable;
 
414
    ident c(ID_COMMAND, name, narg, (void *)fun);
 
415
    idents->access(name, c);
 
416
    return false;
 
417
}
 
418
 
 
419
void addident(const char *name, ident *id)
 
420
{
 
421
    if(!idents) idents = new identtable;
 
422
    idents->access(name, *id);
 
423
}
 
424
 
 
425
static vector<vector<char> *> wordbufs;
 
426
static int bufnest = 0;
 
427
 
 
428
char *parseexp(const char *&p, int right);
 
429
 
 
430
void parsemacro(const char *&p, int level, vector<char> &wordbuf)
 
431
{
 
432
    int escape = 1;
 
433
    while(*p=='@') p++, escape++;
 
434
    if(level > escape)
 
435
    {
 
436
        while(escape--) wordbuf.add('@');
 
437
        return;
 
438
    }
 
439
    if(*p=='(')
 
440
    {
 
441
        char *ret = parseexp(p, ')');
 
442
        if(ret)
 
443
        {
 
444
            for(char *sub = ret; *sub; ) wordbuf.add(*sub++);
 
445
            delete[] ret;
 
446
        }
 
447
        return;
 
448
    }
 
449
    static vector<char> ident;
 
450
    ident.setsize(0);
 
451
    while(isalnum(*p) || *p=='_') ident.add(*p++);
 
452
    ident.add(0);
 
453
    const char *alias = getalias(ident.getbuf());
 
454
    while(*alias) wordbuf.add(*alias++);
 
455
}
 
456
 
 
457
const char *parsestring(const char *p)
 
458
{
 
459
    for(; *p; p++) switch(*p)
 
460
    {
 
461
        case '\r':
 
462
        case '\n':
 
463
        case '\"':
 
464
            return p;
 
465
        case '^':
 
466
            if(*++p) break;
 
467
            return p;
 
468
    }
 
469
    return p;
 
470
}
 
471
 
 
472
int escapestring(char *dst, const char *src, const char *end)
 
473
{
 
474
    char *start = dst;
 
475
    while(src < end)
 
476
    {
 
477
        int c = *src++;
 
478
        if(c == '^')
 
479
        {
 
480
            if(src >= end) break;
 
481
            int e = *src++;
 
482
            switch(e)
 
483
            {
 
484
                case 'n': *dst++ = '\n'; break;
 
485
                case 't': *dst++ = '\t'; break;
 
486
                case 'f': *dst++ = '\f'; break;
 
487
                default: *dst++ = e; break;
 
488
            }
 
489
        }
 
490
        else *dst++ = c;
 
491
    }
 
492
    return dst - start;
 
493
}
 
494
 
 
495
char *parseexp(const char *&p, int right)          // parse any nested set of () or []
 
496
{
 
497
    if(bufnest++>=wordbufs.length()) wordbufs.add(new vector<char>);
 
498
    vector<char> &wordbuf = *wordbufs[bufnest-1];
 
499
    int left = *p++;
 
500
    for(int brak = 1; brak; )
 
501
    {
 
502
        size_t n = strcspn(p, "\r@\"/()[]");
 
503
        wordbuf.put(p, n);
 
504
        p += n;
 
505
 
 
506
        int c = *p++;
 
507
        switch(c)
 
508
        {
 
509
            case '\r': continue;
 
510
            case '@':
 
511
                if(left == '[') { parsemacro(p, brak, wordbuf); continue; }
 
512
                break;
 
513
            case '\"':
 
514
            {
 
515
                wordbuf.add(c);
 
516
                const char *end = parsestring(p);
 
517
                wordbuf.put(p, end - p);
 
518
                p = end;
 
519
                if(*p=='\"') wordbuf.add(*p++);
 
520
                continue;
 
521
            }
 
522
            case '/':
 
523
                if(*p=='/')
 
524
                {
 
525
                    p += strcspn(p, "\n\0");
 
526
                    continue;
 
527
                }
 
528
                break;
 
529
            case '\0':
 
530
                p--;
 
531
                conoutf(CON_ERROR, "missing \"%c\"", right);
 
532
                wordbuf.setsize(0);
 
533
                bufnest--;
 
534
                return NULL;
 
535
            case '(': case '[': if(c==left) brak++; break;
 
536
            case ')': case ']': if(c==right) brak--; break;
 
537
        }
 
538
        wordbuf.add(c);
 
539
    }
 
540
    wordbuf.pop();
 
541
    char *s;
 
542
    if(left=='(')
 
543
    {
 
544
        wordbuf.add(0);
 
545
        char *ret = executeret(wordbuf.getbuf());                    // evaluate () exps directly, and substitute result
 
546
        wordbuf.pop();
 
547
        s = ret ? ret : newstring("");
 
548
    }
 
549
    else
 
550
    {
 
551
        s = newstring(wordbuf.getbuf(), wordbuf.length());
 
552
    }
 
553
    wordbuf.setsize(0);
 
554
    bufnest--;
 
555
    return s;
 
556
}
 
557
 
 
558
char *lookup(char *n)                           // find value of ident referenced with $ in exp
 
559
{
 
560
    ident *id = idents->access(n+1);
 
561
    if(id) switch(id->type)
 
562
    {
 
563
        case ID_VAR: return exchangestr(n, intstr(*id->storage.i));
 
564
        case ID_FVAR: return exchangestr(n, floatstr(*id->storage.f));
 
565
        case ID_SVAR: return exchangestr(n, *id->storage.s);
 
566
        case ID_ALIAS: return exchangestr(n, id->action);
 
567
    }
 
568
    conoutf(CON_ERROR, "unknown alias lookup: %s", n+1);
 
569
    return n;
 
570
}
 
571
 
 
572
char *parseword(const char *&p, int arg, int &infix)                       // parse single argument, including expressions
 
573
{
 
574
    for(;;)
 
575
    {
 
576
        p += strspn(p, " \t\r");
 
577
        if(p[0]!='/' || p[1]!='/') break;
 
578
        p += strcspn(p, "\n\0");
 
579
    }
 
580
    if(*p=='\"')
 
581
    {
 
582
        p++;
 
583
        const char *end = parsestring(p);
 
584
        char *s = newstring(end - p);
 
585
        s[escapestring(s, p, end)] = '\0';
 
586
        p = end;
 
587
        if(*p=='\"') p++;
 
588
        return s;
 
589
    }
 
590
    if(*p=='(') return parseexp(p, ')');
 
591
    if(*p=='[') return parseexp(p, ']');
 
592
    const char *word = p;
 
593
    for(;;)
 
594
    {
 
595
        p += strcspn(p, "/; \t\r\n\0");
 
596
        if(p[0]!='/' || p[1]=='/') break;
 
597
        else if(p[1]=='\0') { p++; break; }
 
598
        p += 2;
 
599
    }
 
600
    if(p-word==0) return NULL;
 
601
    if(arg==1 && p-word==1) switch(*word)
 
602
    {
 
603
        case '=': infix = *word; break;
 
604
    }
 
605
    char *s = newstring(word, p-word);
 
606
    if(*s=='$') return lookup(s);               // substitute variables
 
607
    return s;
 
608
}
 
609
 
 
610
char *conc(char **w, int n, bool space)
 
611
{
 
612
    int len = space ? max(n-1, 0) : 0;
 
613
    loopj(n) len += (int)strlen(w[j]);
 
614
    char *r = newstring("", len);
 
615
    loopi(n)
 
616
    {
 
617
        strcat(r, w[i]);  // make string-list out of all arguments
 
618
        if(i==n-1) break;
 
619
        if(space) strcat(r, " ");
 
620
    }
 
621
    return r;
 
622
}
 
623
 
 
624
VARN(numargs, _numargs, 25, 0, 0);
 
625
 
 
626
static inline bool isinteger(char *c)
 
627
{
 
628
    return isdigit(c[0]) || ((c[0]=='+' || c[0]=='-' || c[0]=='.') && isdigit(c[1]));
 
629
}
 
630
 
 
631
char *commandret = NULL;
 
632
 
 
633
char *executeret(const char *p)               // all evaluation happens here, recursively
 
634
{
 
635
    const int MAXWORDS = 25;                    // limit, remove
 
636
    char *w[MAXWORDS];
 
637
    char *retval = NULL;
 
638
    #define setretval(v) { char *rv = v; if(rv) retval = rv; }
 
639
    for(bool cont = true; cont;)                // for each ; seperated statement
 
640
    {
 
641
        int numargs = MAXWORDS, infix = 0;
 
642
        loopi(MAXWORDS)                         // collect all argument values
 
643
        {
 
644
            w[i] = parseword(p, i, infix);   // parse and evaluate exps
 
645
            if(!w[i]) { numargs = i; break; }
 
646
        }
 
647
 
 
648
        p += strcspn(p, ";\n\0");
 
649
        cont = *p++!=0;                         // more statements if this isn't the end of the string
 
650
        char *c = w[0];
 
651
        if(!c || !*c) continue;                       // empty statement
 
652
 
 
653
        DELETEA(retval);
 
654
 
 
655
        if(infix)
 
656
        {
 
657
            switch(infix)
 
658
            {
 
659
                case '=':
 
660
                    aliasa(c, numargs>2 ? w[2] : newstring(""));
 
661
                    w[2] = NULL;
 
662
                    break;
 
663
            }
 
664
        }
 
665
        else
 
666
        {
 
667
            ident *id = idents->access(c);
 
668
            if(!id)
 
669
            {
 
670
                if(!isinteger(c))
 
671
                    conoutf(CON_ERROR, "unknown command: %s", c);
 
672
                setretval(newstring(c));
 
673
            }
 
674
            else switch(id->type)
 
675
            {
 
676
                case ID_CCOMMAND:
 
677
                case ID_COMMAND:                     // game defined commands
 
678
                {
 
679
                    void *v[MAXWORDS];
 
680
                    union
 
681
                    {
 
682
                        int i;
 
683
                        float f;
 
684
                    } nstor[MAXWORDS];
 
685
                    int n = 0, wn = 0;
 
686
                    char *cargs = NULL;
 
687
                    if(id->type==ID_CCOMMAND) v[n++] = id->self;
 
688
                    for(const char *a = id->narg; *a; a++, n++) switch(*a)
 
689
                    {
 
690
                        case 's': v[n] = ++wn < numargs ? w[wn] : (char *)""; break;
 
691
                        case 'i': nstor[n].i = ++wn < numargs ? parseint(w[wn]) : 0;  v[n] = &nstor[n].i; break;
 
692
                        case 'f': nstor[n].f = ++wn < numargs ? parsefloat(w[wn]) : 0.0f; v[n] = &nstor[n].f; break;
 
693
#ifndef STANDALONE
 
694
                        case 'D': nstor[n].i = addreleaseaction(id->name) ? 1 : 0; v[n] = &nstor[n].i; break;
 
695
#endif
 
696
                        case 'V': v[n++] = w+1; nstor[n].i = numargs-1; v[n] = &nstor[n].i; break;
 
697
                        case 'C': if(!cargs) cargs = conc(w+1, numargs-1, true); v[n] = cargs; break;
 
698
                        default: fatal("builtin declared with illegal type");
 
699
                    }
 
700
                    switch(n)
 
701
                    {
 
702
                        case 0: ((void (__cdecl *)()                                      )id->fun)();                             break;
 
703
                        case 1: ((void (__cdecl *)(void *)                                )id->fun)(v[0]);                         break;
 
704
                        case 2: ((void (__cdecl *)(void *, void *)                        )id->fun)(v[0], v[1]);                   break;
 
705
                        case 3: ((void (__cdecl *)(void *, void *, void *)                )id->fun)(v[0], v[1], v[2]);             break;
 
706
                        case 4: ((void (__cdecl *)(void *, void *, void *, void *)        )id->fun)(v[0], v[1], v[2], v[3]);       break;
 
707
                        case 5: ((void (__cdecl *)(void *, void *, void *, void *, void *))id->fun)(v[0], v[1], v[2], v[3], v[4]); break;
 
708
                        case 6: ((void (__cdecl *)(void *, void *, void *, void *, void *, void *))id->fun)(v[0], v[1], v[2], v[3], v[4], v[5]); break;
 
709
                        case 7: ((void (__cdecl *)(void *, void *, void *, void *, void *, void *, void *))id->fun)(v[0], v[1], v[2], v[3], v[4], v[5], v[6]); break;
 
710
                        case 8: ((void (__cdecl *)(void *, void *, void *, void *, void *, void *, void *, void *))id->fun)(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]); break;
 
711
                        default: fatal("builtin declared with too many args (use V?)");
 
712
                    }
 
713
                    if(cargs) delete[] cargs;
 
714
                    setretval(commandret);
 
715
                    commandret = NULL;
 
716
                    break;
 
717
                }
 
718
 
 
719
                case ID_VAR:                        // game defined variables
 
720
                    if(numargs <= 1)
 
721
                    {
 
722
                        if(id->flags&IDF_HEX && id->maxval==0xFFFFFF)
 
723
                            conoutf("%s = 0x%.6X (%d, %d, %d)", c, *id->storage.i, (*id->storage.i>>16)&0xFF, (*id->storage.i>>8)&0xFF, *id->storage.i&0xFF);
 
724
                        else
 
725
                            conoutf(id->flags&IDF_HEX ? "%s = 0x%X" : "%s = %d", c, *id->storage.i);      // var with no value just prints its current value
 
726
                    }
 
727
                    else
 
728
                    {
 
729
                        int val = parseint(w[1]);
 
730
                        if(id->flags&IDF_HEX && numargs > 2)
 
731
                        {
 
732
                            val <<= 16;
 
733
                            val |= parseint(w[2])<<8;
 
734
                            if(numargs > 3) val |= parseint(w[3]);
 
735
                        }
 
736
                        setvarchecked(id, val);
 
737
                    }
 
738
                    break;
 
739
 
 
740
                case ID_FVAR:
 
741
                    if(numargs <= 1) conoutf("%s = %s", c, floatstr(*id->storage.f));
 
742
                    else setfvarchecked(id, parsefloat(w[1]));
 
743
                    break;
 
744
 
 
745
                case ID_SVAR:
 
746
                    if(numargs <= 1) conoutf(strchr(*id->storage.s, '"') ? "%s = [%s]" : "%s = \"%s\"", c, *id->storage.s);
 
747
                    else setsvarchecked(id, w[1]);
 
748
                    break;
 
749
 
 
750
                case ID_ALIAS:                              // alias, also used as functions and (global) variables
 
751
                {
 
752
                    delete[] w[0];
 
753
                    static vector<ident *> argids;
 
754
                    for(int i = 1; i<numargs; i++)
 
755
                    {
 
756
                        if(i > argids.length())
 
757
                        {
 
758
                            defformatstring(argname)("arg%d", i);
 
759
                            argids.add(newident(argname));
 
760
                        }
 
761
                        pushident(*argids[i-1], w[i]); // set any arguments as (global) arg values so functions can access them
 
762
                    }
 
763
                    _numargs = numargs-1;
 
764
                    bool wasoverriding = overrideidents;
 
765
                    if(id->override!=NO_OVERRIDE) overrideidents = true;
 
766
                    char *wasexecuting = id->isexecuting;
 
767
                    id->isexecuting = id->action;
 
768
                    setretval(executeret(id->action));
 
769
                    if(id->isexecuting != id->action && id->isexecuting != wasexecuting) delete[] id->isexecuting;
 
770
                    id->isexecuting = wasexecuting;
 
771
                    overrideidents = wasoverriding;
 
772
                    for(int i = 1; i<numargs; i++) popident(*argids[i-1]);
 
773
                    continue;
 
774
                }
 
775
            }
 
776
        }
 
777
        loopj(numargs) if(w[j]) delete[] w[j];
 
778
    }
 
779
    return retval;
 
780
}
 
781
 
 
782
int execute(const char *p)
 
783
{
 
784
    char *ret = executeret(p);
 
785
    int i = 0;
 
786
    if(ret) { i = parseint(ret); delete[] ret; }
 
787
    return i;
 
788
}
 
789
 
 
790
#ifndef STANDALONE
 
791
static int sortidents(ident **x, ident **y)
 
792
{
 
793
    return strcmp((*x)->name, (*y)->name);
 
794
}
 
795
 
 
796
void writeescapedstring(stream *f, const char *s)
 
797
{
 
798
    f->putchar('"');
 
799
    for(; *s; s++) switch(*s)
 
800
    {
 
801
        case '\n': f->write("^n", 2); break;
 
802
        case '\t': f->write("^t", 2); break;
 
803
        case '\f': f->write("^f", 2); break;
 
804
        case '"': f->write("^\"", 2); break;
 
805
        default: f->putchar(*s); break;
 
806
    }
 
807
    f->putchar('"');
 
808
}
 
809
#endif
 
810
 
 
811
// below the commands that implement a small imperative language. thanks to the semantics of
 
812
// () and [] expressions, any control construct can be defined trivially.
 
813
 
 
814
static string retbuf[3];
 
815
static int retidx = 0;
 
816
 
 
817
const char *intstr(int v)
 
818
{
 
819
    retidx = (retidx + 1)%3;
 
820
    formatstring(retbuf[retidx])("%d", v);
 
821
    return retbuf[retidx];
 
822
}
 
823
 
 
824
void intret(int v)
 
825
{
 
826
    commandret = newstring(intstr(v));
 
827
}
 
828
 
 
829
const char *floatstr(float v)
 
830
{
 
831
    retidx = (retidx + 1)%3;
 
832
    formatstring(retbuf[retidx])(v==int(v) ? "%.1f" : "%.7g", v);
 
833
    return retbuf[retidx];
 
834
}
 
835
 
 
836
void floatret(float v)
 
837
{
 
838
    commandret = newstring(floatstr(v));
 
839
}
 
840
 
 
841
#undef ICOMMANDNAME
 
842
#define ICOMMANDNAME(name) _stdcmd
 
843
 
 
844
ICOMMAND(if, "sss", (char *cond, char *t, char *f), commandret = executeret(cond[0] && (!isinteger(cond) || parseint(cond)) ? t : f));
 
845
ICOMMAND(?, "sss", (char *cond, char *t, char *f), result(cond[0] && (!isinteger(cond) || parseint(cond)) ? t : f));
 
846
ICOMMAND(loop, "sis", (char *var, int *n, char *body),
 
847
{
 
848
    if(*n<=0) return;
 
849
    ident *id = newident(var);
 
850
    if(id->type!=ID_ALIAS) return;
 
851
    loopi(*n)
 
852
    {
 
853
        if(i) sprintf(id->action, "%d", i);
 
854
        else pushident(*id, newstring("0", 16));
 
855
        execute(body);
 
856
    }
 
857
    popident(*id);
 
858
});
 
859
ICOMMAND(loopwhile, "siss", (char *var, int *n, char *cond, char *body),
 
860
{
 
861
    if(*n<=0) return;
 
862
    ident *id = newident(var);
 
863
    if(id->type!=ID_ALIAS) return;
 
864
    loopi(*n)
 
865
    {
 
866
        if(i) sprintf(id->action, "%d", i);
 
867
        else pushident(*id, newstring("0", 16));
 
868
        if(!execute(cond)) break;
 
869
        execute(body);
 
870
    }
 
871
    popident(*id);
 
872
});
 
873
ICOMMAND(while, "ss", (char *cond, char *body), while(execute(cond)) execute(body));    // can't get any simpler than this :)
 
874
 
 
875
void concat(const char *s) { commandret = newstring(s); }
 
876
void result(const char *s) { commandret = newstring(s); }
 
877
 
 
878
void concatword(char **args, int *numargs)
 
879
{
 
880
    commandret = conc(args, *numargs, false);
 
881
}
 
882
 
 
883
void format(char **args, int *numargs)
 
884
{
 
885
    vector<char> s;
 
886
    char *f = args[0];
 
887
    while(*f)
 
888
    {
 
889
        int c = *f++;
 
890
        if(c == '%')
 
891
        {
 
892
            int i = *f++;
 
893
            if(i >= '1' && i <= '9')
 
894
            {
 
895
                i -= '0';
 
896
                const char *sub = i < *numargs ? args[i] : "";
 
897
                while(*sub) s.add(*sub++);
 
898
            }
 
899
            else s.add(i);
 
900
        }
 
901
        else s.add(c);
 
902
    }
 
903
    s.add('\0');
 
904
    result(s.getbuf());
 
905
}
 
906
 
 
907
#define whitespaceskip s += strspn(s, "\n\t ")
 
908
#define elementskip *s=='"' ? (++s, s += strcspn(s, "\"\n\0"), s += *s=='"') : s += strcspn(s, "\n\t \0")
 
909
 
 
910
void explodelist(const char *s, vector<char *> &elems)
 
911
{
 
912
    whitespaceskip;
 
913
    while(*s)
 
914
    {
 
915
        const char *elem = s;
 
916
        elementskip;
 
917
        elems.add(*elem=='"' ? newstring(elem+1, s-elem-(s[-1]=='"' ? 2 : 1)) : newstring(elem, s-elem));
 
918
        whitespaceskip;
 
919
    }
 
920
}
 
921
 
 
922
char *indexlist(const char *s, int pos)
 
923
{
 
924
    whitespaceskip;
 
925
    loopi(pos)
 
926
    {
 
927
        elementskip;
 
928
        whitespaceskip;
 
929
        if(!*s) break;
 
930
    }
 
931
    const char *e = s;
 
932
    elementskip;
 
933
    if(*e=='"')
 
934
    {
 
935
        e++;
 
936
        if(s[-1]=='"') --s;
 
937
    }
 
938
    return newstring(e, s-e);
 
939
}
 
940
 
 
941
int listlen(const char *s)
 
942
{
 
943
    int n = 0;
 
944
    whitespaceskip;
 
945
    for(; *s; n++) elementskip, whitespaceskip;
 
946
    return n;
 
947
}
 
948
 
 
949
void at(char *s, int *pos)
 
950
{
 
951
    commandret = indexlist(s, *pos);
 
952
}
 
953
 
 
954
void substr(char *s, int *start, char *count)
 
955
{
 
956
    int len = strlen(s), offset = clamp(*start, 0, len);
 
957
    commandret = newstring(&s[offset], count[0] ? clamp(parseint(count), 0, len - offset) : len - offset);
 
958
}
 
959
 
 
960
void getalias_(char *s)
 
961
{
 
962
    result(getalias(s));
 
963
}
 
964
 
 
965
COMMAND(concat, "C");
 
966
COMMAND(result, "s");
 
967
COMMAND(concatword, "V");
 
968
COMMAND(format, "V");
 
969
COMMAND(at, "si");
 
970
COMMAND(substr, "sis");
 
971
ICOMMAND(listlen, "s", (char *s), intret(listlen(s)));
 
972
COMMANDN(getalias, getalias_, "s");
 
973
 
 
974
void looplist(const char *var, const char *list, const char *body, bool search)
 
975
{
 
976
    ident *id = newident(var);
 
977
    if(id->type!=ID_ALIAS) { if(search) intret(-1); return; }
 
978
    int n = 0;
 
979
    for(const char *s = list;;)
 
980
    {
 
981
        whitespaceskip;
 
982
        if(!*s) { if(search) intret(-1); break; }
 
983
        const char *start = s;
 
984
        elementskip;
 
985
        const char *end = s;
 
986
        if(*start=='"') { start++; if(end[-1]=='"') --end; }
 
987
        char *val = newstring(start, end-start);
 
988
        if(n++) aliasa(id->name, val);
 
989
        else pushident(*id, val);
 
990
        if(execute(body) && search) { intret(n-1); break; }
 
991
    }
 
992
    if(n) popident(*id);
 
993
}
 
994
 
 
995
void prettylist(const char *s, const char *conj)
 
996
{
 
997
    vector<char> p;
 
998
    whitespaceskip;
 
999
    for(int len = listlen(s), n = 0; *s; n++)
 
1000
    {
 
1001
        const char *elem = s;
 
1002
        elementskip;
 
1003
        p.put(elem, s - elem);
 
1004
        if(n+1 < len)
 
1005
        {
 
1006
            if(len > 2 || !conj[0]) p.add(',');
 
1007
            if(n+2 == len && conj[0])
 
1008
            {
 
1009
                p.add(' ');
 
1010
                p.put(conj, strlen(conj));
 
1011
            }
 
1012
            p.add(' ');
 
1013
        }
 
1014
        whitespaceskip;
 
1015
    }
 
1016
    p.add('\0');
 
1017
    result(p.getbuf());
 
1018
}
 
1019
COMMAND(prettylist, "ss");
 
1020
 
 
1021
int listincludes(const char *list, const char *needle, int needlelen)
 
1022
{
 
1023
    const char *s = list;
 
1024
    whitespaceskip;
 
1025
    int offset = 0;
 
1026
    while(*s)
 
1027
    {
 
1028
        const char *elem = s;
 
1029
        elementskip;
 
1030
        int len = s-elem;
 
1031
        if(*elem=='"')
 
1032
        {
 
1033
            elem++;
 
1034
            len -= s[-1]=='"' ? 2 : 1;
 
1035
        }
 
1036
        if(needlelen == len && !strncmp(needle, elem, len)) return offset;
 
1037
        whitespaceskip;
 
1038
        offset++;
 
1039
    }
 
1040
    return -1;
 
1041
}
 
1042
    
 
1043
char *listdel(const char *s, const char *del)
 
1044
{
 
1045
    vector<char> p;
 
1046
    whitespaceskip;
 
1047
    while(*s)
 
1048
    {
 
1049
        const char *elem = s;
 
1050
        elementskip;
 
1051
        int len = s-elem;
 
1052
        if(*elem=='"')
 
1053
        {
 
1054
            elem++;
 
1055
            len -= s[-1]=='"' ? 2 : 1;
 
1056
        }
 
1057
        if(listincludes(del, elem, len) < 0)
 
1058
        {
 
1059
            if(!p.empty()) p.add(' ');
 
1060
            p.put(elem, len);
 
1061
        }
 
1062
        whitespaceskip;
 
1063
    }
 
1064
    p.add('\0');
 
1065
    return newstring(p.getbuf());
 
1066
}
 
1067
 
 
1068
ICOMMAND(listdel, "ss", (char *list, char *del), commandret = listdel(list, del));
 
1069
ICOMMAND(indexof, "ss", (char *list, char *elem), intret(listincludes(list, elem, strlen(elem))));
 
1070
ICOMMAND(listfind, "sss", (char *var, char *list, char *body), looplist(var, list, body, true));
 
1071
ICOMMAND(looplist, "sss", (char *var, char *list, char *body), looplist(var, list, body, false));
 
1072
 
 
1073
ICOMMAND(+, "ii", (int *a, int *b), intret(*a + *b));
 
1074
ICOMMAND(*, "ii", (int *a, int *b), intret(*a * *b));
 
1075
ICOMMAND(-, "ii", (int *a, int *b), intret(*a - *b));
 
1076
ICOMMAND(+f, "ff", (float *a, float *b), floatret(*a + *b));
 
1077
ICOMMAND(*f, "ff", (float *a, float *b), floatret(*a * *b));
 
1078
ICOMMAND(-f, "ff", (float *a, float *b), floatret(*a - *b));
 
1079
ICOMMAND(=, "ii", (int *a, int *b), intret((int)(*a == *b)));
 
1080
ICOMMAND(!=, "ii", (int *a, int *b), intret((int)(*a != *b)));
 
1081
ICOMMAND(<, "ii", (int *a, int *b), intret((int)(*a < *b)));
 
1082
ICOMMAND(>, "ii", (int *a, int *b), intret((int)(*a > *b)));
 
1083
ICOMMAND(<=, "ii", (int *a, int *b), intret((int)(*a <= *b)));
 
1084
ICOMMAND(>=, "ii", (int *a, int *b), intret((int)(*a >= *b)));
 
1085
ICOMMAND(=f, "ff", (float *a, float *b), intret((int)(*a == *b)));
 
1086
ICOMMAND(!=f, "ff", (float *a, float *b), intret((int)(*a != *b)));
 
1087
ICOMMAND(<f, "ff", (float *a, float *b), intret((int)(*a < *b)));
 
1088
ICOMMAND(>f, "ff", (float *a, float *b), intret((int)(*a > *b)));
 
1089
ICOMMAND(<=f, "ff", (float *a, float *b), intret((int)(*a <= *b)));
 
1090
ICOMMAND(>=f, "ff", (float *a, float *b), intret((int)(*a >= *b)));
 
1091
ICOMMAND(^, "ii", (int *a, int *b), intret(*a ^ *b));
 
1092
ICOMMAND(!, "i", (int *a), intret(*a == 0));
 
1093
ICOMMAND(&, "ii", (int *a, int *b), intret(*a & *b));
 
1094
ICOMMAND(|, "ii", (int *a, int *b), intret(*a | *b));
 
1095
ICOMMAND(~, "i", (int *a), intret(~*a));
 
1096
ICOMMAND(^~, "ii", (int *a, int *b), intret(*a ^ ~*b));
 
1097
ICOMMAND(&~, "ii", (int *a, int *b), intret(*a & ~*b));
 
1098
ICOMMAND(|~, "ii", (int *a, int *b), intret(*a | ~*b));
 
1099
ICOMMAND(<<, "ii", (int *a, int *b), intret(*a << *b));
 
1100
ICOMMAND(>>, "ii", (int *a, int *b), intret(*a >> *b));
 
1101
ICOMMAND(&&, "V", (char **args, int *numargs),
 
1102
{
 
1103
    int val = 1;
 
1104
    loopi(*numargs) { val = execute(args[i]); if(!val) break; }
 
1105
    intret(val);
 
1106
});
 
1107
ICOMMAND(||, "V", (char **args, int *numargs),
 
1108
{
 
1109
    int val = 0;
 
1110
    loopi(*numargs) { val = execute(args[i]); if(val) break; }
 
1111
    intret(val);
 
1112
});
 
1113
 
 
1114
ICOMMAND(div, "ii", (int *a, int *b), intret(*b ? *a / *b : 0));
 
1115
ICOMMAND(mod, "ii", (int *a, int *b), intret(*b ? *a % *b : 0));
 
1116
ICOMMAND(divf, "ff", (float *a, float *b), floatret(*b ? *a / *b : 0));
 
1117
ICOMMAND(modf, "ff", (float *a, float *b), floatret(*b ? fmod(*a, *b) : 0));
 
1118
ICOMMAND(sin, "f", (float *a), floatret(sin(*a*RAD)));
 
1119
ICOMMAND(cos, "f", (float *a), floatret(cos(*a*RAD)));
 
1120
ICOMMAND(tan, "f", (float *a), floatret(tan(*a*RAD)));
 
1121
ICOMMAND(asin, "f", (float *a), floatret(asin(*a)/RAD));
 
1122
ICOMMAND(acos, "f", (float *a), floatret(acos(*a)/RAD));
 
1123
ICOMMAND(atan, "f", (float *a), floatret(atan(*a)/RAD));
 
1124
ICOMMAND(sqrt, "f", (float *a), floatret(sqrt(*a)));
 
1125
ICOMMAND(pow, "ff", (float *a, float *b), floatret(pow(*a, *b)));
 
1126
ICOMMAND(loge, "f", (float *a), floatret(log(*a)));
 
1127
ICOMMAND(log2, "f", (float *a), floatret(log(*a)/M_LN2));
 
1128
ICOMMAND(log10, "f", (float *a), floatret(log10(*a)));
 
1129
ICOMMAND(exp, "f", (float *a), floatret(exp(*a)));
 
1130
ICOMMAND(min, "V", (char **args, int *numargs),
 
1131
{
 
1132
    int val = *numargs > 0 ? parseint(args[*numargs - 1]) : 0;
 
1133
    loopi(*numargs - 1) val = min(val, parseint(args[i]));
 
1134
    intret(val);
 
1135
});
 
1136
ICOMMAND(max, "V", (char **args, int *numargs),
 
1137
{
 
1138
    int val = *numargs > 0 ? parseint(args[*numargs - 1]) : 0;
 
1139
    loopi(*numargs - 1) val = max(val, parseint(args[i]));
 
1140
    intret(val);
 
1141
});
 
1142
ICOMMAND(minf, "V", (char **args, int *numargs),
 
1143
{
 
1144
    float val = *numargs > 0 ? parsefloat(args[*numargs - 1]) : 0.0f;
 
1145
    loopi(*numargs - 1) val = min(val, parsefloat(args[i]));
 
1146
    floatret(val);
 
1147
});
 
1148
ICOMMAND(maxf, "V", (char **args, int *numargs),
 
1149
{
 
1150
    float val = *numargs > 0 ? parsefloat(args[*numargs - 1]) : 0.0f;
 
1151
    loopi(*numargs - 1) val = max(val, parsefloat(args[i]));
 
1152
    floatret(val);
 
1153
});
 
1154
 
 
1155
ICOMMAND(cond, "V", (char **args, int *numargs),
 
1156
{
 
1157
    for(int i = 0; i < *numargs; i += 2)
 
1158
    {
 
1159
        if(execute(args[i]))
 
1160
        {
 
1161
            if(i+1 < *numargs) commandret = executeret(args[i+1]);
 
1162
            break;
 
1163
        }
 
1164
    }
 
1165
});
 
1166
#define CASECOMMAND(name, fmt, type, compare) \
 
1167
    ICOMMAND(name, fmt "V", (type *val, char **args, int *numargs), \
 
1168
    { \
 
1169
        int i; \
 
1170
        for(i = 1; i+1 < *numargs; i += 2) \
 
1171
        { \
 
1172
            if(compare) \
 
1173
            { \
 
1174
                commandret = executeret(args[i+1]); \
 
1175
                return; \
 
1176
            } \
 
1177
        } \
 
1178
        if(i < *numargs) commandret = executeret(args[i]); \
 
1179
    })
 
1180
CASECOMMAND(case, "i", int, parseint(args[i]) == *val);
 
1181
CASECOMMAND(casef, "f", float, parsefloat(args[i]) == *val);
 
1182
CASECOMMAND(cases, "s", char, !strcmp(args[i], val));
 
1183
 
 
1184
ICOMMAND(rnd, "ii", (int *a, int *b), intret(*a - *b > 0 ? rnd(*a - *b) + *b : *b));
 
1185
ICOMMAND(strcmp, "ss", (char *a, char *b), intret(strcmp(a,b)==0));
 
1186
ICOMMAND(=s, "ss", (char *a, char *b), intret(strcmp(a,b)==0));
 
1187
ICOMMAND(!=s, "ss", (char *a, char *b), intret(strcmp(a,b)!=0));
 
1188
ICOMMAND(<s, "ss", (char *a, char *b), intret(strcmp(a,b)<0));
 
1189
ICOMMAND(>s, "ss", (char *a, char *b), intret(strcmp(a,b)>0));
 
1190
ICOMMAND(<=s, "ss", (char *a, char *b), intret(strcmp(a,b)<=0));
 
1191
ICOMMAND(>=s, "ss", (char *a, char *b), intret(strcmp(a,b)>=0));
 
1192
ICOMMAND(echo, "C", (char *s), conoutf("%s", s));
 
1193
ICOMMAND(error, "C", (char *s), conoutf(CON_ERROR, s));
 
1194
ICOMMAND(strstr, "ss", (char *a, char *b), { char *s = strstr(a, b); intret(s ? s-a : -1); });
 
1195
ICOMMAND(strlen, "s", (char *s), intret(strlen(s)));
 
1196
 
 
1197
char *strreplace(const char *s, const char *oldval, const char *newval)
 
1198
{
 
1199
    vector<char> buf;
 
1200
 
 
1201
    int oldlen = strlen(oldval);
 
1202
    if(!oldlen) return newstring(s);
 
1203
    for(;;)
 
1204
    {
 
1205
        const char *found = strstr(s, oldval);
 
1206
        if(found)
 
1207
        {
 
1208
            while(s < found) buf.add(*s++);
 
1209
            for(const char *n = newval; *n; n++) buf.add(*n);
 
1210
            s = found + oldlen;
 
1211
        }
 
1212
        else
 
1213
        {
 
1214
            while(*s) buf.add(*s++);
 
1215
            buf.add('\0');
 
1216
            return newstring(buf.getbuf(), buf.length());
 
1217
        }
 
1218
    }
 
1219
}
 
1220
 
 
1221
ICOMMAND(strreplace, "sss", (char *s, char *o, char *n), commandret = strreplace(s, o, n));
 
1222
 
 
1223
#ifndef STANDALONE
 
1224
ICOMMAND(getmillis, "i", (int *total), intret(*total ? totalmillis : lastmillis));
 
1225
 
 
1226
struct sleepcmd
 
1227
{
 
1228
    int delay, millis;
 
1229
    char *command;
 
1230
    bool override, persist;
 
1231
};
 
1232
vector<sleepcmd> sleepcmds;
 
1233
 
 
1234
void addsleep(int *msec, char *cmd)
 
1235
{
 
1236
    sleepcmd &s = sleepcmds.add();
 
1237
    s.delay = max(*msec, 1);
 
1238
    s.millis = lastmillis;
 
1239
    s.command = newstring(cmd);
 
1240
    s.override = overrideidents;
 
1241
    s.persist = persistidents;
 
1242
}
 
1243
 
 
1244
COMMANDN(sleep, addsleep, "is");
 
1245
 
 
1246
void checksleep(int millis)
 
1247
{
 
1248
    loopv(sleepcmds)
 
1249
    {
 
1250
        sleepcmd &s = sleepcmds[i];
 
1251
        if(millis - s.millis >= s.delay)
 
1252
        {
 
1253
            char *cmd = s.command; // execute might create more sleep commands
 
1254
            s.command = NULL;
 
1255
            bool waspersisting = persistidents, wasoverriding = overrideidents;
 
1256
            persistidents = s.persist;
 
1257
            overrideidents = s.override;
 
1258
            execute(cmd);
 
1259
            persistidents = waspersisting;
 
1260
            overrideidents = wasoverriding;
 
1261
            delete[] cmd;
 
1262
            if(sleepcmds.inrange(i) && !sleepcmds[i].command) sleepcmds.remove(i--);
 
1263
        }
 
1264
    }
 
1265
}
 
1266
 
 
1267
void clearsleep(bool clearoverrides)
 
1268
{
 
1269
    int len = 0;
 
1270
    loopv(sleepcmds) if(sleepcmds[i].command)
 
1271
    {
 
1272
        if(clearoverrides && !sleepcmds[i].override) sleepcmds[len++] = sleepcmds[i];
 
1273
        else delete[] sleepcmds[i].command;
 
1274
    }
 
1275
    sleepcmds.shrink(len);
 
1276
}
 
1277
 
 
1278
void clearsleep_(int *clearoverrides)
 
1279
{
 
1280
    clearsleep(*clearoverrides!=0 || overrideidents);
 
1281
}
 
1282
 
 
1283
COMMANDN(clearsleep, clearsleep_, "i");
 
1284
#endif
 
1285
 
 
1286
 
 
1287
// XXX Emscripten: console.cpp
 
1288
 
 
1289
struct cline { char *line; int type, outtime; };
 
1290
vector<cline> conlines;
 
1291
 
 
1292
int commandmillis = -1;
 
1293
string commandbuf;
 
1294
char *commandaction = NULL, *commandprompt = NULL;
 
1295
int commandpos = -1;
 
1296
int totalmillis = -1; // XXX Emscripten
 
1297
 
 
1298
VARFP(maxcon, 10, 200, 1000, { while(conlines.length() > maxcon) delete[] conlines.pop().line; });
 
1299
 
 
1300
#define CONSTRLEN 512
 
1301
 
 
1302
void conline(int type, const char *sf)        // add a line to the console buffer
 
1303
{
 
1304
    cline cl;
 
1305
    cl.line = conlines.length()>maxcon ? conlines.pop().line : newstring("", CONSTRLEN-1);   // constrain the buffer size
 
1306
    cl.type = type;
 
1307
    cl.outtime = totalmillis;                       // for how long to keep line on screen
 
1308
    conlines.insert(0, cl);
 
1309
    copystring(cl.line, sf, CONSTRLEN);
 
1310
}
 
1311
 
 
1312
void conoutfv(int type, const char *fmt, va_list args)
 
1313
{
 
1314
    static char buf[CONSTRLEN];
 
1315
    vformatstring(buf, fmt, args, sizeof(char)*CONSTRLEN);
 
1316
    conline(type, buf);
 
1317
    //filtertext(buf, buf); // XXX Emscripten
 
1318
    puts(buf);
 
1319
}
 
1320
 
 
1321
void conoutf(const char *fmt, ...)
 
1322
{
 
1323
    va_list args;
 
1324
    va_start(args, fmt);
 
1325
    conoutfv(CON_INFO, fmt, args);
 
1326
    va_end(args); 
 
1327
}
 
1328
 
 
1329
void conoutf(int type, const char *fmt, ...)
 
1330
{
 
1331
    va_list args;
 
1332
    va_start(args, fmt);
 
1333
    conoutfv(type, fmt, args);
 
1334
    va_end(args);
 
1335
}
 
1336
// XXX =======================================
 
1337
 
 
1338
// XXX Emscripten: tools.cpp
 
1339
 
 
1340
#define N              (624)             
 
1341
#define M              (397)                
 
1342
#define K              (0x9908B0DFU)       
 
1343
#define hiBit(u)       ((u) & 0x80000000U)  
 
1344
#define loBit(u)       ((u) & 0x00000001U)  
 
1345
#define loBits(u)      ((u) & 0x7FFFFFFFU)  
 
1346
#define mixBits(u, v)  (hiBit(u)|loBits(v)) 
 
1347
 
 
1348
static uint state[N+1];     
 
1349
static uint *next;          
 
1350
static int left = -1;     
 
1351
 
 
1352
void seedMT(uint seed)
 
1353
{
 
1354
    register uint x = (seed | 1U) & 0xFFFFFFFFU, *s = state;
 
1355
    register int j;
 
1356
    for(left=0, *s++=x, j=N; --j; *s++ = (x*=69069U) & 0xFFFFFFFFU);
 
1357
}
 
1358
 
 
1359
uint reloadMT(void)
 
1360
{
 
1361
    register uint *p0=state, *p2=state+2, *pM=state+M, s0, s1;
 
1362
    register int j;
 
1363
    if(left < -1) seedMT(time(NULL));
 
1364
    left=N-1, next=state+1;
 
1365
    for(s0=state[0], s1=state[1], j=N-M+1; --j; s0=s1, s1=*p2++) *p0++ = *pM++ ^ (mixBits(s0, s1) >> 1) ^ (loBit(s1) ? K : 0U);
 
1366
    for(pM=state, j=M; --j; s0=s1, s1=*p2++) *p0++ = *pM++ ^ (mixBits(s0, s1) >> 1) ^ (loBit(s1) ? K : 0U);
 
1367
    s1=state[0], *p0 = *pM ^ (mixBits(s0, s1) >> 1) ^ (loBit(s1) ? K : 0U);
 
1368
    s1 ^= (s1 >> 11);
 
1369
    s1 ^= (s1 <<  7) & 0x9D2C5680U;
 
1370
    s1 ^= (s1 << 15) & 0xEFC60000U;
 
1371
    return(s1 ^ (s1 >> 18));
 
1372
}
 
1373
 
 
1374
uint randomMT(void)
 
1375
{
 
1376
    uint y;
 
1377
    if(--left < 0) return(reloadMT());
 
1378
    y  = *next++;
 
1379
    y ^= (y >> 11);
 
1380
    y ^= (y <<  7) & 0x9D2C5680U;
 
1381
    y ^= (y << 15) & 0xEFC60000U;
 
1382
    return(y ^ (y >> 18));
 
1383
}
 
1384
 
 
1385
// XXX ==============================================
 
1386
 
 
1387
// XXX Emscripten: main.cpp
 
1388
 
 
1389
void fatal(const char *s, ...)    // failure exit
 
1390
{
 
1391
    static int errors = 0;
 
1392
    errors++;
 
1393
 
 
1394
    if(errors <= 2) // print up to one extra recursive error
 
1395
    {
 
1396
        defvformatstring(msg,s,s);
 
1397
        puts(msg);
 
1398
    }
 
1399
 
 
1400
    exit(EXIT_FAILURE);
 
1401
}
 
1402
 
 
1403
VARP(somevar, 0, 0, 1024);
 
1404
 
 
1405
int main()
 
1406
{
 
1407
  printf("*\n");
 
1408
  execute("somevar 9");
 
1409
  execute("temp = (+ 22 $somevar)");
 
1410
  execute("if (> $temp 30) [ temp = (+ $temp 1) ] [ temp = (* $temp 2) ]");
 
1411
  execute("if (< $temp 30) [ temp = 0 ] [ temp = (+ $temp 1) ]");
 
1412
  execute("echo [Temp is] $temp");
 
1413
  printf("%d\n", getvar("somevar"));
 
1414
  execute("x = 2");
 
1415
  execute("push x 5");
 
1416
  execute("push x 11");
 
1417
  execute("pop x");
 
1418
  execute("echo $x");
 
1419
  execute("greet = [ echo hello, $arg1 ]");
 
1420
  execute("greet everyone");
 
1421
  printf("*\n");
 
1422
  return 0;
 
1423
}
 
1424
 
 
1425
// XXX ===============================