LCOV - code coverage report
Current view: top level - engine/interface - cubestd.cpp (source / functions) Hit Total Coverage
Test: Libprimis Test Coverage Lines: 763 957 79.7 %
Date: 2024-11-22 05:07:59 Functions: 153 166 92.2 %

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

Generated by: LCOV version 1.14