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