LCOV - code coverage report
Current view: top level - engine/interface - cubestd.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 78.8 % 994 783
Test Date: 2026-08-20 06:51:03 Functions: 91.9 % 172 158

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

Generated by: LCOV version 2.0-1