Line data Source code
1 : /**
2 : * @file cubestd.cpp
3 : * @brief cubescript commands
4 : *
5 : * these functions & assignment macros define standard functions used with the language
6 : * the language does not otherwise define special operators (besides bracket semantics)
7 : *
8 : * including, but not limited to:
9 : * - file handling
10 : * - arithmetic and boolean operators
11 : * - control statements
12 : */
13 :
14 : #include "../libprimis-headers/cube.h"
15 : #include "../../shared/stream.h"
16 :
17 : #include "console.h"
18 : #include "control.h"
19 : #include "cs.h"
20 :
21 : #include "render/hud.h"
22 :
23 : /**
24 : * @brief executes the cubescript in a file located at a relative path to the game's home dir
25 : *
26 : * @param cfgfile a pointer to the name of the file
27 : * @param if true, prints out a message if there is no file found
28 : *
29 : * @return true if file was read for execution, false if this fails
30 : */
31 7 : bool execfile(const char *cfgfile, bool msg)
32 : {
33 : string s;
34 7 : copystring(s, cfgfile);
35 7 : char *buf = loadfile(path(s), nullptr);
36 7 : if(!buf)
37 : {
38 7 : if(msg)
39 : {
40 1 : conoutf(Console_Error, "could not read \"%s\"", cfgfile);
41 : }
42 7 : return false;
43 : }
44 0 : const char *oldsourcefile = sourcefile,
45 0 : *oldsourcestr = sourcestr;
46 0 : sourcefile = cfgfile;
47 0 : sourcestr = buf;
48 0 : execute(buf);
49 0 : sourcefile = oldsourcefile;
50 0 : sourcestr = oldsourcestr;
51 0 : delete[] buf;
52 0 : return true;
53 : }
54 :
55 : //cmd
56 1 : static void exec(const char *file, int *msg)
57 : {
58 1 : intret(execfile(file, *msg != 0) ? 1 : 0);
59 1 : }
60 :
61 : /**
62 : * @brief Converts a string to use CubeScript escape characters
63 : *
64 : * Escapes strings by converting \<special char> to ^<special char>
65 : * (^ is the escape char in cubescript)
66 : *
67 : * @param s the string to convert
68 : *
69 : * @return an entry in the strbuf containing the converted string
70 : */
71 3 : const char *escapestring(const char *s)
72 : {
73 3 : stridx = (stridx + 1)%4;
74 3 : std::vector<char> &buf = strbuf[stridx];
75 3 : buf.clear();
76 3 : buf.push_back('"');
77 55 : for(; *s; s++)
78 : {
79 52 : switch(*s)
80 : {
81 2 : case '\n':
82 : {
83 2 : buf.push_back('^');
84 2 : buf.push_back('n');
85 2 : break;
86 : }
87 2 : case '\t':
88 : {
89 2 : buf.push_back('^');
90 2 : buf.push_back('t');
91 2 : break;
92 : }
93 2 : case '\f':
94 : {
95 2 : buf.push_back('^');
96 2 : buf.push_back('f');
97 2 : break;
98 : }
99 1 : case '"':
100 : {
101 1 : buf.push_back('^');
102 1 : buf.push_back('"');
103 1 : break;
104 : }
105 1 : case '^':
106 : {
107 1 : buf.push_back('^');
108 1 : buf.push_back('^');
109 1 : break;
110 : }
111 44 : default:
112 : {
113 44 : buf.push_back(*s);
114 44 : break;
115 : }
116 : }
117 : }
118 3 : buf.push_back('\"');
119 3 : buf.push_back('\0');
120 3 : return buf.data();
121 : }
122 :
123 1 : static void escapecmd(const char *s)
124 : {
125 1 : result(escapestring(s));
126 1 : }
127 :
128 1 : static void unescapecmd(const char *s)
129 : {
130 1 : int len = std::strlen(s);
131 1 : char *d = newstring(len);
132 1 : unescapestring(d, s, &s[len]);
133 1 : stringret(d);
134 1 : }
135 :
136 2 : const char *escapeid(const char *s)
137 : {
138 2 : const char *end = s + std::strcspn(s, "\"/;()[]@ \f\t\r\n\0");
139 2 : return *end ? escapestring(s) : s;
140 : }
141 :
142 16 : bool validateblock(const char *s)
143 : {
144 16 : constexpr int maxbrak = 100;
145 : static std::array<char, maxbrak> brakstack;
146 16 : int brakdepth = 0;
147 308 : for(; *s; s++)
148 : {
149 302 : switch(*s)
150 : {
151 220 : case '[':
152 : case '(':
153 : {
154 220 : if(brakdepth >= maxbrak)
155 : {
156 2 : return false;
157 : }
158 218 : brakstack[brakdepth++] = *s;
159 218 : break;
160 : }
161 8 : case ']':
162 : {
163 8 : if(brakdepth <= 0 || brakstack[--brakdepth] != '[')
164 : {
165 2 : return false;
166 : }
167 6 : break;
168 : }
169 8 : case ')':
170 : {
171 8 : if(brakdepth <= 0 || brakstack[--brakdepth] != '(')
172 : {
173 2 : return false;
174 : }
175 6 : break;
176 : }
177 2 : case '"':
178 : {
179 2 : s = parsestring(s + 1);
180 2 : if(*s != '"')
181 : {
182 1 : return false;
183 : }
184 1 : break;
185 : }
186 2 : case '/':
187 : {
188 2 : if(s[1] == '/')
189 : {
190 1 : return false;
191 : }
192 1 : break;
193 : }
194 2 : case '@':
195 : case '\f':
196 : {
197 2 : return false;
198 : }
199 : }
200 : }
201 6 : return brakdepth == 0;
202 : }
203 :
204 0 : static const char *escapeid(ident &id)
205 : {
206 0 : return escapeid(id.name);
207 : }
208 :
209 1 : void writecfg(const char *savedconfig, const char *autoexec, const char *defaultconfig, const char *name)
210 : {
211 1 : std::fstream f;
212 1 : conoutf("writing to %s", copypath(name && name[0] ? name : savedconfig));
213 1 : f.open(copypath(name && name[0] ? name : savedconfig), std::ios::out);
214 1 : if(!f.is_open())
215 : {
216 1 : conoutf("file not opened, config not written");
217 1 : return;
218 : }
219 : //write the top of file comment manually
220 0 : f << "// automatically written on exit, DO NOT MODIFY\n// delete this file to have";
221 0 : if(defaultconfig)
222 : {
223 0 : f << defaultconfig;
224 : }
225 0 : f << "overwrite these settings\n// modify settings in game, or put settings in";
226 0 : if(autoexec)
227 : {
228 0 : f << autoexec;
229 : }
230 0 : f << "to override anything\n\n";
231 0 : writecrosshairs(f);
232 0 : std::vector<ident *> ids;
233 0 : for(auto& [k, id] : idents)
234 : {
235 0 : ids.push_back(&id);
236 : }
237 0 : std::sort(ids.begin(), ids.end());
238 0 : for(size_t i = 0; i < ids.size(); i++)
239 : {
240 0 : ident &id = *ids[i];
241 0 : if(id.flags&Idf_Persist)
242 : {
243 0 : switch(id.type)
244 : {
245 0 : case Id_Var:
246 : {
247 0 : f << escapeid(id) << " " << *id.val.storage.i << std::endl;
248 0 : break;
249 : }
250 0 : case Id_FloatVar:
251 : {
252 0 : f << escapeid(id) << " " << floatstr(*id.val.storage.f) << std::endl;
253 0 : break;
254 : }
255 0 : case Id_StringVar:
256 : {
257 0 : f << escapeid(id) << " " << escapestring(*id.val.storage.s) << std::endl;
258 0 : break;
259 : }
260 : }
261 : }
262 : }
263 0 : writebinds(f);
264 0 : for(ident *&id : ids)
265 : {
266 0 : if(id->type==Id_Alias && id->flags&Idf_Persist && !(id->flags&Idf_Overridden))
267 : {
268 0 : switch(id->valtype)
269 : {
270 0 : case Value_String:
271 : {
272 0 : if(!id->alias.val.s[0])
273 : {
274 0 : break;
275 : }
276 0 : if(!validateblock(id->alias.val.s))
277 : {
278 0 : f << escapeid(*id) << " = " << escapestring(id->alias.val.s) << std::endl;
279 0 : break;
280 : }
281 : }
282 : [[fallthrough]];
283 : case Value_Float:
284 : case Value_Integer:
285 : {
286 0 : f << escapeid(*id) << " = [" << id->getstr() << "]" << std::endl;
287 0 : break;
288 : }
289 : }
290 : }
291 : }
292 0 : writecompletions(f);
293 0 : f.close();
294 1 : }
295 :
296 1 : static void changedvars()
297 : {
298 1 : std::vector<const ident *> ids;
299 1104 : for(const auto& [k, id] : idents)
300 : {
301 1103 : if(id.flags&Idf_Overridden)
302 : {
303 0 : ids.push_back(&id);
304 : }
305 : }
306 1 : std::sort(ids.begin(), ids.end());
307 1 : for(const ident *i: ids)
308 : {
309 0 : printvar(i);
310 : }
311 1 : }
312 :
313 : static std::array<string, 4> retbuf;
314 : static int retidx = 0;
315 :
316 : //cs.h
317 68 : void intformat(char *buf, int v, int len)
318 : {
319 68 : nformatstring(buf, len, "%d", v);
320 68 : }
321 :
322 : //cs.h
323 116 : void floatformat(char *buf, float v, int len)
324 : {
325 116 : nformatstring(buf, len, v==static_cast<int>(v) ? "%.1f" : "%.7g", v);
326 116 : }
327 :
328 60 : const char *intstr(int v)
329 : {
330 60 : retidx = (retidx + 1)%4;
331 60 : intformat(retbuf[retidx], v);
332 60 : return retbuf[retidx];
333 : }
334 :
335 0 : void getval(const identval &v, int type, tagval &r)
336 : {
337 0 : switch(type)
338 : {
339 0 : case Value_String:
340 : case Value_Macro:
341 : case Value_CString:
342 : {
343 0 : r.setstr(newstring(v.s));
344 0 : break;
345 : }
346 0 : case Value_Integer:
347 : {
348 0 : r.setint(v.i);
349 0 : break;
350 : }
351 0 : case Value_Float:
352 : {
353 0 : r.setfloat(v.f);
354 0 : break;
355 : }
356 0 : default:
357 : {
358 0 : r.setnull();
359 0 : break;
360 : }
361 : }
362 0 : }
363 :
364 : /**
365 : * @brief Returns the value of the specified type inside the given identval.
366 : *
367 : * The return value for int/float is not heap allocated, but instead a circular
368 : * buffer that remains allocated for the life of the program.
369 : *
370 : * The buffer is formatted by intformat/floatformat if the type enclosed is an
371 : * int/float, or is just returned directly in the case of a string.
372 : *
373 : * @param v the identval to extract a string from
374 : * @param type the value type inside the identval
375 : *
376 : * @return pointer to the retbuf with the formatted int string inside it
377 : */
378 354 : static const char *getstr(const identval &v, int type)
379 : {
380 354 : switch(type)
381 : {
382 247 : case Value_String:
383 : case Value_Macro:
384 : case Value_CString:
385 : {
386 247 : return v.s;
387 : }
388 60 : case Value_Integer:
389 : {
390 60 : return intstr(v.i);
391 : }
392 0 : case Value_Float:
393 : {
394 0 : return floatstr(v.f);
395 : }
396 47 : default:
397 : {
398 47 : return "";
399 : }
400 : }
401 : }
402 :
403 247 : const char *tagval::getstr() const
404 : {
405 247 : return ::getstr(*this, type);
406 : }
407 :
408 107 : const char *ident::getstr() const
409 : {
410 107 : return ::getstr(alias.val, valtype);
411 : }
412 :
413 531 : void intret(int v)
414 : {
415 531 : commandret->setint(v);
416 531 : }
417 :
418 112 : const char *floatstr(float v)
419 : {
420 112 : retidx = (retidx + 1)%4;
421 112 : floatformat(retbuf[retidx], v);
422 112 : return retbuf[retidx];
423 : }
424 :
425 161 : void floatret(float v)
426 : {
427 161 : commandret->setfloat(v);
428 161 : }
429 :
430 0 : const char *numberstr(double v)
431 : {
432 0 : auto numberformat = [] (char *buf, double v)
433 : {
434 0 : int i = static_cast<int>(v);
435 0 : constexpr int len = 20;
436 0 : if(v == i)
437 : {
438 0 : nformatstring(buf, len, "%d", i);
439 : }
440 : else
441 : {
442 0 : nformatstring(buf, len, "%.7g", v);
443 : }
444 0 : };
445 :
446 0 : retidx = (retidx + 1)%4;
447 0 : numberformat(retbuf[retidx], v);
448 0 : return retbuf[retidx];
449 : }
450 :
451 0 : static void loopiter(ident &id, identstack &stack, const tagval &v)
452 : {
453 0 : if(id.alias.stack != &stack)
454 : {
455 0 : pusharg(id, v, stack);
456 0 : id.flags &= ~Idf_Unknown;
457 : }
458 : else
459 : {
460 0 : if(id.valtype == Value_String)
461 : {
462 0 : delete[] id.alias.val.s;
463 : }
464 0 : cleancode(id);
465 0 : id.setval(v);
466 : }
467 0 : }
468 :
469 0 : void loopiter(ident *id, identstack &stack, int i)
470 : {
471 : tagval v;
472 0 : v.setint(i);
473 0 : loopiter(*id, stack, v);
474 0 : }
475 :
476 0 : void loopend(ident *id, identstack &stack)
477 : {
478 0 : if(id->alias.stack == &stack)
479 : {
480 0 : poparg(*id);
481 : }
482 0 : }
483 :
484 221 : static void setiter(ident &id, int i, identstack &stack)
485 : {
486 221 : if(id.alias.stack == &stack)
487 : {
488 171 : if(id.valtype != Value_Integer)
489 : {
490 0 : if(id.valtype == Value_String)
491 : {
492 0 : delete[] id.alias.val.s;
493 : }
494 0 : cleancode(id);
495 0 : id.valtype = Value_Integer;
496 : }
497 171 : id.alias.val.i = i;
498 : }
499 : else
500 : {
501 : tagval t;
502 50 : t.setint(i);
503 50 : pusharg(id, t, stack);
504 50 : id.flags &= ~Idf_Unknown;
505 : }
506 221 : }
507 :
508 30 : static void doloop(ident &id, int offset, int n, int step, const uint *body)
509 : {
510 30 : if(n <= 0 || id.type != Id_Alias)
511 : {
512 12 : return;
513 : }
514 : identstack stack;
515 162 : for(int i = 0; i < n; ++i)
516 : {
517 144 : setiter(id, offset + i*step, stack);
518 144 : execute(body);
519 : }
520 18 : poparg(id);
521 : }
522 :
523 39 : static void loopconc(ident &id, int offset, int n, const uint *body, bool space)
524 : {
525 39 : if(n <= 0 || id.type != Id_Alias)
526 : {
527 7 : return;
528 : }
529 : identstack stack;
530 32 : std::vector<char> s;
531 109 : for(int i = 0; i < n; ++i)
532 : {
533 77 : setiter(id, offset + i, stack);
534 : tagval v;
535 77 : executeret(body, v);
536 77 : const char *vstr = v.getstr();
537 77 : int len = std::strlen(vstr);
538 77 : if(space && i)
539 : {
540 45 : s.push_back(' ');
541 : }
542 216 : for(int j = 0; j < len; ++j)
543 : {
544 139 : s.push_back(vstr[j]);
545 : }
546 77 : freearg(v);
547 : }
548 32 : if(n > 0)
549 : {
550 32 : poparg(id);
551 : }
552 32 : s.push_back('\0');
553 32 : char * arr = new char[s.size()];
554 32 : std::memcpy(arr, s.data(), s.size());
555 32 : commandret->setstr(arr);
556 32 : }
557 :
558 13 : static void concatword(tagval *v, int n)
559 : {
560 13 : commandret->setstr(conc(v, n, false));
561 13 : }
562 :
563 2 : static void append(ident *id, const tagval *v, bool space)
564 : {
565 2 : if(id->type != Id_Alias || v->type == Value_Null)
566 : {
567 2 : return;
568 : }
569 : tagval r;
570 0 : const char *prefix = id->getstr();
571 0 : if(prefix[0])
572 : {
573 0 : r.setstr(conc(v, 1, space, prefix));
574 : }
575 : else
576 : {
577 0 : v->getval(r);
578 : }
579 0 : if(id->index < Max_Args)
580 : {
581 0 : setarg(*id, r);
582 : }
583 : else
584 : {
585 0 : setalias(*id, r);
586 : }
587 :
588 : }
589 :
590 6 : void result(tagval &v)
591 : {
592 6 : *commandret = v;
593 6 : v.type = Value_Null;
594 6 : }
595 :
596 31 : void stringret(char *s)
597 : {
598 31 : commandret->setstr(s);
599 31 : }
600 :
601 15 : void result(const char *s)
602 : {
603 15 : commandret->setstr(newstring(s));
604 15 : }
605 :
606 7 : static void format(tagval *args, int numargs)
607 : {
608 7 : std::vector<char> s;
609 7 : if(!args)
610 : {
611 0 : conoutf(Console_Error, "no parameters to format");
612 0 : return;
613 : }
614 7 : const char *f = args[0].getstr();
615 32 : while(*f)
616 : {
617 25 : int c = *f++;
618 25 : if(c == '%')
619 : {
620 0 : int i = *f++;
621 0 : if(i >= '1' && i <= '9')
622 : {
623 0 : i -= '0';
624 0 : const char *sub = i < numargs ? args[i].getstr() : "";
625 0 : while(*sub)
626 : {
627 0 : s.push_back(*sub++);
628 : }
629 0 : }
630 : else
631 : {
632 0 : s.push_back(i);
633 : }
634 : }
635 : else
636 : {
637 25 : s.push_back(c);
638 : }
639 : }
640 7 : s.push_back('\0');
641 : //create new array to pass back
642 7 : char * arr = new char[s.size()];
643 7 : std::memcpy(arr, s.data(), s.size());
644 7 : commandret->setstr(arr);
645 7 : }
646 :
647 : static const char *liststart = nullptr,
648 : *listend = nullptr,
649 : *listquotestart = nullptr,
650 : *listquoteend = nullptr;
651 :
652 784 : static void skiplist(const char *&p)
653 : {
654 : for(;;)
655 : {
656 792 : p += std::strspn(p, " \t\r\n");
657 788 : if(p[0]!='/' || p[1]!='/')
658 : {
659 : break;
660 : }
661 4 : p += std::strcspn(p, "\n\0");
662 : }
663 784 : }
664 :
665 449 : static bool parselist(const char *&s, const char *&start = liststart, const char *&end = listend, const char *"estart = listquotestart, const char *"eend = listquoteend)
666 : {
667 449 : skiplist(s);
668 449 : switch(*s)
669 : {
670 2 : case '"':
671 : {
672 2 : quotestart = s++;
673 2 : start = s;
674 2 : s = parsestring(s);
675 2 : end = s;
676 2 : if(*s == '"')
677 : {
678 2 : s++;
679 : }
680 2 : quoteend = s;
681 2 : break;
682 : }
683 7 : case '(':
684 : case '[':
685 7 : quotestart = s;
686 7 : start = s+1;
687 7 : for(int braktype = *s++, brak = 1;;)
688 : {
689 7 : s += std::strcspn(s, "\"/;()[]\0");
690 7 : int c = *s++;
691 7 : switch(c)
692 : {
693 1 : case '\0':
694 : {
695 1 : s--;
696 1 : quoteend = end = s;
697 1 : return true;
698 : }
699 0 : case '"':
700 : {
701 0 : s = parsestring(s);
702 0 : if(*s == '"')
703 : {
704 0 : s++;
705 : }
706 0 : break;
707 : }
708 0 : case '/':
709 : {
710 0 : if(*s == '/')
711 : {
712 0 : s += std::strcspn(s, "\n\0");
713 : }
714 0 : break;
715 : }
716 0 : case '(':
717 : case '[':
718 : {
719 0 : if(c == braktype)
720 : {
721 0 : brak++;
722 : }
723 0 : break;
724 : }
725 3 : case ')':
726 : {
727 3 : if(braktype == '(' && --brak <= 0)
728 : {
729 3 : goto endblock;
730 : }
731 0 : break;
732 : }
733 3 : case ']':
734 : {
735 3 : if(braktype == '[' && --brak <= 0)
736 : {
737 3 : goto endblock;
738 : }
739 0 : break;
740 : }
741 : }
742 0 : }
743 6 : endblock:
744 6 : end = s-1;
745 6 : quoteend = s;
746 6 : break;
747 122 : case '\0':
748 : case ')':
749 : case ']':
750 : {
751 122 : return false;
752 : }
753 318 : default:
754 : {
755 318 : quotestart = start = s;
756 318 : s = parseword(s);
757 318 : quoteend = end = s;
758 318 : break;
759 : }
760 : }
761 326 : skiplist(s);
762 326 : if(*s == ';')
763 : {
764 0 : s++;
765 : }
766 326 : return true;
767 : }
768 :
769 40 : static char *listelem(const char *start = liststart, const char *end = listend, const char *quotestart = listquotestart)
770 : {
771 40 : size_t len = end-start;
772 40 : char *s = newstring(len);
773 40 : if(*quotestart == '"')
774 : {
775 2 : unescapestring(s, start, end);
776 : }
777 : else
778 : {
779 38 : std::memcpy(s, start, len);
780 38 : s[len] = '\0';
781 : }
782 40 : return s;
783 : }
784 :
785 8 : void explodelist(const char *s, std::vector<char *> &elems, int limit)
786 : {
787 : const char *start, *end, *qstart;
788 21 : while((limit < 0 || static_cast<int>(elems.size()) < limit) && parselist(s, start, end, qstart))
789 : {
790 13 : elems.push_back(listelem(start, end, qstart));
791 : }
792 8 : }
793 :
794 9 : void explodelist(const char *s, std::vector<std::string> &elems, int limit)
795 : {
796 : const char *start, *end, *qstart;
797 32 : while((limit < 0 || static_cast<int>(elems.size()) < limit) && parselist(s, start, end, qstart))
798 : {
799 14 : char *elem = listelem(start, end, qstart);
800 14 : elems.push_back(std::string(elem));
801 14 : delete[] elem;
802 : }
803 9 : }
804 :
805 15 : static int listlen(const char *s)
806 : {
807 15 : int n = 0;
808 47 : while(parselist(s))
809 : {
810 32 : n++;
811 : }
812 15 : return n;
813 : }
814 :
815 4 : static void listlencmd(const char *s)
816 : {
817 4 : intret(listlen(s));
818 4 : }
819 :
820 7 : static void at(tagval *args, int numargs)
821 : {
822 7 : if(!numargs)
823 : {
824 0 : return;
825 : }
826 7 : const char *start = args[0].getstr(),
827 7 : *end = start + std::strlen(start),
828 7 : *qstart = "";
829 14 : for(int i = 1; i < numargs; i++)
830 : {
831 7 : const char *list = start;
832 7 : int pos = args[i].getint();
833 11 : for(; pos > 0; pos--)
834 : {
835 4 : if(!parselist(list))
836 : {
837 0 : break;
838 : }
839 : }
840 7 : if(pos > 0 || !parselist(list, start, end, qstart))
841 : {
842 2 : start = end = qstart = "";
843 : }
844 : }
845 7 : commandret->setstr(listelem(start, end, qstart));
846 : }
847 :
848 11 : static void sublist(const char *s, const int *skip, const int *count, const int *numargs)
849 : {
850 11 : int offset = std::max(*skip, 0),
851 11 : len = *numargs >= 3 ? std::max(*count, 0) : -1;
852 16 : for(int i = 0; i < offset; ++i)
853 : {
854 5 : if(!parselist(s))
855 : {
856 0 : break;
857 : }
858 : }
859 11 : if(len < 0)
860 : {
861 4 : if(offset > 0)
862 : {
863 1 : skiplist(s);
864 : }
865 4 : commandret->setstr(newstring(s));
866 4 : return;
867 : }
868 7 : const char *list = s,
869 : *start, *end, *qstart,
870 7 : *qend = s;
871 7 : if(len > 0 && parselist(s, start, end, list, qend))
872 : {
873 10 : while(--len > 0 && parselist(s, start, end, qstart, qend))
874 : {
875 : //(empty body)
876 : }
877 : }
878 7 : commandret->setstr(newstring(list, qend - list));
879 : }
880 :
881 109 : static void setiter(ident &id, char *val, identstack &stack)
882 : {
883 109 : if(id.alias.stack == &stack)
884 : {
885 93 : if(id.valtype == Value_String)
886 : {
887 93 : delete[] id.alias.val.s;
888 : }
889 : else
890 : {
891 0 : id.valtype = Value_String;
892 : }
893 93 : cleancode(id);
894 93 : id.alias.val.s = val;
895 : }
896 : else
897 : {
898 : tagval t;
899 16 : t.setstr(val);
900 16 : pusharg(id, t, stack);
901 16 : id.flags &= ~Idf_Unknown;
902 : }
903 109 : }
904 :
905 15 : static void listfind(ident *id, const char *list, const uint *body)
906 : {
907 15 : if(id->type!=Id_Alias)
908 : {
909 2 : intret(-1);
910 2 : return;
911 : }
912 : identstack stack;
913 13 : int n = -1;
914 22 : for(const char *s = list, *start, *end; parselist(s, start, end);)
915 : {
916 16 : ++n;
917 16 : setiter(*id, newstring(start, end-start), stack);
918 16 : if(executebool(body))
919 : {
920 7 : intret(n);
921 7 : goto found;
922 : }
923 : }
924 6 : intret(-1); //if element not found in list
925 13 : found: //if element is found in list
926 13 : if(n >= 0)
927 : {
928 10 : poparg(*id);
929 : }
930 : }
931 :
932 : //note: the goto here is the opposite of listfind above: goto triggers when elem not found
933 1 : static void listfindeq(const char *list, const int *val, const int *skip)
934 : {
935 1 : int n = 0;
936 1 : for(const char *s = list, *start, *end, *qstart; parselist(s, start, end, qstart); n++)
937 : {
938 0 : if(parseint(start) == *val)
939 : {
940 0 : intret(n);
941 0 : return;
942 : }
943 0 : for(int i = 0; i < *skip; ++i)
944 : {
945 0 : if(!parselist(s))
946 : {
947 0 : goto notfound;
948 : }
949 0 : n++;
950 : }
951 : }
952 1 : notfound:
953 1 : intret(-1);
954 : }
955 :
956 11 : static void listassoceq(const char *list, const int *val)
957 : {
958 14 : for(const char *s = list, *start, *end, *qstart; parselist(s, start, end);)
959 : {
960 12 : if(parseint(start) == *val)
961 : {
962 7 : if(parselist(s, start, end, qstart))
963 : {
964 6 : stringret(listelem(start, end, qstart));
965 : }
966 7 : return;
967 : }
968 5 : if(!parselist(s))
969 : {
970 2 : break;
971 : }
972 : }
973 : }
974 :
975 2 : static void looplistconc(ident *id, const char *list, const uint *body, bool space)
976 : {
977 2 : if(id->type!=Id_Alias)
978 : {
979 0 : return;
980 : }
981 : identstack stack;
982 2 : std::vector<char> r;
983 2 : int n = 0;
984 2 : for(const char *s = list, *start, *end, *qstart; parselist(s, start, end, qstart); n++)
985 : {
986 0 : char *val = listelem(start, end, qstart);
987 0 : setiter(*id, val, stack);
988 0 : if(n && space)
989 : {
990 0 : r.push_back(' ');
991 : }
992 : tagval v;
993 0 : executeret(body, v);
994 0 : const char *vstr = v.getstr();
995 0 : int len = std::strlen(vstr);
996 0 : for(int i = 0; i < len; ++i)
997 : {
998 0 : r.push_back(vstr[i]);
999 : }
1000 0 : freearg(v);
1001 : }
1002 2 : if(n)
1003 : {
1004 0 : poparg(*id);
1005 : }
1006 2 : r.push_back('\0');
1007 2 : char * arr = new char[r.size()];
1008 2 : std::memcpy(arr, r.data(), r.size());
1009 2 : commandret->setstr(arr);
1010 2 : }
1011 :
1012 9 : static void listcount(ident *id, const char *list, const uint *body)
1013 : {
1014 9 : if(id->type!=Id_Alias)
1015 : {
1016 0 : return;
1017 : }
1018 : identstack stack;
1019 9 : int n = 0,
1020 9 : r = 0;
1021 21 : for(const char *s = list, *start, *end; parselist(s, start, end); n++)
1022 : {
1023 12 : char *val = newstring(start, end-start);
1024 12 : setiter(*id, val, stack);
1025 12 : if(executebool(body))
1026 : {
1027 10 : r++;
1028 : }
1029 : }
1030 9 : if(n)
1031 : {
1032 5 : poparg(*id);
1033 : }
1034 9 : intret(r);
1035 : }
1036 :
1037 11 : static void prettylist(const char *s, const char *conj)
1038 : {
1039 11 : std::vector<char> p;
1040 : const char *start, *end, *qstart;
1041 39 : for(int len = listlen(s), n = 0; parselist(s, start, end, qstart); n++)
1042 : {
1043 28 : if(*qstart == '"')
1044 : {
1045 0 : p.reserve(end - start + 1);
1046 :
1047 0 : for(int i = 0; i < end - start + 1; ++i)
1048 : {
1049 0 : p.emplace_back();
1050 : }
1051 0 : unescapestring(&(*(p.end() - (end - start + 1))), start, end);
1052 : }
1053 : else
1054 : {
1055 91 : for(int i = 0; i < end - start; ++i)
1056 : {
1057 63 : p.push_back(start[i]);
1058 : }
1059 : }
1060 28 : if(n+1 < len)
1061 : {
1062 20 : if(len > 2 || !conj[0])
1063 : {
1064 19 : p.push_back(',');
1065 : }
1066 20 : if(n+2 == len && conj[0])
1067 : {
1068 5 : p.push_back(' ');
1069 17 : for(size_t i = 0; i < std::strlen(conj); ++i)
1070 : {
1071 12 : p.push_back(conj[i]);
1072 : }
1073 : }
1074 20 : p.push_back(' ');
1075 : }
1076 : }
1077 11 : p.push_back('\0');
1078 : //create new array to pass back
1079 11 : char * arr = new char[p.size()];
1080 11 : std::memcpy(arr, p.data(), p.size());
1081 11 : commandret->setstr(arr);
1082 11 : }
1083 :
1084 : /**
1085 : * @brief Returns the int position of the needle inside the passed list
1086 : *
1087 : * @param list string to seach for
1088 : * @param needle substring to locate inside the list
1089 : * @param needlelen length of the substring
1090 : *
1091 : * @return starting position of the first instance of the needle substring in the list, or -1 if not found
1092 : */
1093 47 : static int listincludes(const char *list, const char *needle, int needlelen)
1094 : {
1095 47 : int offset = 0;
1096 91 : for(const char *s = list, *start, *end; parselist(s, start, end);)
1097 : {
1098 77 : int len = end - start;
1099 77 : if(needlelen == len && !std::strncmp(needle, start, len))
1100 : {
1101 33 : return offset;
1102 : }
1103 44 : offset++;
1104 : }
1105 14 : return -1;
1106 : }
1107 :
1108 8 : static void listsplice(const char *s, const char *vals, const int *skip, const int *count)
1109 : {
1110 8 : int offset = std::max(*skip, 0),
1111 8 : len = std::max(*count, 0);
1112 8 : const char *list = s,
1113 : *start, *end, *qstart,
1114 8 : *qend = s;
1115 14 : for(int i = 0; i < offset; ++i)
1116 : {
1117 6 : if(!parselist(s, start, end, qstart, qend))
1118 : {
1119 0 : break;
1120 : }
1121 : }
1122 8 : std::vector<char> p;
1123 8 : if(qend > list)
1124 : {
1125 36 : for(int i = 0; i < qend - list; ++i)
1126 : {
1127 32 : p.push_back(list[i]);
1128 : }
1129 : }
1130 8 : if(*vals)
1131 : {
1132 6 : if(!p.empty())
1133 : {
1134 4 : p.push_back(' ');
1135 : }
1136 51 : for(size_t i = 0; i < std::strlen(vals); ++i)
1137 : {
1138 45 : p.push_back(vals[i]);
1139 : }
1140 : }
1141 12 : for(int i = 0; i < len; ++i)
1142 : {
1143 5 : if(!parselist(s))
1144 : {
1145 1 : break;
1146 : }
1147 : }
1148 8 : skiplist(s);
1149 8 : switch(*s)
1150 : {
1151 6 : case '\0':
1152 : case ')':
1153 : case ']':
1154 : {
1155 6 : break;
1156 : }
1157 2 : default:
1158 : {
1159 2 : if(!p.empty())
1160 : {
1161 2 : p.push_back(' ');
1162 : }
1163 14 : for(size_t i = 0; i < std::strlen(s); ++i)
1164 : {
1165 12 : p.push_back(s[i]);
1166 : }
1167 2 : break;
1168 : }
1169 : }
1170 8 : p.push_back('\0');
1171 8 : char * arr = new char[p.size()];
1172 8 : std::memcpy(arr, p.data(), p.size());
1173 8 : commandret->setstr(arr);
1174 8 : }
1175 :
1176 : //executes the body for each file in the given path, using ident passed
1177 1 : static void loopfiles(ident *id, const char *dir, const char *ext, const uint *body)
1178 : {
1179 1 : if(id->type!=Id_Alias)
1180 : {
1181 0 : return;
1182 : }
1183 : identstack stack;
1184 1 : std::vector<char *> files;
1185 1 : listfiles(dir, ext[0] ? ext : nullptr, files);
1186 1 : std::sort(files.begin(), files.end());
1187 82 : for(size_t i = 0; i < files.size(); i++)
1188 : {
1189 81 : setiter(*id, files[i], stack);
1190 81 : execute(body);
1191 : }
1192 1 : if(files.size())
1193 : {
1194 1 : poparg(*id);
1195 : }
1196 1 : }
1197 :
1198 : /**
1199 : * @brief Searches for a file at the specified location.
1200 : *
1201 : * Searches for a file at the specified path. If one exists, returns 1 to CubeScript.
1202 : *
1203 : * @param name the path to search for
1204 : */
1205 1 : static void findfile_(const char *name)
1206 : {
1207 : string fname;
1208 1 : copystring(fname, name);
1209 1 : path(fname);
1210 1 : intret(
1211 1 : findzipfile(fname) ||
1212 1 : fileexists(fname, "e") || findfile(fname, "e") ? 1 : 0
1213 : );
1214 1 : }
1215 :
1216 18 : static void sortlist(const char *list, ident *x, ident *y, const uint *body, const uint *unique)
1217 : {
1218 : struct SortItem final
1219 : {
1220 : const char *str, *quotestart, *quoteend;
1221 :
1222 104 : size_t quotelength() const
1223 : {
1224 104 : return static_cast<size_t>(quoteend-quotestart);
1225 : }
1226 : };
1227 :
1228 : struct SortFunction final
1229 : {
1230 : ident *x, *y;
1231 : const uint *body;
1232 :
1233 47 : bool operator()(const SortItem &xval, const SortItem &yval)
1234 : {
1235 47 : if(x->valtype != Value_CString)
1236 : {
1237 14 : x->valtype = Value_CString;
1238 : }
1239 47 : cleancode(*x);
1240 47 : x->alias.val.code = reinterpret_cast<const uint *>(xval.str);
1241 47 : if(y->valtype != Value_CString)
1242 : {
1243 14 : y->valtype = Value_CString;
1244 : }
1245 47 : cleancode(*y);
1246 47 : y->alias.val.code = reinterpret_cast<const uint *>(yval.str);
1247 47 : return executebool(body);
1248 : }
1249 : };
1250 :
1251 18 : if(x == y || x->type != Id_Alias || y->type != Id_Alias)
1252 : {
1253 4 : return;
1254 : }
1255 14 : std::vector<SortItem> items;
1256 14 : size_t clen = std::strlen(list),
1257 14 : total = 0;
1258 14 : char *cstr = newstring(list, clen);
1259 14 : const char *curlist = list,
1260 : *start, *end, *quotestart, *quoteend;
1261 59 : while(parselist(curlist, start, end, quotestart, quoteend))
1262 : {
1263 45 : cstr[end - list] = '\0';
1264 45 : SortItem item = { &cstr[start - list], quotestart, quoteend };
1265 45 : items.push_back(item);
1266 45 : total += item.quotelength();
1267 : }
1268 14 : if(items.empty())
1269 : {
1270 0 : commandret->setstr(cstr);
1271 0 : return;
1272 : }
1273 : identstack xstack, ystack;
1274 14 : pusharg(*x, NullVal(), xstack);
1275 14 : x->flags &= ~Idf_Unknown;
1276 14 : pusharg(*y, NullVal(), ystack);
1277 14 : y->flags &= ~Idf_Unknown;
1278 14 : size_t totalunique = total,
1279 14 : numunique = items.size();
1280 14 : if(body)
1281 : {
1282 8 : SortFunction f = { x, y, body };
1283 8 : std::sort(items.begin(), items.end(), f);
1284 8 : if((*unique&Code_OpMask) != Code_Exit)
1285 : {
1286 3 : f.body = unique;
1287 3 : totalunique = items[0].quotelength();
1288 3 : numunique = 1;
1289 12 : for(size_t i = 1; i < items.size(); i++)
1290 : {
1291 9 : SortItem &item = items[i];
1292 9 : if(f(items[i-1], item))
1293 : {
1294 3 : item.quotestart = nullptr;
1295 : }
1296 : else
1297 : {
1298 6 : totalunique += item.quotelength();
1299 6 : numunique++;
1300 : }
1301 : }
1302 : }
1303 : }
1304 : else
1305 : {
1306 6 : SortFunction f = { x, y, unique };
1307 6 : totalunique = items[0].quotelength();
1308 6 : numunique = 1;
1309 18 : for(size_t i = 1; i < items.size(); i++)
1310 : {
1311 12 : SortItem &item = items[i];
1312 21 : for(size_t j = 0; j < i; ++j)
1313 : {
1314 14 : const SortItem &prev = items[j];
1315 14 : if(prev.quotestart && f(item, prev))
1316 : {
1317 5 : item.quotestart = nullptr;
1318 5 : break;
1319 : }
1320 : }
1321 12 : if(item.quotestart)
1322 : {
1323 7 : totalunique += item.quotelength();
1324 7 : numunique++;
1325 : }
1326 : }
1327 : }
1328 14 : poparg(*x);
1329 14 : poparg(*y);
1330 14 : char *sorted = cstr;
1331 14 : size_t sortedlen = totalunique + std::max(numunique - 1, size_t(0));
1332 14 : if(clen < sortedlen)
1333 : {
1334 0 : delete[] cstr;
1335 0 : sorted = newstring(sortedlen);
1336 : }
1337 14 : int offset = 0;
1338 59 : for(size_t i = 0; i < items.size(); i++)
1339 : {
1340 45 : SortItem &item = items[i];
1341 45 : if(!item.quotestart)
1342 : {
1343 8 : continue;
1344 : }
1345 37 : int len = item.quotelength();
1346 37 : if(i)
1347 : {
1348 23 : sorted[offset++] = ' ';
1349 : }
1350 37 : std::memcpy(&sorted[offset], item.quotestart, len);
1351 37 : offset += len;
1352 : }
1353 14 : sorted[offset] = '\0';
1354 14 : commandret->setstr(sorted);
1355 14 : }
1356 :
1357 1 : void initmathcmds()
1358 : {
1359 : //integer and boolean operators, used with named symbol, i.e. + or *
1360 : //no native boolean type, they are treated like integers
1361 114 : addcommand("+", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { int val; if(numargs >= 2) { val = args[0].i; int val2 = args[1].i; val = val + val2; for(int i = 2; i < numargs; i++) { val2 = args[i].i; val = val + val2; } } else { val = numargs > 0 ? args[0].i : 0; ; } intret(val); }; }), "i" "1V", Id_Command); //0 substituted if nothing passed in arg2: n + 0 is still n
1362 6 : addcommand("*", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { int val; if(numargs >= 2) { val = args[0].i; int val2 = args[1].i; val = val * val2; for(int i = 2; i < numargs; i++) { val2 = args[i].i; val = val * val2; } } else { val = numargs > 0 ? args[0].i : 1; ; } intret(val); }; }), "i" "1V", Id_Command); //1 substituted if nothing passed in arg2: n * 1 is still n
1363 6 : addcommand("-", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { int val; if(numargs >= 2) { val = args[0].i; int val2 = args[1].i; val = val - val2; for(int i = 2; i < numargs; i++) { val2 = args[i].i; val = val - val2; } } else { val = numargs > 0 ? args[0].i : 0; val = -val; } intret(val); }; }), "i" "1V", Id_Command); //the minus operator inverts if used as unary
1364 13 : addcommand("=", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = args[0].i == args[1].i; for(int i = 2; i < numargs && val; i++) { val = args[i-1].i == args[i].i; } } else { val = (numargs > 0 ? args[0].i : 0) == 0; } intret(static_cast<int>(val)); }; }), "i" "1V", Id_Command);
1365 7 : addcommand("!=", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = args[0].i != args[1].i; for(int i = 2; i < numargs && val; i++) { val = args[i-1].i != args[i].i; } } else { val = (numargs > 0 ? args[0].i : 0) != 0; } intret(static_cast<int>(val)); }; }), "i" "1V", Id_Command);
1366 16 : addcommand("<", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = args[0].i < args[1].i; for(int i = 2; i < numargs && val; i++) { val = args[i-1].i < args[i].i; } } else { val = (numargs > 0 ? args[0].i : 0) < 0; } intret(static_cast<int>(val)); }; }), "i" "1V", Id_Command);
1367 9 : addcommand(">", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = args[0].i > args[1].i; for(int i = 2; i < numargs && val; i++) { val = args[i-1].i > args[i].i; } } else { val = (numargs > 0 ? args[0].i : 0) > 0; } intret(static_cast<int>(val)); }; }), "i" "1V", Id_Command);
1368 10 : addcommand("<=", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = args[0].i <= args[1].i; for(int i = 2; i < numargs && val; i++) { val = args[i-1].i <= args[i].i; } } else { val = (numargs > 0 ? args[0].i : 0) <= 0; } intret(static_cast<int>(val)); }; }), "i" "1V", Id_Command);
1369 9 : addcommand(">=", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = args[0].i >= args[1].i; for(int i = 2; i < numargs && val; i++) { val = args[i-1].i >= args[i].i; } } else { val = (numargs > 0 ? args[0].i : 0) >= 0; } intret(static_cast<int>(val)); }; }), "i" "1V", Id_Command);
1370 16 : addcommand("^", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { int val; if(numargs >= 2) { val = args[0].i; int val2 = args[1].i; val = val ^ val2; for(int i = 2; i < numargs; i++) { val2 = args[i].i; val = val ^ val2; } } else { val = numargs > 0 ? args[0].i : 0; val = ~val; } intret(val); }; }), "i" "1V", Id_Command);
1371 2 : addcommand("~", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { int val; if(numargs >= 2) { val = args[0].i; int val2 = args[1].i; val = val ^ val2; for(int i = 2; i < numargs; i++) { val2 = args[i].i; val = val ^ val2; } } else { val = numargs > 0 ? args[0].i : 0; val = ~val; } intret(val); }; }), "i" "1V", Id_Command);
1372 19 : addcommand("&", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { int val; if(numargs >= 2) { val = args[0].i; int val2 = args[1].i; val = val & val2; for(int i = 2; i < numargs; i++) { val2 = args[i].i; val = val & val2; } } else { val = numargs > 0 ? args[0].i : 0; ; } intret(val); }; }), "i" "1V", Id_Command);
1373 16 : addcommand("|", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { int val; if(numargs >= 2) { val = args[0].i; int val2 = args[1].i; val = val | val2; for(int i = 2; i < numargs; i++) { val2 = args[i].i; val = val | val2; } } else { val = numargs > 0 ? args[0].i : 0; ; } intret(val); }; }), "i" "1V", Id_Command);
1374 17 : addcommand("^~", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { int val; if(numargs >= 2) { val = args[0].i; int val2 = args[1].i; val = val ^~ val2; for(int i = 2; i < numargs; i++) { val2 = args[i].i; val = val ^~ val2; } } else { val = numargs > 0 ? args[0].i : 0; ; } intret(val); }; }), "i" "1V", Id_Command);
1375 19 : addcommand("&~", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { int val; if(numargs >= 2) { val = args[0].i; int val2 = args[1].i; val = val &~ val2; for(int i = 2; i < numargs; i++) { val2 = args[i].i; val = val &~ val2; } } else { val = numargs > 0 ? args[0].i : 0; ; } intret(val); }; }), "i" "1V", Id_Command);
1376 17 : addcommand("|~", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { int val; if(numargs >= 2) { val = args[0].i; int val2 = args[1].i; val = val |~ val2; for(int i = 2; i < numargs; i++) { val2 = args[i].i; val = val |~ val2; } } else { val = numargs > 0 ? args[0].i : 0; ; } intret(val); }; }), "i" "1V", Id_Command);
1377 21 : addcommand("<<", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { int val; if(numargs >= 2) { val = args[0].i; int val2 = args[1].i; val = val2 < 32 ? val << std::max(val2, 0) : 0; for(int i = 2; i < numargs; i++) { val2 = args[i].i; val = val2 < 32 ? val << std::max(val2, 0) : 0; } } else { val = numargs > 0 ? args[0].i : 0; ; } intret(val); }; }), "i" "1V", Id_Command);
1378 27 : addcommand(">>", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { int val; if(numargs >= 2) { val = args[0].i; int val2 = args[1].i; val >>= std::clamp(val2, 0, 31); for(int i = 2; i < numargs; i++) { val2 = args[i].i; val >>= std::clamp(val2, 0, 31); } } else { val = numargs > 0 ? args[0].i : 0; ; } intret(val); }; }), "i" "1V", Id_Command);
1379 :
1380 : //floating point operators, used with <operator>f, i.e. +f or *f
1381 6 : addcommand("+" "f", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { float val; if(numargs >= 2) { val = args[0].f; float val2 = args[1].f; val = val + val2; for(int i = 2; i < numargs; i++) { val2 = args[i].f; val = val + val2; } } else { val = numargs > 0 ? args[0].f : 0; ; } floatret(val); }; }), "f" "1V", Id_Command);
1382 6 : addcommand("*" "f", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { float val; if(numargs >= 2) { val = args[0].f; float val2 = args[1].f; val = val * val2; for(int i = 2; i < numargs; i++) { val2 = args[i].f; val = val * val2; } } else { val = numargs > 0 ? args[0].f : 1; ; } floatret(val); }; }), "f" "1V", Id_Command);
1383 6 : addcommand("-" "f", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { float val; if(numargs >= 2) { val = args[0].f; float val2 = args[1].f; val = val - val2; for(int i = 2; i < numargs; i++) { val2 = args[i].f; val = val - val2; } } else { val = numargs > 0 ? args[0].f : 0; val = -val; } floatret(val); }; }), "f" "1V", Id_Command);
1384 13 : addcommand("=" "f", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = args[0].f == args[1].f; for(int i = 2; i < numargs && val; i++) { val = args[i-1].f == args[i].f; } } else { val = (numargs > 0 ? args[0].f : 0) == 0; } intret(static_cast<int>(val)); }; }), "f" "1V", Id_Command);
1385 7 : addcommand("!=" "f", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = args[0].f != args[1].f; for(int i = 2; i < numargs && val; i++) { val = args[i-1].f != args[i].f; } } else { val = (numargs > 0 ? args[0].f : 0) != 0; } intret(static_cast<int>(val)); }; }), "f" "1V", Id_Command);
1386 16 : addcommand("<" "f", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = args[0].f < args[1].f; for(int i = 2; i < numargs && val; i++) { val = args[i-1].f < args[i].f; } } else { val = (numargs > 0 ? args[0].f : 0) < 0; } intret(static_cast<int>(val)); }; }), "f" "1V", Id_Command);
1387 9 : addcommand(">" "f", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = args[0].f > args[1].f; for(int i = 2; i < numargs && val; i++) { val = args[i-1].f > args[i].f; } } else { val = (numargs > 0 ? args[0].f : 0) > 0; } intret(static_cast<int>(val)); }; }), "f" "1V", Id_Command);
1388 10 : addcommand("<=" "f", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = args[0].f <= args[1].f; for(int i = 2; i < numargs && val; i++) { val = args[i-1].f <= args[i].f; } } else { val = (numargs > 0 ? args[0].f : 0) <= 0; } intret(static_cast<int>(val)); }; }), "f" "1V", Id_Command);
1389 9 : addcommand(">=" "f", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = args[0].f >= args[1].f; for(int i = 2; i < numargs && val; i++) { val = args[i-1].f >= args[i].f; } } else { val = (numargs > 0 ? args[0].f : 0) >= 0; } intret(static_cast<int>(val)); }; }), "f" "1V", Id_Command);
1390 :
1391 1 : addcommand("!", reinterpret_cast<identfun>(+[] (const tagval *a) { intret(getbool(*a) ? 0 : 1); }), "t", Id_Not);
1392 1 : addcommand("&&", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { if(!numargs) { intret(1); } else { for(int i = 0; i < numargs; ++i) { if(i) { freearg(*commandret); } if(args[i].type == Value_Code) { executeret(args[i].code, *commandret); } else { *commandret = args[i]; } if(!getbool(*commandret)) { break; } } } }; }), "E1V", Id_And);
1393 :
1394 1 : addcommand("||", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { if(!numargs) { intret(0); } else { for(int i = 0; i < numargs; ++i) { if(i) { freearg(*commandret); } if(args[i].type == Value_Code) { executeret(args[i].code, *commandret); } else { *commandret = args[i]; } if(getbool(*commandret)) { break; } } } }; }), "E1V", Id_Or);
1395 :
1396 : //int division
1397 8 : addcommand("div", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { int val; if(numargs >= 2) { val = args[0].i; int val2 = args[1].i; { if(val2) val /= val2; else val = 0; }; for(int i = 2; i < numargs; i++) { val2 = args[i].i; { if(val2) val /= val2; else val = 0; }; } } else { val = numargs > 0 ? args[0].i : 0; ; } intret(val); }; }), "i" "1V", Id_Command);
1398 12 : addcommand("mod", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { int val; if(numargs >= 2) { val = args[0].i; int val2 = args[1].i; { if(val2) val %= val2; else val = 0; }; for(int i = 2; i < numargs; i++) { val2 = args[i].i; { if(val2) val %= val2; else val = 0; }; } } else { val = numargs > 0 ? args[0].i : 0; ; } intret(val); }; }), "i" "1V", Id_Command);
1399 : //float division
1400 8 : addcommand("divf", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { float val; if(numargs >= 2) { val = args[0].f; float val2 = args[1].f; { if(val2) val /= val2; else val = 0; }; for(int i = 2; i < numargs; i++) { val2 = args[i].f; { if(val2) val /= val2; else val = 0; }; } } else { val = numargs > 0 ? args[0].f : 0; ; } floatret(val); }; }), "f" "1V", Id_Command);
1401 12 : addcommand("modf", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { float val; if(numargs >= 2) { val = args[0].f; float val2 = args[1].f; { if(val2) val = std::fmod(val, val2); else val = 0; }; for(int i = 2; i < numargs; i++) { val2 = args[i].f; { if(val2) val = std::fmod(val, val2); else val = 0; }; } } else { val = numargs > 0 ? args[0].f : 0; ; } floatret(val); }; }), "f" "1V", Id_Command);
1402 13 : addcommand("pow", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { float val; if(numargs >= 2) { val = args[0].f; float val2 = args[1].f; val = std::pow(val, val2); for(int i = 2; i < numargs; i++) { val2 = args[i].f; val = std::pow(val, val2); } } else { val = numargs > 0 ? args[0].f : 0; ; } floatret(val); }; }), "f" "1V", Id_Command);
1403 :
1404 : //float transcendentals
1405 7 : addcommand("sin", reinterpret_cast<identfun>(+[] (const float *a) { floatret(std::sin(*a/RAD)); }), "f", Id_Command);
1406 7 : addcommand("cos", reinterpret_cast<identfun>(+[] (const float *a) { floatret(std::cos(*a/RAD)); }), "f", Id_Command);
1407 7 : addcommand("tan", reinterpret_cast<identfun>(+[] (const float *a) { floatret(std::tan(*a/RAD)); }), "f", Id_Command);
1408 7 : addcommand("asin", reinterpret_cast<identfun>(+[] (const float *a) { floatret(std::asin(*a)*RAD); }), "f", Id_Command);
1409 7 : addcommand("acos", reinterpret_cast<identfun>(+[] (const float *a) { floatret(std::acos(*a)*RAD); }), "f", Id_Command);
1410 7 : addcommand("atan", reinterpret_cast<identfun>(+[] (const float *a) { floatret(std::atan(*a)*RAD); }), "f", Id_Command);
1411 7 : addcommand("atan2", reinterpret_cast<identfun>(+[] (const float *y, const float *x) { floatret(std::atan2(*y, *x)*RAD); }), "ff", Id_Command);
1412 7 : addcommand("sqrt", reinterpret_cast<identfun>(+[] (const float *a) { floatret(std::sqrt(*a)); }), "f", Id_Command);
1413 8 : addcommand("loge", reinterpret_cast<identfun>(+[] (const float *a) { floatret(std::log(*a)); }), "f", Id_Command);
1414 7 : addcommand("log2", reinterpret_cast<identfun>(+[] (const float *a) { floatret(std::log(*a)/M_LN2); }), "f", Id_Command);
1415 7 : addcommand("log10", reinterpret_cast<identfun>(+[] (const float *a) { floatret(std::log10(*a)); }), "f", Id_Command);
1416 7 : addcommand("exp", reinterpret_cast<identfun>(+[] (const float *a) { floatret(std::exp(*a)); }), "f", Id_Command);
1417 :
1418 12 : addcommand("min", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { int val = numargs > 0 ? args[0].i : 0; for(int i = 1; i < numargs; i++) { val = std::min(val, args[i].i); } intret(val); }; }), "i" "1V", Id_Command);
1419 12 : addcommand("max", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { int val = numargs > 0 ? args[0].i : 0; for(int i = 1; i < numargs; i++) { val = std::max(val, args[i].i); } intret(val); }; }), "i" "1V", Id_Command);
1420 11 : addcommand("minf", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { float val = numargs > 0 ? args[0].f : 0; for(int i = 1; i < numargs; i++) { val = std::min(val, args[i].f); } floatret(val); }; }), "f" "1V", Id_Command);
1421 11 : addcommand("maxf", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { float val = numargs > 0 ? args[0].f : 0; for(int i = 1; i < numargs; i++) { val = std::max(val, args[i].f); } floatret(val); }; }), "f" "1V", Id_Command);
1422 :
1423 7 : addcommand("bitscan", reinterpret_cast<identfun>(+[] (const int *n) { intret(BITSCAN(*n)); }), "i", Id_Command);
1424 :
1425 7 : addcommand("abs", reinterpret_cast<identfun>(+[] (const int *n) { intret(std::abs(*n)); }), "i", Id_Command);
1426 7 : addcommand("absf", reinterpret_cast<identfun>(+[] (const float *n) { floatret(std::fabs(*n)); }), "f", Id_Command);
1427 :
1428 8 : addcommand("floor", reinterpret_cast<identfun>(+[] (const float *n) { floatret(std::floor(*n)); }), "f", Id_Command);
1429 8 : addcommand("ceil", reinterpret_cast<identfun>(+[] (const float *n) { floatret(std::ceil(*n)); }), "f", Id_Command);
1430 16 : addcommand("round", reinterpret_cast<identfun>(+[] (const float *n, const float *k) { { double step = *k; double r = *n; if(step > 0) { r += step * (r < 0 ? -0.5 : 0.5); r -= std::fmod(r, step); } else { r = r < 0 ? std::ceil(r - 0.5) : std::floor(r + 0.5); } floatret(static_cast<float>(r)); }; }), "ff", Id_Command);
1431 :
1432 1 : addcommand("cond", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs)
1433 : {
1434 14 : for(int i = 0; i < numargs; i += 2)
1435 : {
1436 12 : if(i+1 < numargs) //if not the last arg
1437 : {
1438 10 : if(executebool(args[i].code))
1439 : {
1440 4 : executeret(args[i+1].code, *commandret);
1441 4 : break;
1442 : }
1443 : }
1444 : else
1445 : {
1446 2 : executeret(args[i].code, *commandret);
1447 2 : break;
1448 : }
1449 : }
1450 1 : }), "ee2V", Id_Command);
1451 :
1452 2 : addcommand("case", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { int val = args[0].getint(); int i; for(i = 1; i+1 < numargs; i += 2) { if(args[i].type == Value_Null || args[i].getint() == val) { executeret(args[i+1].code, *commandret); return; } } }; }), "i" "te2V", Id_Command);
1453 2 : addcommand("casef", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { float val = args[0].getfloat(); int i; for(i = 1; i+1 < numargs; i += 2) { if(args[i].type == Value_Null || args[i].getfloat() == val) { executeret(args[i+1].code, *commandret); return; } } }; }), "f" "te2V", Id_Command);
1454 2 : addcommand("cases", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { const char * val = args[0].getstr(); int i; for(i = 1; i+1 < numargs; i += 2) { if(args[i].type == Value_Null || !std::strcmp(args[i].getstr(), val)) { executeret(args[i+1].code, *commandret); return; } } }; }), "s" "te2V", Id_Command);
1455 :
1456 2 : addcommand("rnd", reinterpret_cast<identfun>(+[] (const int *a, const int *b) { intret(*a - *b > 0 ? randomint(*a - *b) + *b : *b); }), "ii", Id_Command);
1457 2 : addcommand("rndstr", reinterpret_cast<identfun>(+[] (const int *len) { { int n = std::clamp(*len, 0, 10000); char *s = newstring(n); for(int i = 0; i < n;) { int r = rand(); for(int j = std::min(i + 4, n); i < j; i++) { s[i] = (r%255) + 1; r /= 255; } } s[n] = '\0'; stringret(s); }; }), "i", Id_Command);
1458 :
1459 17 : addcommand("tohex", reinterpret_cast<identfun>(+[] (const int *n, const int *p) { { constexpr int len = 20; char *buf = newstring(len); nformatstring(buf, len, "0x%.*X", std::max(*p, 1), *n); stringret(buf); }; }), "ii", Id_Command);
1460 :
1461 2 : addcommand("strcmp", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = std::strcmp(args[0].s, args[1].s) == 0; for(int i = 2; i < numargs && val; i++) { val = std::strcmp(args[i-1].s, args[i].s) == 0; } } else { val = (numargs > 0 ? args[0].s[0] : 0) == 0; } intret(static_cast<int>(val)); }; }), "s1V", Id_Command);
1462 40 : addcommand("=s", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = std::strcmp(args[0].s, args[1].s) == 0; for(int i = 2; i < numargs && val; i++) { val = std::strcmp(args[i-1].s, args[i].s) == 0; } } else { val = (numargs > 0 ? args[0].s[0] : 0) == 0; } intret(static_cast<int>(val)); }; }), "s1V", Id_Command);
1463 5 : addcommand("!=s", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = std::strcmp(args[0].s, args[1].s) != 0; for(int i = 2; i < numargs && val; i++) { val = std::strcmp(args[i-1].s, args[i].s) != 0; } } else { val = (numargs > 0 ? args[0].s[0] : 0) != 0; } intret(static_cast<int>(val)); }; }), "s1V", Id_Command);
1464 17 : addcommand("<s", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = std::strcmp(args[0].s, args[1].s) < 0; for(int i = 2; i < numargs && val; i++) { val = std::strcmp(args[i-1].s, args[i].s) < 0; } } else { val = (numargs > 0 ? args[0].s[0] : 0) < 0; } intret(static_cast<int>(val)); }; }), "s1V", Id_Command);
1465 9 : addcommand(">s", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = std::strcmp(args[0].s, args[1].s) > 0; for(int i = 2; i < numargs && val; i++) { val = std::strcmp(args[i-1].s, args[i].s) > 0; } } else { val = (numargs > 0 ? args[0].s[0] : 0) > 0; } intret(static_cast<int>(val)); }; }), "s1V", Id_Command);
1466 8 : addcommand("<=s", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = std::strcmp(args[0].s, args[1].s) <= 0; for(int i = 2; i < numargs && val; i++) { val = std::strcmp(args[i-1].s, args[i].s) <= 0; } } else { val = (numargs > 0 ? args[0].s[0] : 0) <= 0; } intret(static_cast<int>(val)); }; }), "s1V", Id_Command);
1467 8 : addcommand(">=s", reinterpret_cast<identfun>(+[] (const tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = std::strcmp(args[0].s, args[1].s) >= 0; for(int i = 2; i < numargs && val; i++) { val = std::strcmp(args[i-1].s, args[i].s) >= 0; } } else { val = (numargs > 0 ? args[0].s[0] : 0) >= 0; } intret(static_cast<int>(val)); }; }), "s1V", Id_Command);
1468 1 : }
1469 :
1470 7 : char *strreplace(const char *s, const char *oldval, const char *newval, const char *newval2)
1471 : {
1472 7 : std::vector<char> buf;
1473 :
1474 7 : int oldlen = std::strlen(oldval);
1475 7 : if(!oldlen)
1476 : {
1477 2 : return newstring(s);
1478 : }
1479 5 : for(int i = 0;; i++)
1480 : {
1481 19 : const char *found = std::strstr(s, oldval);
1482 19 : if(found)
1483 : {
1484 25 : while(s < found)
1485 : {
1486 11 : buf.push_back(*s++);
1487 : }
1488 29 : for(const char *n = i&1 ? newval2 : newval; *n; n++)
1489 : {
1490 15 : buf.push_back(*n);
1491 : }
1492 14 : s = found + oldlen;
1493 : }
1494 : else
1495 : {
1496 27 : while(*s)
1497 : {
1498 22 : buf.push_back(*s++);
1499 : }
1500 5 : buf.push_back('\0');
1501 5 : return newstring(buf.data(), buf.size());
1502 : }
1503 14 : }
1504 7 : }
1505 :
1506 : //external api function, for loading the string manip functions into the global hashtable
1507 1 : void initstrcmds()
1508 : {
1509 3 : addcommand("echo", reinterpret_cast<identfun>(+[] (const char *s) { conoutf("^f1%s", s); }), "C", Id_Command);
1510 2 : addcommand("error", reinterpret_cast<identfun>(+[] (const char *s) { conoutf(Console_Error, "%s", s); }), "C", Id_Command);
1511 6 : addcommand("strstr", reinterpret_cast<identfun>(+[] (const char *a, const char *b) { { const char *s = std::strstr(a, b); intret(s ? s-a : -1); }; }), "ss", Id_Command);
1512 6 : addcommand("strlen", reinterpret_cast<identfun>(+[] (const char *s) { intret(std::strlen(s)); }), "s", Id_Command);
1513 :
1514 2 : addcommand("strlower", reinterpret_cast<identfun>(+[] (const char *s) { { int len = std::strlen(s); char *m = newstring(len); for(int i = 0; i < static_cast<int>(len); ++i) { m[i] = cubelower(s[i]); } m[len] = '\0'; stringret(m); }; }), "s", Id_Command);
1515 2 : addcommand("strupper", reinterpret_cast<identfun>(+[] (const char *s) { { int len = std::strlen(s); char *m = newstring(len); for(int i = 0; i < static_cast<int>(len); ++i) { m[i] = cubeupper(s[i]); } m[len] = '\0'; stringret(m); }; }), "s", Id_Command);
1516 :
1517 14 : static auto strsplice = [] (const char *s, const char *vals, const int *skip, const int *count)
1518 : {
1519 14 : int slen = std::strlen(s),
1520 14 : vlen = std::strlen(vals),
1521 14 : offset = std::clamp(*skip, 0, slen),
1522 14 : len = std::clamp(*count, 0, slen - offset);
1523 14 : char *p = newstring(slen - len + vlen);
1524 14 : if(offset)
1525 : {
1526 6 : std::memcpy(p, s, offset);
1527 : }
1528 14 : if(vlen)
1529 : {
1530 11 : std::memcpy(&p[offset], vals, vlen);
1531 : }
1532 14 : if(offset + len < slen)
1533 : {
1534 8 : std::memcpy(&p[offset + vlen], &s[offset + len], slen - (offset + len));
1535 : }
1536 14 : p[slen - len + vlen] = '\0';
1537 14 : commandret->setstr(p);
1538 14 : };
1539 1 : addcommand("strsplice", reinterpret_cast<identfun>(+strsplice), "ssii", Id_Command);
1540 8 : addcommand("strreplace", reinterpret_cast<identfun>(+[] (const char *s, const char *o, const char *n, const char *n2) { commandret->setstr(strreplace(s, o, n, n2[0] ? n2 : n)); }), "ssss", Id_Command);
1541 :
1542 1 : static auto substr = [] (const char *s, const int *start, const int *count, const int *numargs)
1543 : {
1544 1 : int len = std::strlen(s),
1545 1 : offset = std::clamp(*start, 0, len);
1546 1 : commandret->setstr(newstring(&s[offset], *numargs >= 3 ? std::clamp(*count, 0, len - offset) : len - offset));
1547 1 : };
1548 1 : addcommand("substr", reinterpret_cast<identfun>(+substr), "siiN", Id_Command);
1549 :
1550 5 : static auto stripcolors = [] (const char *s)
1551 : {
1552 5 : int len = std::strlen(s);
1553 5 : char *d = newstring(len);
1554 5 : filtertext(d, s, true, false, len);
1555 5 : stringret(d);
1556 5 : };
1557 1 : addcommand("stripcolors", reinterpret_cast<identfun>(+stripcolors), "s", Id_Command);
1558 2 : addcommand("appendword", reinterpret_cast<identfun>(+[] (ident *id, const tagval *v) { append(id, v, false); }), "rt", Id_Command);
1559 :
1560 7 : static auto concat = [] (const tagval *v, int n)
1561 : {
1562 7 : commandret->setstr(conc(v, n, true));
1563 7 : };
1564 1 : addcommand("concat", reinterpret_cast<identfun>(+concat), "V", Id_Command);
1565 1 : addcommand("concatword", reinterpret_cast<identfun>(concatword), "V", Id_Command);
1566 1 : addcommand("format", reinterpret_cast<identfun>(format), "V", Id_Command);
1567 1 : }
1568 :
1569 : struct sleepcmd final
1570 : {
1571 : int delay, millis, flags;
1572 : std::string command;
1573 : };
1574 : std::vector<sleepcmd> sleepcmds;
1575 :
1576 1 : void addsleep(const int *msec, const char *cmd)
1577 : {
1578 1 : sleepcmd s;
1579 1 : s.delay = std::max(*msec, 1);
1580 1 : s.millis = lastmillis;
1581 1 : s.command = std::string(cmd);
1582 1 : s.flags = identflags;
1583 1 : sleepcmds.push_back(std::move(s));
1584 1 : }
1585 :
1586 0 : void checksleep(int millis)
1587 : {
1588 0 : for(size_t i = 0; i < sleepcmds.size(); i++)
1589 : {
1590 0 : sleepcmd &s = sleepcmds[i];
1591 0 : if(millis - s.millis >= s.delay)
1592 : {
1593 0 : std::string cmd = s.command; // execute might create more sleep commands
1594 0 : s.command.clear();
1595 0 : int oldflags = identflags;
1596 0 : identflags = s.flags;
1597 0 : execute(cmd.c_str());
1598 0 : identflags = oldflags;
1599 0 : if(sleepcmds.size() > i && sleepcmds[i].command.empty())
1600 : {
1601 0 : sleepcmds.erase(sleepcmds.begin() + i);
1602 0 : i--;
1603 : }
1604 0 : }
1605 : }
1606 0 : }
1607 :
1608 1 : void clearsleep(bool clearoverrides)
1609 : {
1610 1 : int len = 0;
1611 1 : for(sleepcmd &i : sleepcmds)
1612 : {
1613 0 : if(i.command.size())
1614 : {
1615 0 : if(clearoverrides && !(i.flags&Idf_Overridden))
1616 : {
1617 0 : sleepcmds[len++] = i;
1618 : }
1619 : }
1620 : }
1621 1 : sleepcmds.resize(len);
1622 1 : }
1623 :
1624 1 : void clearsleep_(const int *clearoverrides)
1625 : {
1626 1 : clearsleep(*clearoverrides!=0 || identflags&Idf_Overridden);
1627 1 : }
1628 :
1629 1 : void initcontrolcmds()
1630 : {
1631 1 : addcommand("exec", reinterpret_cast<identfun>(exec), "sb", Id_Command);
1632 1 : addcommand("escape", reinterpret_cast<identfun>(escapecmd), "s", Id_Command);
1633 1 : addcommand("unescape", reinterpret_cast<identfun>(unescapecmd), "s", Id_Command);
1634 1 : addcommand("writecfg", reinterpret_cast<identfun>(writecfg), "s", Id_Command);
1635 1 : addcommand("changedvars", reinterpret_cast<identfun>(changedvars), "", Id_Command);
1636 :
1637 1 : addcommand("if", reinterpret_cast<identfun>(+[] (const tagval *cond, const uint *t, const uint *f) { executeret(getbool(*cond) ? t : f, *commandret); }), "tee", Id_If);
1638 7 : addcommand("?", reinterpret_cast<identfun>(+[] (tagval *cond, tagval *t, tagval *f) { result(*(getbool(*cond) ? t : f)); }), "tTT", Id_Command);
1639 :
1640 1 : addcommand("pushif", reinterpret_cast<identfun>(+[] (ident *id, tagval *v, const uint *code)
1641 : {
1642 1 : if(id->type != Id_Alias || id->index < Max_Args)
1643 : {
1644 0 : return;
1645 : }
1646 1 : if(getbool(*v))
1647 : {
1648 : identstack stack;
1649 0 : pusharg(*id, *v, stack);
1650 0 : v->type = Value_Null;
1651 0 : id->flags &= ~Idf_Unknown;
1652 0 : executeret(code, *commandret);
1653 0 : poparg(*id);
1654 : }
1655 1 : }), "rTe", Id_Command);
1656 1 : addcommand("do", reinterpret_cast<identfun>(+[] (const uint *body) { executeret(body, *commandret); }), "e", Id_Do);
1657 2 : addcommand("append", reinterpret_cast<identfun>(+[] (ident *id, const tagval *v) { append(id, v, true); }), "rt", Id_Command);
1658 1 : addcommand("result", reinterpret_cast<identfun>(+[] (tagval *v) { { *commandret = *v; v->type = Value_Null; }; }), "T", Id_Result);
1659 :
1660 1 : addcommand("listlen", reinterpret_cast<identfun>(listlencmd), "s", Id_Command);
1661 1 : addcommand("at", reinterpret_cast<identfun>(at), "si1V", Id_Command);
1662 1 : addcommand("sublist", reinterpret_cast<identfun>(sublist), "siiN", Id_Command);
1663 1 : addcommand("listcount", reinterpret_cast<identfun>(listcount), "rse", Id_Command);
1664 1 : addcommand("listfind", reinterpret_cast<identfun>(listfind), "rse", Id_Command);
1665 1 : addcommand("listfind=", reinterpret_cast<identfun>(listfindeq), "sii", Id_Command);
1666 7 : addcommand("loop", reinterpret_cast<identfun>(+[] (ident *id, const int *n, const uint *body) { doloop(*id, 0, *n, 1, body); }), "rie", Id_Command);
1667 9 : addcommand("loop+", reinterpret_cast<identfun>(+[] (ident *id, const int *offset, const int *n, const uint *body) { doloop(*id, *offset, *n, 1, body); }), "riie", Id_Command);
1668 8 : addcommand("loop*", reinterpret_cast<identfun>(+[] (ident *id, const int *step, const int *n, const uint *body) { doloop(*id, 0, *n, *step, body); }), "riie", Id_Command);
1669 10 : addcommand("loop+*", reinterpret_cast<identfun>(+[] (ident *id, const int *offset, const int *step, const int *n, const uint *body) { doloop(*id, *offset, *n, *step, body); }), "riiie", Id_Command);
1670 30 : addcommand("loopconcat", reinterpret_cast<identfun>(+[] (ident *id, const int *n, const uint *body) { loopconc(*id, 0, *n, body, true); }), "rie", Id_Command);
1671 11 : addcommand("loopconcat+", reinterpret_cast<identfun>(+[] (ident *id, const int *offset, const int *n, const uint *body) { loopconc(*id, *offset, *n, body, true); }), "riie", Id_Command);
1672 :
1673 2 : addcommand("while", reinterpret_cast<identfun>(+[] (const uint *cond, const uint *body) { while(executebool(cond)) execute(body); }), "ee", Id_Command);
1674 :
1675 1 : static auto looplist = [] (ident *id, const char *list, const uint *body)
1676 : {
1677 1 : if(id->type!=Id_Alias)
1678 : {
1679 0 : return;
1680 : }
1681 : identstack stack;
1682 1 : int n = 0;
1683 1 : for(const char *s = list, *start, *end, *qstart; parselist(s, start, end, qstart); n++)
1684 : {
1685 0 : setiter(*id, listelem(start, end, qstart), stack);
1686 0 : execute(body);
1687 : }
1688 1 : if(n)
1689 : {
1690 0 : poparg(*id);
1691 : }
1692 : };
1693 :
1694 1 : static auto looplist2 = [] (ident *id, ident *id2, const char *list, const uint *body)
1695 : {
1696 1 : if(id->type!=Id_Alias || id2->type!=Id_Alias)
1697 : {
1698 0 : return;
1699 : }
1700 : identstack stack, stack2;
1701 1 : int n = 0;
1702 1 : for(const char *s = list, *start, *end, *qstart; parselist(s, start, end, qstart); n += 2)
1703 : {
1704 0 : setiter(*id, listelem(start, end, qstart), stack);
1705 0 : setiter(*id2, parselist(s, start, end, qstart) ? listelem(start, end, qstart) : newstring(""), stack2);
1706 0 : execute(body);
1707 : }
1708 1 : if(n)
1709 : {
1710 0 : poparg(*id);
1711 0 : poparg(*id2);
1712 : }
1713 : };
1714 :
1715 1 : static auto looplist3 = [] (ident *id, ident *id2, ident *id3, const char *list, const uint *body)
1716 : {
1717 1 : if(id->type!=Id_Alias || id2->type!=Id_Alias || id3->type!=Id_Alias)
1718 : {
1719 0 : return;
1720 : }
1721 : identstack stack, stack2, stack3;
1722 1 : int n = 0;
1723 1 : for(const char *s = list, *start, *end, *qstart; parselist(s, start, end, qstart); n += 3)
1724 : {
1725 0 : setiter(*id, listelem(start, end, qstart), stack);
1726 0 : setiter(*id2, parselist(s, start, end, qstart) ? listelem(start, end, qstart) : newstring(""), stack2);
1727 0 : setiter(*id3, parselist(s, start, end, qstart) ? listelem(start, end, qstart) : newstring(""), stack3);
1728 0 : execute(body);
1729 : }
1730 1 : if(n)
1731 : {
1732 0 : poparg(*id);
1733 0 : poparg(*id2);
1734 0 : poparg(*id3);
1735 : }
1736 : };
1737 1 : addcommand("looplist", reinterpret_cast<identfun>(+looplist), "rse", Id_Command);
1738 1 : addcommand("looplist2", reinterpret_cast<identfun>(+looplist2), "rrse", Id_Command);
1739 1 : addcommand("looplist3", reinterpret_cast<identfun>(+looplist3), "rrrse", Id_Command);
1740 :
1741 1 : addcommand("listassoc=", reinterpret_cast<identfun>(listassoceq), "si", Id_Command);
1742 2 : addcommand("looplistconcat", reinterpret_cast<identfun>(+[] (ident *id, const char *list, const uint *body) { looplistconc(id, list, body, true); }), "rse", Id_Command);
1743 2 : addcommand("looplistconcatword", reinterpret_cast<identfun>(+[] (ident *id, const char *list, const uint *body) { looplistconc(id, list, body, false); }), "rse", Id_Command);
1744 1 : addcommand("prettylist", reinterpret_cast<identfun>(prettylist), "ss", Id_Command);
1745 13 : addcommand("indexof", reinterpret_cast<identfun>(+[] (const char *list, const char *elem) { intret(listincludes(list, elem, std::strlen(elem))); }), "ss", Id_Command);
1746 :
1747 1 : addcommand("listdel", reinterpret_cast<identfun>(+[] (const char *list, const char *elems)
1748 : {
1749 : {
1750 7 : std::vector<char> p;
1751 27 : for(const char *start, *end, *qstart, *qend; parselist(list, start, end, qstart, qend);)
1752 : {
1753 13 : int len = end - start;
1754 13 : if(listincludes(elems, start, len) < 0)
1755 : {
1756 4 : if(!p.empty())
1757 : {
1758 1 : p.push_back(' ');
1759 : }
1760 26 : for(int i = 0; i < qend - qstart; ++i)
1761 : {
1762 22 : p.push_back(qstart[i]);
1763 : }
1764 : }
1765 : }
1766 7 : p.push_back('\0');
1767 7 : char * arr = new char[p.size()];
1768 7 : std::memcpy(arr, p.data(), p.size());
1769 7 : commandret->setstr(arr);
1770 7 : }
1771 8 : }), "ss", Id_Command);
1772 :
1773 1 : addcommand("listintersect", reinterpret_cast<identfun>(+[] (const char *list, const char *elems)
1774 : {
1775 : {
1776 8 : std::vector<char> p;
1777 27 : for(const char *start, *end, *qstart, *qend; parselist(list, start, end, qstart, qend);)
1778 : {
1779 11 : int len = end - start;
1780 11 : if(listincludes(elems, start, len) >= 0)
1781 : {
1782 9 : if(!p.empty())
1783 : {
1784 3 : p.push_back(' ');
1785 : }
1786 58 : for(int i = 0; i < qend - qstart; ++i)
1787 : {
1788 49 : p.push_back(qstart[i]);
1789 : }
1790 : }
1791 : }
1792 8 : p.push_back('\0');
1793 8 : char * arr = new char[p.size()];
1794 8 : std::memcpy(arr, p.data(), p.size());
1795 8 : commandret->setstr(arr);
1796 8 : }
1797 9 : }), "ss", Id_Command);
1798 :
1799 1 : addcommand("listunion", reinterpret_cast<identfun>(+[] (const char *list, const char *elems)
1800 : {
1801 : {
1802 8 : std::vector<char> p;
1803 76 : for(size_t i = 0; i < std::strlen(list); ++i)
1804 : {
1805 68 : p.push_back(list[i]);
1806 : }
1807 27 : for(const char *start, *end, *qstart, *qend; parselist(elems, start, end, qstart, qend);)
1808 : {
1809 11 : int len = end - start;
1810 11 : if(listincludes(list, start, len) < 0)
1811 : {
1812 2 : if(!p.empty())
1813 : {
1814 2 : p.push_back(' ');
1815 : }
1816 14 : for(int i = 0; i < qend - qstart; ++i)
1817 : {
1818 12 : p.push_back(qstart[i]);
1819 : }
1820 : }
1821 : }
1822 8 : p.push_back('\0');
1823 8 : char * arr = new char[p.size()];
1824 8 : std::memcpy(arr, p.data(), p.size());
1825 8 : commandret->setstr(arr);
1826 8 : }
1827 9 : }), "ss", Id_Command);
1828 :
1829 1 : addcommand("loopfiles", reinterpret_cast<identfun>(loopfiles), "rsse", Id_Command);
1830 1 : addcommand("listsplice", reinterpret_cast<identfun>(listsplice), "ssii", Id_Command);
1831 1 : addcommand("findfile", reinterpret_cast<identfun>(findfile_), "s", Id_Command);
1832 1 : addcommand("sortlist", reinterpret_cast<identfun>(sortlist), "srree", Id_Command);
1833 9 : addcommand("uniquelist", reinterpret_cast<identfun>(+[] (const char *list, ident *x, ident *y, const uint *body) { sortlist(list, x, y, nullptr, body); }), "srre", Id_Command);
1834 2 : addcommand("getmillis", reinterpret_cast<identfun>(+[] (const int *total) { intret(*total ? totalmillis : lastmillis); }), "i", Id_Command);
1835 1 : addcommand("sleep", reinterpret_cast<identfun>(addsleep), "is", Id_Command);
1836 1 : addcommand("clearsleep", reinterpret_cast<identfun>(clearsleep_), "i", Id_Command);
1837 1 : }
|