LCOV - code coverage report
Current view: top level - engine/interface - cubestd.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 79.9 % 958 765
Test Date: 2026-05-09 04:28:55 Functions: 92.2 % 166 153

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

Generated by: LCOV version 2.0-1