From a958d36c95b3bda69ba9e17b1d707c272716bf4b Mon Sep 17 00:00:00 2001 From: div0 Date: Sat, 11 Oct 2008 07:00:30 +0000 Subject: [PATCH] Experimental new tokenizer (should now be 1:1 compatible to the console parsing), used for about anything now. Votes no longer get accidentally reformatted, e.g. "(foo)" to "( foo )". git-svn-id: svn://svn.icculus.org/nexuiz/trunk@4723 f962a42d-fe04-0410-a3ab-8c8b0445ebaa --- data/qcsrc/client/Main.qc | 6 +- data/qcsrc/client/mapvoting.qc | 2 +- data/qcsrc/client/progs.src | 4 +- data/qcsrc/common/campaign_file.qc | 2 +- data/qcsrc/common/gamecommand.qc | 26 +-- data/qcsrc/common/util.qc | 163 ++++++++++++++++++- data/qcsrc/common/util.qh | 11 ++ data/qcsrc/menu/gamecommand.qc | 2 +- data/qcsrc/menu/menu.qc | 2 +- data/qcsrc/menu/nexuiz/dialog_singleplayer.c | 2 +- data/qcsrc/menu/nexuiz/keybinder.c | 8 +- data/qcsrc/menu/nexuiz/maplist.c | 4 +- data/qcsrc/menu/nexuiz/slider_resolution.c | 2 +- data/qcsrc/menu/nexuiz/util.qc | 4 +- data/qcsrc/menu/nexuiz/weaponslist.c | 4 +- data/qcsrc/menu/progs.src | 3 +- data/qcsrc/server/campaign.qc | 2 +- data/qcsrc/server/cl_player.qc | 10 +- data/qcsrc/server/cl_weapons.qc | 2 +- data/qcsrc/server/clientcommands.qc | 8 +- data/qcsrc/server/defs.qh | 1 - data/qcsrc/server/g_world.qc | 4 +- data/qcsrc/server/gamecommand.qc | 4 +- data/qcsrc/server/ipban.qc | 4 +- data/qcsrc/server/progs.src | 4 +- data/qcsrc/server/t_items.qc | 4 +- data/qcsrc/server/vote.qc | 147 ++++++++--------- data/qcsrc/server/vote.qh | 2 +- 28 files changed, 294 insertions(+), 143 deletions(-) diff --git a/data/qcsrc/client/Main.qc b/data/qcsrc/client/Main.qc index 8992bee2b..8cbdcb4cc 100644 --- a/data/qcsrc/client/Main.qc +++ b/data/qcsrc/client/Main.qc @@ -253,7 +253,7 @@ float CSQC_ConsoleCommand(string strMessage) float argc; // Tokenize String //argc = tokenize(strMessage); - argc = tokenizebyseparator(strMessage, " "); + argc = tokenize_sane(strMessage); // Acquire Command local string strCmd; @@ -305,7 +305,7 @@ float CSQC_ConsoleCommand(string strMessage) float GameCommand(string msg) { float argc; - argc = tokenize(msg); + argc = tokenize_sane(msg); string cmd; cmd = argv(0); if(cmd == "mv_download") { @@ -879,7 +879,7 @@ string getcommandkey(string text, string command) { if (!keys) { - n = tokenize(findkeysforcommand(command)); + n = tokenize_insane(findkeysforcommand(command)); // uses '...' strings for(j = 0; j < n; ++j) { k = stof(argv(j)); diff --git a/data/qcsrc/client/mapvoting.qc b/data/qcsrc/client/mapvoting.qc index d9725d948..cdca6e6e2 100644 --- a/data/qcsrc/client/mapvoting.qc +++ b/data/qcsrc/client/mapvoting.qc @@ -242,7 +242,7 @@ void MapVote_CheckPK3(string pic, string pk3, float id) } else { - Cmd_MapVote_MapDownload(tokenize(strcat("mv_download ", ftos(id)))); + Cmd_MapVote_MapDownload(tokenize_sane(strcat("mv_download ", ftos(id)))); } } diff --git a/data/qcsrc/client/progs.src b/data/qcsrc/client/progs.src index 5b80a7ad3..7c7771d55 100644 --- a/data/qcsrc/client/progs.src +++ b/data/qcsrc/client/progs.src @@ -1,14 +1,14 @@ ../../csprogs.dat pre.qh +../common/util-pre.qh Defs.qc csqc_constants.qc ../common/constants.qh - csqc_builtins.qc +../common/util.qh ../common/mapinfo.qh -../common/util.qh interpolate.qh teamradar.qh waypointsprites.qh diff --git a/data/qcsrc/common/campaign_file.qc b/data/qcsrc/common/campaign_file.qc index 277674103..7f5bf96b9 100644 --- a/data/qcsrc/common/campaign_file.qc +++ b/data/qcsrc/common/campaign_file.qc @@ -31,7 +31,7 @@ float CampaignFile_Load(float offset, float n) continue; // comment if(lineno >= offset) { - entlen = tokenize(l); + entlen = tokenize_insane(l); // using insane tokenizer for CSV #define CAMPAIGN_GETARG0 if(i >= entlen) #define CAMPAIGN_GETARG1 CAMPAIGN_GETARG0 error("syntax error in campaign file: line has not enough fields"); diff --git a/data/qcsrc/common/gamecommand.qc b/data/qcsrc/common/gamecommand.qc index e6af3b82d..003ee5ddf 100644 --- a/data/qcsrc/common/gamecommand.qc +++ b/data/qcsrc/common/gamecommand.qc @@ -49,7 +49,7 @@ float GameCommand_Generic(string command) float argc; float i, j, f, n; string s, s2; - argc = tokenize(command); + argc = tokenize_sane(command); if(argv(0) == "help") { print(" rpn EXPRESSION... - a RPN calculator.\n"); @@ -441,8 +441,8 @@ float GameCommand_Generic(string command) // s s2 union s2 = rpn_pop(); s = rpn_get(); - f = tokenize(s); - f2 = tokenize(strcat(s, " ", s2)); + f = tokenize_sane(s); + f2 = tokenize_sane(strcat(s, " ", s2)); // tokens 0..(f-1) represent s // tokens f..f2 represent s2 // UNION: add all tokens to s that are in s2 but not in s @@ -459,13 +459,13 @@ float GameCommand_Generic(string command) if(substring(s, 0, 1) == " ") s = substring(s, 1, 99999); rpn_set(s); - tokenize(command); + tokenize_sane(command); } else if(rpncmd == "intersection") { // s s2 intersection s2 = rpn_pop(); s = rpn_get(); - f = tokenize(s); - f2 = tokenize(strcat(s, " ", s2)); + f = tokenize_sane(s); + f2 = tokenize_sane(strcat(s, " ", s2)); // tokens 0..(f-1) represent s // tokens f..f2 represent s2 // INTERSECTION: keep only the tokens from s that are also in s2 @@ -481,13 +481,13 @@ float GameCommand_Generic(string command) if(substring(s, 0, 1) == " ") s = substring(s, 1, 99999); rpn_set(s); - tokenize(command); + tokenize_sane(command); } else if(rpncmd == "difference") { // s s2 difference s2 = rpn_pop(); s = rpn_get(); - f = tokenize(s); - f2 = tokenize(strcat(s, " ", s2)); + f = tokenize_sane(s); + f2 = tokenize_sane(strcat(s, " ", s2)); // tokens 0..(f-1) represent s // tokens f..f2 represent s2 // DIFFERENCE: keep only the tokens from s that are not in s2 @@ -502,11 +502,11 @@ float GameCommand_Generic(string command) if(substring(s, 0, 1) == " ") s = substring(s, 1, 99999); rpn_set(s); - tokenize(command); + tokenize_sane(command); } else if(rpncmd == "shuffle") { // s shuffle s = rpn_get(); - f = tokenize(s); + f = tokenize_sane(s); for(i = 0; i < f - 1; ++i) { // move a random item from i..f-1 to position i @@ -518,13 +518,13 @@ float GameCommand_Generic(string command) for(j = i; j < f; ++j) if(j != f2) s = strcat(s, " ", argv(j)); - f = tokenize(s); + f = tokenize_sane(s); } if(substring(s, 0, 1) == " ") s = substring(s, 1, 99999); rpn_set(s); - tokenize(command); + tokenize_sane(command); } else if(rpncmd == "fexists_assert") { s = rpn_pop(); if(!rpn_error) diff --git a/data/qcsrc/common/util.qc b/data/qcsrc/common/util.qc index d3d766397..e8d3a1a8f 100644 --- a/data/qcsrc/common/util.qc +++ b/data/qcsrc/common/util.qc @@ -646,7 +646,7 @@ string fixPriorityList(string order, float from, float to, float complete) string neworder; float i, n, w; - n = tokenize(order); + n = tokenize_sane(order); for(i = 0; i < n; ++i) { w = stof(argv(i)); @@ -656,7 +656,7 @@ string fixPriorityList(string order, float from, float to, float complete) if(complete) { - n = tokenize(neworder); + n = tokenize_sane(neworder); for(w = to; w >= from; --w) { for(i = 0; i < n; ++i) @@ -675,7 +675,7 @@ string swapInPriorityList(string order, float i, float j) string s; float w, n; - n = tokenize(order); + n = tokenize_sane(order); if(i >= 0 && i < n && j >= 0 && j < n && i != j) { @@ -875,7 +875,7 @@ void cvar_settemp_restore() { // undo what cvar_settemp did float n, i; - n = tokenize(cvar_string("settemp_list")); + n = tokenize_sane(cvar_string("settemp_list")); for(i = 0; i < n - 3; i += 3) cvar_set(argv(i + 1), cvar_string(argv(i + 2))); cvar_set("settemp_list", "0"); @@ -897,3 +897,158 @@ float almost_in_bounds(float a, float b, float c) eps = (max(a, -a) + max(c, -c)) * 0.001; return b == median(a - eps, b, c + eps); } + + + +#ifdef MENUQC +float (string s) _tokenize_builtin = #58; +string (float argnum) _argv_builtin = #59; +float (string s, string sep) _tokenizebyseparator_builtin = #479; +#else +float (string s) _tokenize_builtin = #441; +string (float argnum) _argv_builtin = #442; +float (string s, string sep) _tokenizebyseparator_builtin = #479; +#endif + +float MAX_TOKENS = 256; +string _argv_sane_buffer[MAX_TOKENS]; +float _argv_sane_startpos[MAX_TOKENS]; +float _argv_sane_endpos[MAX_TOKENS]; +float _argc_sane; + +string _argv_sane(float i) +{ + // Perl-ish -1 for the last argument + if(i < 0) + i = _argc_sane + i; + return _argv_sane_buffer[i]; +} + +float _argv_start_index_sane(float i) +{ + // Perl-ish -1 for the last argument + if(i < 0) + i = _argc_sane + i; + return _argv_sane_startpos[i]; +} + +float _argv_end_index_sane(float i) +{ + // Perl-ish -1 for the last argument + if(i < 0) + i = _argc_sane + i; + return _argv_sane_endpos[i]; +} + +string TOKENIZE_SANE_WHITESPACE_CHARS = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"; +string TOKENIZE_SANE_COMMENT_BREAKERS = "\x0d\x0a"; +// change this if DP changes its type to "char"! + +float _tokenize_sane(string s) +{ + // This MUST match COM_ParseToken_Console! + string com_token, tmp; + float data; + float end; + float i; + + _argc_sane = 0; + data = 0; + end = strlen(s); + + for(i = 0; i < MAX_TOKENS; ++i) + { + if(_argv_sane_buffer[i]) + strunzone(_argv_sane_buffer[i]); + _argv_sane_buffer[i] = string_null; + _argv_sane_startpos[i] = 0; + } + + for(;;) + { + // skip whitespace + for(; data < end && strstrofs(TOKENIZE_SANE_WHITESPACE_CHARS, substring(s, data, 1), 0) >= 0; ++data) + ; + if(data == end) + break; + + if(substring(s, data, 2) == "//") + { + // comment + + // Any call to the tokenizer ALREADY assumes it's a single line, so we can safely abort if we see a comment. + /* + data += 2; + while(data < end && strstrofs(TOKENIZE_SANE_COMMENT_BREAKERS, substring(s, data, 1), 0) >= 0) + ++data; + continue; // go to skipwhite again + */ + + // I'd like to simply put a "break" here, but then fteqcc says this function has unreachable code + return _argc_sane; + } + else if(substring(s, data, 1) == "\"") + { + // quoted string + com_token = ""; + _argv_sane_startpos[_argc_sane] = data; + for(++data; data < end && substring(s, data, 1) != "\""; ++data) + { + // allow escaped " and \ case + tmp = substring(s, data, 2); + if(tmp == "\\\"" || tmp == "\\\\") + ++data; + com_token = strcat(com_token, substring(s, data, 1)); + } + if(substring(s, data, 1) == "\"") + ++data; + _argv_sane_endpos[_argc_sane] = data; + _argv_sane_buffer[_argc_sane] = strzone(com_token); + ++_argc_sane; + } + else + { + // regular word + com_token = ""; + _argv_sane_startpos[_argc_sane] = data; + for(; data < end && strstrofs(TOKENIZE_SANE_WHITESPACE_CHARS, substring(s, data, 1), 0) < 0; ++data) + com_token = strcat(com_token, substring(s, data, 1)); + _argv_sane_endpos[_argc_sane] = data; + _argv_sane_buffer[_argc_sane] = strzone(com_token); + ++_argc_sane; + } + } + + return _argc_sane; +} + + + +var void(void) func_null; + +// "sane" tokenizer +// matching the console 1:1 + +float tokenize_sane(string s) +{ + argv = _argv_sane; + argv_start_index = _argv_start_index_sane; + argv_end_index = _argv_end_index_sane; + return _tokenize_sane(s); +} + +float tokenize_insane(string s) +{ + argv = _argv_builtin; + argv_start_index = func_null; + argv_end_index = func_null; + return _tokenize_builtin(s); +} + +float tokenizebyseparator(string s, string sep) +{ + argv = _argv_builtin; + argv_start_index = func_null; + argv_end_index = func_null; + return _tokenizebyseparator_builtin(s, sep); +} diff --git a/data/qcsrc/common/util.qh b/data/qcsrc/common/util.qh index 216a2fdd2..f32df285f 100644 --- a/data/qcsrc/common/util.qh +++ b/data/qcsrc/common/util.qh @@ -105,3 +105,14 @@ void get_mi_min_max_texcoords(float mode); float almost_equals(float a, float b); float almost_in_bounds(float a, float b, float c); + +#undef tokenize +#undef tokenizebyseparator +#undef argv + +var string(float) argv; +var float(float) argv_start_index; +var float(float) argv_end_index; +float tokenize_sane(string s); +float tokenize_insane(string s); +float tokenizebyseparator(string s, string sep); diff --git a/data/qcsrc/menu/gamecommand.qc b/data/qcsrc/menu/gamecommand.qc index 1b2aa938c..9dc199eab 100644 --- a/data/qcsrc/menu/gamecommand.qc +++ b/data/qcsrc/menu/gamecommand.qc @@ -34,7 +34,7 @@ void _dumptree_close(entity pass, entity me) void GameCommand(string theCommand) { float argc; - argc = tokenize(theCommand); + argc = tokenize_sane(theCommand); if(argv(0) == "help" || argc == 0) { diff --git a/data/qcsrc/menu/menu.qc b/data/qcsrc/menu/menu.qc index 6e48f49c3..81537e4ab 100644 --- a/data/qcsrc/menu/menu.qc +++ b/data/qcsrc/menu/menu.qc @@ -82,7 +82,7 @@ void() m_init_delayed = } draw_currentSkin = strzone(draw_currentSkin); while((s = fgets(fh))) - if(tokenize(s) == 2) + if(tokenize_insane(s) == 2) // uses '...' syntax for vectors Skin_ApplySetting(argv(0), argv(1)); fclose(fh); diff --git a/data/qcsrc/menu/nexuiz/dialog_singleplayer.c b/data/qcsrc/menu/nexuiz/dialog_singleplayer.c index 48ffd5239..33c2ad2d6 100644 --- a/data/qcsrc/menu/nexuiz/dialog_singleplayer.c +++ b/data/qcsrc/menu/nexuiz/dialog_singleplayer.c @@ -28,7 +28,7 @@ void InstantAction_LoadMap(entity btn, entity dummy) { if(substring(s, 0, 4) == "set ") s = substring(s, 4, strlen(s) - 4); - n = tokenize(s); + n = tokenize_sane(s); if(argv(0) == "bot_number") cvar_set("bot_number", argv(1)); else if(argv(0) == "skill") diff --git a/data/qcsrc/menu/nexuiz/keybinder.c b/data/qcsrc/menu/nexuiz/keybinder.c index 575a90e98..46c79449a 100644 --- a/data/qcsrc/menu/nexuiz/keybinder.c +++ b/data/qcsrc/menu/nexuiz/keybinder.c @@ -50,7 +50,7 @@ void Nexuiz_KeyBinds_Read() return; while((s = fgets(fh))) { - if(tokenize(s) != 2) + if(tokenize_sane(s) != 2) continue; Nexuiz_KeyBinds_Functions[Nexuiz_KeyBinds_Count] = strzone(argv(0)); Nexuiz_KeyBinds_Descriptions[Nexuiz_KeyBinds_Count] = strzone(argv(1)); @@ -116,7 +116,7 @@ void keyGrabbedNexuizKeyBinder(entity me, float key, float ascii) if(func == "") return; - n = tokenize(findkeysforcommand(func)); + n = tokenize_insane(findkeysforcommand(func)); // uses '...' strings nvalid = 0; for(j = 0; j < n; ++j) { @@ -186,7 +186,7 @@ void KeyBinder_Bind_Clear(entity btn, entity me) if(func == "") return; - n = tokenize(findkeysforcommand(func)); + n = tokenize_insane(findkeysforcommand(func)); // uses '...' strings for(j = 0; j < n; ++j) { k = stof(argv(j)); @@ -300,7 +300,7 @@ void drawListBoxItemNexuizKeyBinder(entity me, float i, vector absSize, float is draw_Text(me.realUpperMargin * eY + extraMargin * eX, descr, me.realFontSize, theColor, theAlpha, 0); if(func != "") { - n = tokenize(findkeysforcommand(func)); + n = tokenize_insane(findkeysforcommand(func)); // uses '...' strings s = ""; for(j = 0; j < n; ++j) { diff --git a/data/qcsrc/menu/nexuiz/maplist.c b/data/qcsrc/menu/nexuiz/maplist.c index 3ceebd4c9..fceddc06a 100644 --- a/data/qcsrc/menu/nexuiz/maplist.c +++ b/data/qcsrc/menu/nexuiz/maplist.c @@ -103,7 +103,7 @@ void g_maplistCacheToggleNexuizMapList(entity me, float i) else { s = ""; - n = tokenize(cvar_string("g_maplist")); + n = tokenize_sane(cvar_string("g_maplist")); for(i = 0; i < n; ++i) if(argv(i) != bspname) s = strcat(s, " ", argv(i)); @@ -212,7 +212,7 @@ void refilterNexuizMapList(entity me) s = "0"; for(i = 1; i < MapInfo_count; i *= 2) s = strcat(s, s); - n = tokenize(cvar_string("g_maplist")); + n = tokenize_sane(cvar_string("g_maplist")); for(i = 0; i < n; ++i) { j = MapInfo_FindName(argv(i)); diff --git a/data/qcsrc/menu/nexuiz/slider_resolution.c b/data/qcsrc/menu/nexuiz/slider_resolution.c index 0a963a6ba..f9dbce473 100644 --- a/data/qcsrc/menu/nexuiz/slider_resolution.c +++ b/data/qcsrc/menu/nexuiz/slider_resolution.c @@ -47,7 +47,7 @@ void saveCvarsNexuizResolutionSlider(entity me) { if(me.value >= 0 || me.value < me.nValues) { - tokenize(me.getIdentifier(me)); + tokenize_sane(me.getIdentifier(me)); cvar_set("vid_width", argv(0)); cvar_set("vid_height", argv(1)); cvar_set("vid_conwidth", argv(2)); diff --git a/data/qcsrc/menu/nexuiz/util.qc b/data/qcsrc/menu/nexuiz/util.qc index 53fce6ece..fe0a6d791 100644 --- a/data/qcsrc/menu/nexuiz/util.qc +++ b/data/qcsrc/menu/nexuiz/util.qc @@ -36,7 +36,7 @@ void saveCvarsMulti(entity me) me.saveCvars_Multi(me); s = cvar_string(me.cvarName); - n = tokenize(me.cvarNames_Multi); + n = tokenize_sane(me.cvarNames_Multi); for(i = 0; i < n; ++i) cvar_set(argv(i), s); } @@ -193,7 +193,7 @@ void() Item_Nex_ExtResponseSystem_CheckForResponse = local float argc; while(strlen((s = getextresponse()))) { - argc = tokenize(s); + argc = tokenize_sane(s); Item_Nex_ExtResponseSystem_Parse(argc); } } diff --git a/data/qcsrc/menu/nexuiz/weaponslist.c b/data/qcsrc/menu/nexuiz/weaponslist.c index fcaa34bfc..3c09ab8fa 100644 --- a/data/qcsrc/menu/nexuiz/weaponslist.c +++ b/data/qcsrc/menu/nexuiz/weaponslist.c @@ -40,7 +40,7 @@ void drawNexuizWeaponsList(entity me) print("AUTOFIXED\n"); cvar_set("cl_weaponpriority", t); } - me.nItems = tokenize(t); + me.nItems = tokenize_sane(t); drawListBox(me); } void WeaponsList_MoveUp_Click(entity box, entity me) @@ -99,7 +99,7 @@ string toStringNexuizWeaponsList(entity me) { float n, i; string s; - n = tokenize(cvar_string("cl_weaponpriority")); + n = tokenize_sane(cvar_string("cl_weaponpriority")); s = ""; for(i = 0; i < n; ++i) s = strcat(s, WeaponName(stof(argv(i))), ", "); diff --git a/data/qcsrc/menu/progs.src b/data/qcsrc/menu/progs.src index c7a1ddf51..86b18b82d 100644 --- a/data/qcsrc/menu/progs.src +++ b/data/qcsrc/menu/progs.src @@ -1,14 +1,15 @@ ../../menu.dat config.qh +../common/util-pre.qh msys.qh mbuiltin.qh +../common/util.qh oo/base.h ../common/constants.qh ../common/mapinfo.qh -../common/util.qh ../common/campaign_common.qh gamecommand.qh diff --git a/data/qcsrc/server/campaign.qc b/data/qcsrc/server/campaign.qc index 87bd31393..d4d61327d 100644 --- a/data/qcsrc/server/campaign.qc +++ b/data/qcsrc/server/campaign.qc @@ -76,7 +76,7 @@ void CampaignSaveCvar(string cvarname, float value) { while((l = fgets(fh))) { - len = tokenize(l); + len = tokenize_sane(l); if(len != 3) continue; if(argv(0) != "set") diff --git a/data/qcsrc/server/cl_player.qc b/data/qcsrc/server/cl_player.qc index 5c137506e..57ce671a4 100644 --- a/data/qcsrc/server/cl_player.qc +++ b/data/qcsrc/server/cl_player.qc @@ -74,7 +74,7 @@ vector animparseline(float animfile) if (animfile < 0) return '0 1 2'; line = fgets(animfile); - c = tokenize(line); + c = tokenize_sane(line); if (c != 3) { animparseerror = TRUE; @@ -802,7 +802,7 @@ float GetPlayerSoundSampleField_fixed; void PrecacheGlobalSound(string samplestring) { float n, i; - tokenize(samplestring); + tokenize_sane(samplestring); n = stof(argv(1)); if(n > 0) { @@ -824,7 +824,7 @@ void PrecachePlayerSounds(string f) return; while((s = fgets(fh))) { - if(tokenize(s) != 3) + if(tokenize_sane(s) != 3) { dprint("Invalid sound info line: ", s, "\n"); continue; @@ -868,7 +868,7 @@ void LoadPlayerSounds(string f, float first) return; while((s = fgets(fh))) { - if(tokenize(s) != 3) + if(tokenize_sane(s) != 3) continue; field = GetPlayerSoundSampleField(argv(0)); if(GetPlayerSoundSampleField_notFound) @@ -903,7 +903,7 @@ void GlobalSound(string sample, float chan, float teamsay) if(sample == "") return; - tokenize(sample); + tokenize_sane(sample); n = stof(argv(1)); if(n > 0) sample = strcat(argv(0), ftos(ceil(random() * n)), ".wav"); // randomization diff --git a/data/qcsrc/server/cl_weapons.qc b/data/qcsrc/server/cl_weapons.qc index 1ce8fc4b9..b208cf538 100644 --- a/data/qcsrc/server/cl_weapons.qc +++ b/data/qcsrc/server/cl_weapons.qc @@ -9,7 +9,7 @@ void W_SwitchWeapon(float imp) float W_GetCycleWeapon(entity pl, string weaponorder, float dir, float imp, float complain) { float n, i, weaponwant, first_valid, prev_valid, switchtonext, switchtolast; - n = tokenize(weaponorder); + n = tokenize_sane(weaponorder); switchtonext = switchtolast = 0; first_valid = prev_valid = 0; diff --git a/data/qcsrc/server/clientcommands.qc b/data/qcsrc/server/clientcommands.qc index 97057db50..e92d160c3 100644 --- a/data/qcsrc/server/clientcommands.qc +++ b/data/qcsrc/server/clientcommands.qc @@ -225,7 +225,7 @@ void SV_ParseClientCommand(string s) { local string cmd; local float i; - tokenize(s); + tokenize_sane(s); if(GameCommand_Vote(s, self)) { return; @@ -342,7 +342,7 @@ void SV_ParseClientCommand(string s) { } else if(argv(0) == "maplist") { local float n, j; local string col; - n = tokenize(cvar_string("g_maplist")); + n = tokenize_sane(cvar_string("g_maplist")); sprint(self, "^7Maps in list: "); for(i = 0, j = 0; i < n; ++i) { @@ -371,10 +371,10 @@ void SV_ParseClientCommand(string s) { } else if(argv(0) == "voice") { VoiceMessage(argv(1)); } else if(argv(0) == "say") { - Say(self, FALSE, substring(s, 4, strlen(s) - 4)); + Say(self, FALSE, substring(s, argv_start_index(1), argv_end_index(-1) - argv_start_index(1))); //clientcommand(self, formatmessage(s)); } else if(argv(0) == "say_team") { - Say(self, TRUE, substring(s, 9, strlen(s) - 9)); + Say(self, TRUE, substring(s, argv_start_index(1), argv_end_index(-1) - argv_start_index(1))); //clientcommand(self, formatmessage(s)); } else if(argv(0) == "info") { cmd = cvar_string(strcat("sv_info_", argv(1))); diff --git a/data/qcsrc/server/defs.qh b/data/qcsrc/server/defs.qh index 51e1ad6e0..b69583eca 100644 --- a/data/qcsrc/server/defs.qh +++ b/data/qcsrc/server/defs.qh @@ -245,7 +245,6 @@ float votefinished; .float vote_next; .float vote_vote; void VoteThink(); -string VoteParse(); float VoteAllowed(string vote); void VoteReset(); void VoteAccept(); diff --git a/data/qcsrc/server/g_world.qc b/data/qcsrc/server/g_world.qc index e64aa5ddc..ab0034368 100644 --- a/data/qcsrc/server/g_world.qc +++ b/data/qcsrc/server/g_world.qc @@ -168,7 +168,7 @@ void cvar_changes_init() return; while((s = fgets(fh))) { - n = tokenize(s); + n = tokenize_sane(s); if(n < 1) continue; if(argv(0) == "//") @@ -363,7 +363,7 @@ void spawnfunc_worldspawn (void) { while((s = fgets(fd))) { - l = tokenize(s); + l = tokenize_sane(s); if(l < 2) continue; if(argv(0) == "cd") diff --git a/data/qcsrc/server/gamecommand.qc b/data/qcsrc/server/gamecommand.qc index 7649815a4..994696de7 100644 --- a/data/qcsrc/server/gamecommand.qc +++ b/data/qcsrc/server/gamecommand.qc @@ -420,7 +420,7 @@ void EffectIndexDump() fh = fopen("effectinfo.txt", FILE_READ); while((s = fgets(fh))) { - tokenize(s); + tokenize_sane(s); if(argv(0) == "effect") { if(db_get(d, argv(1)) != "1") @@ -456,7 +456,7 @@ void GameCommand(string command) float argc; entity client; float entno; - argc = tokenize(command); + argc = tokenize_sane(command); if(argv(0) == "help" || argc == 0) { diff --git a/data/qcsrc/server/ipban.qc b/data/qcsrc/server/ipban.qc index 1fe534e9e..a49807a96 100644 --- a/data/qcsrc/server/ipban.qc +++ b/data/qcsrc/server/ipban.qc @@ -55,7 +55,7 @@ void Ban_LoadBans() Ban_Delete(i); ban_count = 0; ban_loaded = TRUE; - n = tokenize(cvar_string("g_banned_list")); + n = tokenize_sane(cvar_string("g_banned_list")); if(stof(argv(0)) == 1) { ban_count = (n - 1) / 2; @@ -207,7 +207,7 @@ float GameCommand_Ban(string command) float entno; float masksize; - argc = tokenize(command); + argc = tokenize_sane(command); if(argv(0) == "help") { print(" kickban # n m p - kickban player n for m seconds, using mask size p (1 to 4)\n"); diff --git a/data/qcsrc/server/progs.src b/data/qcsrc/server/progs.src index 7a75f862e..1fcd0b331 100644 --- a/data/qcsrc/server/progs.src +++ b/data/qcsrc/server/progs.src @@ -1,12 +1,13 @@ ../../progs.dat // output filename +../common/util-pre.qh sys.qh builtins.qh constants.qh ../common/constants.qh defs.qh // Should rename this, it has fields and globals - extensions.qh +../common/util.qh //// tZork Turrets //// tturrets/include/turret_tturrets_early.qh @@ -14,7 +15,6 @@ tturrets/include/turret_tturrets_early.qh campaign.qh ../common/campaign_common.qh ../common/mapinfo.qh -../common/util.qh ../common/util.qc portals.qh diff --git a/data/qcsrc/server/t_items.qc b/data/qcsrc/server/t_items.qc index 7fe8018b2..d2b6c1aab 100644 --- a/data/qcsrc/server/t_items.qc +++ b/data/qcsrc/server/t_items.qc @@ -613,7 +613,7 @@ void weapon_defaultspawnfunc(float wpn) if(self.classname != "droppedweapon" && self.classname != "replacedweapon") { s = cvar_string(strcat("g_weaponreplace_", ftos(wpn))); - t = tokenize(s); + t = tokenize_sane(s); if(t >= 2) { self.team = --internalteam; @@ -1128,7 +1128,7 @@ void spawnfunc_target_items (void) precache_sound("misc/powerup.wav"); precache_sound("weapons/weaponpickup.wav"); - n = tokenize(self.netname); + n = tokenize_sane(self.netname); for(i = 0; i < n; ++i) { if(argv(i) == "unlimited_ammo") self.items |= IT_UNLIMITED_AMMO; diff --git a/data/qcsrc/server/vote.qc b/data/qcsrc/server/vote.qc index 46e1bc669..a4582daea 100644 --- a/data/qcsrc/server/vote.qc +++ b/data/qcsrc/server/vote.qc @@ -14,11 +14,11 @@ float VoteCheckNasty(string cmd) entity GetKickVoteVictim(string vote, string cmd, entity caller) { float tokens; - float i, n, t; + float n, t; string ns; entity e; - tokens = tokenize(vote); + tokens = tokenize_sane(vote); ns = ""; if(tokens >= 2) @@ -37,10 +37,7 @@ entity GetKickVoteVictim(string vote, string cmd, entity caller) if(ns != "") { - GetKickVoteVictim_reason = ""; - for(i = t; i < tokens; ++i) - GetKickVoteVictim_reason = strcat(GetKickVoteVictim_reason, argv(i), " "); - GetKickVoteVictim_reason = substring(GetKickVoteVictim_reason, 0, strlen(GetKickVoteVictim_reason) - 1); + GetKickVoteVictim_reason = substring(vote, argv_start_index(t), argv_end_index(-1) - argv_start_index(t)); n = stof(ns); if(ns == ftos(n)) if(n >= 1) if(n <= maxclients) @@ -58,8 +55,57 @@ entity GetKickVoteVictim(string vote, string cmd, entity caller) return world; } +string RemapVote_display; +string RemapVote_vote; +float RemapVote(string vote, string cmd, entity e) +{ + float vote_argc; + entity victim; + vote_argc = tokenize_sane(vote); + + print(">>", vote, "<<\n"); + + if(!VoteAllowed(argv(0))) + { + return FALSE; + } + + // VoteAllowed tokenizes! + vote_argc = tokenize_sane(vote); + + // remap chmap to gotomap (forces intermission) + if(vote_argc < 2) + if(argv(0) == "chmap" || argv(0) == "gotomap" || argv(0) == "kick" || argv(0) == "kickban") // won't work without arguments + return FALSE; + if(argv(0) == "chmap") + vote = strcat("gotomap ", substring(vote, argv_start_index(1), argv_end_index(-1) - argv_start_index(1))); + if(argv(0) == "gotomap") + { + if(!(vote = ValidateMap(substring(vote, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)), e))) + return FALSE; + vote = strcat("gotomap ", vote); + vote_argc = tokenize_sane(vote); // ValidateMap may have done some stuff to it + } + + // make kick and kickban votes a bit nicer (and reject them if formatted badly) + if(argv(0) == "kick" || argv(0) == "kickban") + { + if(!(victim = GetKickVoteVictim(vote, cmd, e))) + return FALSE; + RemapVote_vote = GetKickVoteVictim_newcommand; + RemapVote_display = strcat("^1", vote, " (^7", victim.netname, "^1): ", GetKickVoteVictim_reason); + } + else + { + RemapVote_vote = vote; + RemapVote_display = strzone(strcat("^1", vote)); + } + + return TRUE; +} + float GameCommand_Vote(string s, entity e) { - local entity victim; + tokenize_sane(s); if(argv(0) == "help") { print_to(e, " vote COMMANDS ARGUMENTS. See 'vote help' for more info."); return TRUE; @@ -86,7 +132,7 @@ float GameCommand_Vote(string s, entity e) { print_to(e, "^1There is already a vote called."); } else { local string vote; - vote = VoteParse(); + vote = VoteParse(s); if(vote == "") { print_to(e, "^1Your vote is empty. See help for more info."); } else if(e @@ -94,37 +140,9 @@ float GameCommand_Vote(string s, entity e) { print_to(e, strcat("^1You have to wait ^2", ftos(e.vote_next - time), "^1 seconds before you can again call a vote.")); } else if(VoteCheckNasty(vote)) { print_to(e, "Syntax error in command. See help for more info."); - } else if(VoteAllowed(strcat1(argv(2)))) { // strcat seems to be necessary - // remap chmap to gotomap (forces intermission) - if(vote == "chmap" || vote == "gotomap") // won't work without arguments - return TRUE; - if(substring(vote, 0, 6) == "chmap ") - vote = strcat("gotomap ", substring(vote, 6, strlen(vote) - 6)); - if(substring(vote, 0, 8) == "gotomap ") - { - if(!(vote = ValidateMap(substring(vote, 8, strlen(vote) - 8), e))) - return TRUE; - vote = strcat("gotomap ", vote); - } - - // make kick and kickban votes a bit nicer (and reject them if formatted badly) - if(substring(vote, 0, 5) == "kick " || substring(vote, 0, 8) == "kickban ") - { - if(!(victim = GetKickVoteVictim(vote, "vcall", e))) - return TRUE; - vote = GetKickVoteVictim_newcommand; - votecalledvote_display = strzone(strcat("^1", vote, " (^7", victim.netname, "^1): ", GetKickVoteVictim_reason)); - } - else if(vote == "kick" || vote == "kickban") - { - print_to(e, strcat("Usage: ", vote, " #playernumber (as in \"status\")\n")); - return TRUE; - } - else - { - votecalledvote_display = strzone(strcat("^1", vote)); - } - votecalledvote = strzone(vote); + } else if(RemapVote(vote, "vcall", e)) { + votecalledvote = strzone(RemapVote_vote); + votecalledvote_display = strzone(RemapVote_display); votecalled = TRUE; votecalledmaster = FALSE; votefinished = time + cvar("sv_vote_timeout"); @@ -183,41 +201,17 @@ float GameCommand_Vote(string s, entity e) { } } else if(argv(1) == "do") { if(!e || e.vote_master) { - local string dovote, dovote_display; - dovote = VoteParse(); + local string dovote; + dovote = VoteParse(s); if(dovote == "") { print_to(e, "^1Your command was empty. See help for more info."); } else if(VoteCheckNasty(dovote)) { print_to(e, "Syntax error in command. See help for more info."); - } else if(VoteAllowed(strcat1(argv(2)))) { // strcat seems to be necessary - if(dovote == "chmap" || dovote == "gotomap") // won't work without arguments - return TRUE; - if(substring(dovote, 0, 6) == "chmap ") - dovote = strcat("gotomap ", substring(dovote, 6, strlen(dovote) - 6)); - if(substring(dovote, 0, 8) == "gotomap ") - { - if(!(dovote = ValidateMap(substring(dovote, 8, strlen(dovote) - 8), e))) - return TRUE; - dovote = strcat("gotomap ", dovote); - } - - dovote_display = dovote; - if(substring(dovote, 0, 5) == "kick " || substring(dovote, 0, 8) == "kickban ") - { - if(!(victim = GetKickVoteVictim(dovote, "vdo", e))) - return TRUE; - dovote = GetKickVoteVictim_newcommand; - dovote_display = strcat("^1", dovote, " (^7", victim.netname, "^1): ", GetKickVoteVictim_reason); - } - else if(dovote == "kick" || dovote == "kickban") - { - print_to(e, strcat("Usage: ", dovote, " #playernumber (as in \"status\")\n")); - return TRUE; - } - bprint("\{1}^2* ^3", VoteNetname(e), "^2 used his ^3master^2 status to do \"^2", dovote_display, "^2\".\n"); + } else if(RemapVote(dovote, "vdo", e)) { // strcat seems to be necessary + bprint("\{1}^2* ^3", VoteNetname(e), "^2 used his ^3master^2 status to do \"^2", RemapVote_display, "^2\".\n"); if(cvar("sv_eventlog")) - GameLogEcho(strcat(":vote:vdo:", ftos(e.playerid), ":", dovote_display)); - localcmd(strcat(dovote, "\n")); + GameLogEcho(strcat(":vote:vdo:", ftos(e.playerid), ":", RemapVote_display)); + localcmd(strcat(RemapVote_vote, "\n")); } else { print_to(e, "^1This command is not ok. See help for more info."); } @@ -378,21 +372,12 @@ void VoteThink() { } } -string VoteParse() { - local float index; - index = 3; - local string vote; - vote = argv(2); - while(argv(index) != "") { - vote = strcat(vote, " ", argv(index)); - ++index; - } - - return vote; +string VoteParse(string all) { + return substring(all, argv_start_index(2), argv_end_index(-1) - argv_start_index(2)); } float VoteAllowed(string votecommand) { - tokenize(cvar_string("sv_vote_commands")); + tokenize_sane(cvar_string("sv_vote_commands")); local float index; index = 0; while(argv(index) != "") { diff --git a/data/qcsrc/server/vote.qh b/data/qcsrc/server/vote.qh index 1a71afc29..7007b7eb3 100644 --- a/data/qcsrc/server/vote.qh +++ b/data/qcsrc/server/vote.qh @@ -8,7 +8,7 @@ void VoteHelp(entity e); string VoteNetname(entity e); string ValidateMap(string m, entity e); void VoteThink(); -string VoteParse(); +string VoteParse(string s); float VoteAllowed(string votecommand); void VoteReset(); void VoteAccept(); -- 2.39.2