/*
Copyright (C) 1996-1997 Id Software, Inc.
+Copyright (C) 2000-2020 DarkPlaces contributors
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
#include "quakedef.h"
#include "utf8lib.h"
-cvar_t registered = {CVAR_CLIENT | CVAR_SERVER, "registered","0", "indicates if this is running registered quake (whether gfx/pop.lmp was found)"};
-cvar_t cmdline = {CVAR_CLIENT | CVAR_SERVER, "cmdline","0", "contains commandline the engine was launched with"};
+cvar_t registered = {CF_CLIENT | CF_SERVER, "registered","0", "indicates if this is running registered quake (whether gfx/pop.lmp was found)"};
+cvar_t cmdline = {CF_CLIENT | CF_SERVER, "cmdline","0", "contains commandline the engine was launched with"};
// FIXME: Find a better place for these.
-cvar_t cl_playermodel = {CVAR_CLIENT | CVAR_SERVER | CVAR_USERINFO | CVAR_SAVE, "playermodel", "", "current player model in Nexuiz/Xonotic"};
-cvar_t cl_playerskin = {CVAR_CLIENT | CVAR_SERVER | CVAR_USERINFO | CVAR_SAVE, "playerskin", "", "current player skin in Nexuiz/Xonotic"};
+cvar_t cl_playermodel = {CF_CLIENT | CF_SERVER | CF_USERINFO | CF_ARCHIVE, "playermodel", "", "current player model in Nexuiz/Xonotic"};
+cvar_t cl_playerskin = {CF_CLIENT | CF_SERVER | CF_USERINFO | CF_ARCHIVE, "playerskin", "", "current player skin in Nexuiz/Xonotic"};
char com_token[MAX_INPUTLINE];
-gamemode_t gamemode;
-const char *gamename;
-const char *gamenetworkfiltername; // same as gamename currently but with _ in place of spaces so that "getservers" packets parse correctly (this also means the
-const char *gamedirname1;
-const char *gamedirname2;
-const char *gamescreenshotname;
-const char *gameuserdirname;
-char com_modname[MAX_OSPATH] = "";
-
//===========================================================================
void SZ_Clear (sizebuf_t *buf)
// If it fits, append it. Continue.
// If it doesn't fit, output current line, advance to next line. Append the word. This is a continuation. Continue.
- qboolean isContinuation = false;
+ qbool isContinuation = false;
float spaceWidth;
const char *startOfLine = string;
const char *cursor = string;
return result;
/*
- qboolean isContinuation = false;
+ qbool isContinuation = false;
float currentWordSpace = 0;
const char *currentWord = 0;
float minReserve = 0;
Parse a token out of a string
==============
*/
-int COM_ParseToken_Simple(const char **datapointer, qboolean returnnewline, qboolean parsebackslash, qboolean parsecomments)
+int COM_ParseToken_Simple(const char **datapointer, qbool returnnewline, qbool parsebackslash, qbool parsecomments)
{
int len;
int c;
Parse a token out of a string
==============
*/
-int COM_ParseToken_QuakeC(const char **datapointer, qboolean returnnewline)
+int COM_ParseToken_QuakeC(const char **datapointer, qbool returnnewline)
{
int len;
int c;
Parse a token out of a string
==============
*/
-int COM_ParseToken_VM_Tokenize(const char **datapointer, qboolean returnnewline)
+int COM_ParseToken_VM_Tokenize(const char **datapointer, qbool returnnewline)
{
int len;
int c;
return true;
}
-
/*
-================
-COM_CheckParm
+===============
+Com_CalcRoll
-Returns the position (1 to argc-1) in the program's argument list
-where the given parameter apears, or 0 if not present
-================
+Used by view and sv_user
+===============
*/
-int COM_CheckParm (const char *parm)
+float Com_CalcRoll (const vec3_t angles, const vec3_t velocity, const vec_t angleval, const vec_t velocityval)
{
- int i;
-
- for (i=1 ; i<sys.argc ; i++)
- {
- if (!sys.argv[i])
- continue; // NEXTSTEP sometimes clears appkit vars.
- if (!strcmp (parm,sys.argv[i]))
- return i;
- }
-
- return 0;
-}
-
-//===========================================================================
-
-// Game mods
-
-gamemode_t com_startupgamemode;
-gamemode_t com_startupgamegroup;
-
-typedef struct gamemode_info_s
-{
- gamemode_t mode; // this gamemode
- gamemode_t group; // different games with same group can switch automatically when gamedirs change
- const char* prog_name; // not null
- const char* cmdline; // not null
- const char* gamename; // not null
- const char* gamenetworkfiltername; // not null
- const char* gamedirname1; // not null
- const char* gamedirname2; // null
- const char* gamescreenshotname; // not nul
- const char* gameuserdirname; // not null
-} gamemode_info_t;
-
-static const gamemode_info_t gamemode_info [GAME_COUNT] =
-{// game basegame prog_name cmdline gamename gamenetworkfilername basegame modgame screenshot userdir // commandline option
-{ GAME_NORMAL, GAME_NORMAL, "", "-quake", "DarkPlaces-Quake", "DarkPlaces-Quake", "id1", NULL, "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
-{ GAME_HIPNOTIC, GAME_NORMAL, "hipnotic", "-hipnotic", "Darkplaces-Hipnotic", "Darkplaces-Hipnotic", "id1", "hipnotic", "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -hipnotic runs Quake mission pack 1: The Scourge of Armagon
-{ GAME_ROGUE, GAME_NORMAL, "rogue", "-rogue", "Darkplaces-Rogue", "Darkplaces-Rogue", "id1", "rogue", "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -rogue runs Quake mission pack 2: The Dissolution of Eternity
-{ GAME_NEHAHRA, GAME_NORMAL, "nehahra", "-nehahra", "DarkPlaces-Nehahra", "DarkPlaces-Nehahra", "id1", "nehahra", "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game
-{ GAME_QUOTH, GAME_NORMAL, "quoth", "-quoth", "Darkplaces-Quoth", "Darkplaces-Quoth", "id1", "quoth", "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -quoth runs the Quoth mod for playing community maps made for it
-{ GAME_NEXUIZ, GAME_NEXUIZ, "nexuiz", "-nexuiz", "Nexuiz", "Nexuiz", "data", NULL, "nexuiz", "nexuiz" }, // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
-{ GAME_XONOTIC, GAME_XONOTIC, "xonotic", "-xonotic", "Xonotic", "Xonotic", "data", NULL, "xonotic", "xonotic" }, // COMMANDLINEOPTION: Game: -xonotic runs the multiplayer game Xonotic
-{ GAME_TRANSFUSION, GAME_TRANSFUSION, "transfusion", "-transfusion", "Transfusion", "Transfusion", "basetf", NULL, "transfusion", "transfusion" }, // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
-{ GAME_GOODVSBAD2, GAME_GOODVSBAD2, "gvb2", "-goodvsbad2", "GoodVs.Bad2", "GoodVs.Bad2", "rts", NULL, "gvb2", "gvb2" }, // COMMANDLINEOPTION: Game: -goodvsbad2 runs the psychadelic RTS FPS game Good Vs Bad 2
-{ GAME_TEU, GAME_TEU, "teu", "-teu", "TheEvilUnleashed", "TheEvilUnleashed", "baseteu", NULL, "teu", "teu" }, // COMMANDLINEOPTION: Game: -teu runs The Evil Unleashed (this option is obsolete as they are not using darkplaces)
-{ GAME_BATTLEMECH, GAME_BATTLEMECH, "battlemech", "-battlemech", "Battlemech", "Battlemech", "base", NULL, "battlemech", "battlemech" }, // COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech
-{ GAME_ZYMOTIC, GAME_ZYMOTIC, "zymotic", "-zymotic", "Zymotic", "Zymotic", "basezym", NULL, "zymotic", "zymotic" }, // COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
-{ GAME_SETHERAL, GAME_SETHERAL, "setheral", "-setheral", "Setheral", "Setheral", "data", NULL, "setheral", "setheral" }, // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
-{ GAME_TENEBRAE, GAME_NORMAL, "tenebrae", "-tenebrae", "DarkPlaces-Tenebrae", "DarkPlaces-Tenebrae", "id1", "tenebrae", "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -tenebrae runs the graphics test mod known as Tenebrae (some features not implemented)
-{ GAME_NEOTERIC, GAME_NORMAL, "neoteric", "-neoteric", "Neoteric", "Neoteric", "id1", "neobase", "neo", "darkplaces" }, // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
-{ GAME_OPENQUARTZ, GAME_NORMAL, "openquartz", "-openquartz", "OpenQuartz", "OpenQuartz", "id1", NULL, "openquartz", "darkplaces" }, // COMMANDLINEOPTION: Game: -openquartz runs the game OpenQuartz, a standalone GPL replacement of the quake content
-{ GAME_PRYDON, GAME_NORMAL, "prydon", "-prydon", "PrydonGate", "PrydonGate", "id1", "prydon", "prydon", "darkplaces" }, // COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate
-{ GAME_DELUXEQUAKE, GAME_DELUXEQUAKE, "dq", "-dq", "Deluxe Quake", "Deluxe_Quake", "basedq", "extradq", "basedq", "dq" }, // COMMANDLINEOPTION: Game: -dq runs the game Deluxe Quake
-{ GAME_THEHUNTED, GAME_THEHUNTED, "thehunted", "-thehunted", "The Hunted", "The_Hunted", "thdata", NULL, "th", "thehunted" }, // COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted
-{ GAME_DEFEATINDETAIL2, GAME_DEFEATINDETAIL2, "did2", "-did2", "Defeat In Detail 2", "Defeat_In_Detail_2", "data", NULL, "did2_", "did2" }, // COMMANDLINEOPTION: Game: -did2 runs the game Defeat In Detail 2
-{ GAME_DARSANA, GAME_DARSANA, "darsana", "-darsana", "Darsana", "Darsana", "ddata", NULL, "darsana", "darsana" }, // COMMANDLINEOPTION: Game: -darsana runs the game Darsana
-{ GAME_CONTAGIONTHEORY, GAME_CONTAGIONTHEORY, "contagiontheory", "-contagiontheory", "Contagion Theory", "Contagion_Theory", "ctdata", NULL, "ct", "contagiontheory" }, // COMMANDLINEOPTION: Game: -contagiontheory runs the game Contagion Theory
-{ GAME_EDU2P, GAME_EDU2P, "edu2p", "-edu2p", "EDU2 Prototype", "EDU2_Prototype", "id1", "edu2", "edu2_p", "edu2prototype" }, // COMMANDLINEOPTION: Game: -edu2p runs the game Edu2 prototype
-{ GAME_PROPHECY, GAME_PROPHECY, "prophecy", "-prophecy", "Prophecy", "Prophecy", "gamedata", NULL, "phcy", "prophecy" }, // COMMANDLINEOPTION: Game: -prophecy runs the game Prophecy
-{ GAME_BLOODOMNICIDE, GAME_BLOODOMNICIDE, "omnicide", "-omnicide", "Blood Omnicide", "Blood_Omnicide", "kain", NULL, "omnicide", "omnicide" }, // COMMANDLINEOPTION: Game: -omnicide runs the game Blood Omnicide
-{ GAME_STEELSTORM, GAME_STEELSTORM, "steelstorm", "-steelstorm", "Steel-Storm", "Steel-Storm", "gamedata", NULL, "ss", "steelstorm" }, // COMMANDLINEOPTION: Game: -steelstorm runs the game Steel Storm
-{ GAME_STEELSTORM2, GAME_STEELSTORM2, "steelstorm2", "-steelstorm2", "Steel Storm 2", "Steel_Storm_2", "gamedata", NULL, "ss2", "steelstorm2" }, // COMMANDLINEOPTION: Game: -steelstorm2 runs the game Steel Storm 2
-{ GAME_SSAMMO, GAME_SSAMMO, "steelstorm-ammo", "-steelstormammo", "Steel Storm A.M.M.O.", "Steel_Storm_A.M.M.O.", "gamedata", NULL, "ssammo", "steelstorm-ammo" }, // COMMANDLINEOPTION: Game: -steelstormammo runs the game Steel Storm A.M.M.O.
-{ GAME_STEELSTORMREVENANTS, GAME_STEELSTORMREVENANTS, "steelstorm-revenants", "-steelstormrev", "Steel Storm: Revenants", "Steel_Storm_Revenants", "base", NULL, "ssrev", "steelstorm-revenants" }, // COMMANDLINEOPTION: Game: -steelstormrev runs the game Steel Storm: Revenants
-{ GAME_TOMESOFMEPHISTOPHELES, GAME_TOMESOFMEPHISTOPHELES, "tomesofmephistopheles","-tomesofmephistopheles", "Tomes of Mephistopheles", "Tomes_of_Mephistopheles", "gamedata", NULL, "tom", "tomesofmephistopheles" }, // COMMANDLINEOPTION: Game: -tomesofmephistopheles runs the game Tomes of Mephistopheles
-{ GAME_STRAPBOMB, GAME_STRAPBOMB, "strapbomb", "-strapbomb", "Strap-on-bomb Car", "Strap-on-bomb_Car", "id1", NULL, "strap", "strapbomb" }, // COMMANDLINEOPTION: Game: -strapbomb runs the game Strap-on-bomb Car
-{ GAME_MOONHELM, GAME_MOONHELM, "moonhelm", "-moonhelm", "MoonHelm", "MoonHelm", "data", NULL, "mh", "moonhelm" }, // COMMANDLINEOPTION: Game: -moonhelm runs the game MoonHelm
-{ GAME_VORETOURNAMENT, GAME_VORETOURNAMENT, "voretournament", "-voretournament", "Vore Tournament", "Vore_Tournament", "data", NULL, "voretournament", "voretournament" }, // COMMANDLINEOPTION: Game: -voretournament runs the multiplayer game Vore Tournament
-{ GAME_DOOMBRINGER, GAME_DOOMBRINGER, "doombringer", "-doombringer", "DOOMBRINGER", "DOOMBRINGER", "dbdata", NULL, "doombringer", "doombringer" }, // COMMANDLINEOPTION: Game: -doombringer runs the game DOOMBRINGER
-};
-
-static void COM_SetGameType(int index);
-void COM_InitGameType (void)
-{
- char name [MAX_OSPATH];
- int i;
- int index = 0;
-
-#ifdef FORCEGAME
- COM_ToLowerString(FORCEGAME, name, sizeof (name));
-#else
- // check executable filename for keywords, but do it SMARTLY - only check the last path element
- FS_StripExtension(FS_FileWithoutPath(sys.argv[0]), name, sizeof (name));
- COM_ToLowerString(name, name, sizeof (name));
-#endif
- for (i = 1;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
- if (gamemode_info[i].prog_name && gamemode_info[i].prog_name[0] && strstr (name, gamemode_info[i].prog_name))
- index = i;
-
- // check commandline options for keywords
- for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
- if (COM_CheckParm (gamemode_info[i].cmdline))
- index = i;
-
- com_startupgamemode = gamemode_info[index].mode;
- com_startupgamegroup = gamemode_info[index].group;
- COM_SetGameType(index);
-}
-
-void COM_ChangeGameTypeForGameDirs(void)
-{
- int i;
- int index = -1;
- // this will not not change the gamegroup
- // first check if a base game (single gamedir) matches
- for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
- {
- if (gamemode_info[i].group == com_startupgamegroup && !(gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]))
- {
- index = i;
- break;
- }
- }
- // now that we have a base game, see if there is a matching derivative game (two gamedirs)
- if (fs_numgamedirs)
- {
- for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
- {
- if (gamemode_info[i].group == com_startupgamegroup && (gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]) && !strcasecmp(fs_gamedirs[0], gamemode_info[i].gamedirname2))
- {
- index = i;
- break;
- }
- }
- }
- // we now have a good guess at which game this is meant to be...
- if (index >= 0 && gamemode != gamemode_info[index].mode)
- COM_SetGameType(index);
-}
+ vec3_t forward, right, up;
+ float sign;
+ float side;
-static void COM_SetGameType(int index)
-{
- static char gamenetworkfilternamebuffer[64];
- int i, t;
- if (index < 0 || index >= (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0])))
- index = 0;
- gamemode = gamemode_info[index].mode;
- gamename = gamemode_info[index].gamename;
- gamenetworkfiltername = gamemode_info[index].gamenetworkfiltername;
- gamedirname1 = gamemode_info[index].gamedirname1;
- gamedirname2 = gamemode_info[index].gamedirname2;
- gamescreenshotname = gamemode_info[index].gamescreenshotname;
- gameuserdirname = gamemode_info[index].gameuserdirname;
-
- if (gamemode == com_startupgamemode)
- {
- if((t = COM_CheckParm("-customgamename")) && t + 1 < sys.argc)
- gamename = gamenetworkfiltername = sys.argv[t+1];
- if((t = COM_CheckParm("-customgamenetworkfiltername")) && t + 1 < sys.argc)
- gamenetworkfiltername = sys.argv[t+1];
- if((t = COM_CheckParm("-customgamedirname1")) && t + 1 < sys.argc)
- gamedirname1 = sys.argv[t+1];
- if((t = COM_CheckParm("-customgamedirname2")) && t + 1 < sys.argc)
- gamedirname2 = *sys.argv[t+1] ? sys.argv[t+1] : NULL;
- if((t = COM_CheckParm("-customgamescreenshotname")) && t + 1 < sys.argc)
- gamescreenshotname = sys.argv[t+1];
- if((t = COM_CheckParm("-customgameuserdirname")) && t + 1 < sys.argc)
- gameuserdirname = sys.argv[t+1];
- }
+ AngleVectors (angles, forward, right, up);
+ side = DotProduct (velocity, right);
+ sign = side < 0 ? -1 : 1;
+ side = fabs(side);
- if (gamedirname2 && gamedirname2[0])
- Con_Printf("Game is %s using base gamedirs %s %s", gamename, gamedirname1, gamedirname2);
+ if (side < velocityval)
+ side = side * angleval / velocityval;
else
- Con_Printf("Game is %s using base gamedir %s", gamename, gamedirname1);
- for (i = 0;i < fs_numgamedirs;i++)
- {
- if (i == 0)
- Con_Printf(", with mod gamedirs");
- Con_Printf(" %s", fs_gamedirs[i]);
- }
- Con_Printf("\n");
+ side = angleval;
- if (strchr(gamenetworkfiltername, ' '))
- {
- char *s;
- // if there are spaces in the game's network filter name it would
- // cause parse errors in getservers in dpmaster, so we need to replace
- // them with _ characters
- strlcpy(gamenetworkfilternamebuffer, gamenetworkfiltername, sizeof(gamenetworkfilternamebuffer));
- while ((s = strchr(gamenetworkfilternamebuffer, ' ')) != NULL)
- *s = '_';
- gamenetworkfiltername = gamenetworkfilternamebuffer;
- }
+ return side*sign;
- Con_Printf("gamename for server filtering: %s\n", gamenetworkfiltername);
}
+//===========================================================================
/*
================
Cvar_RegisterVariable (®istered);
Cvar_RegisterVariable (&cmdline);
Cvar_RegisterVariable(&cl_playermodel);
- Cvar_RegisterAlias(&cl_playermodel, "_cl_playermodel");
+ Cvar_RegisterVirtual(&cl_playermodel, "_cl_playermodel");
Cvar_RegisterVariable(&cl_playerskin);
- Cvar_RegisterAlias(&cl_playerskin, "_cl_playerskin");
+ Cvar_RegisterVirtual(&cl_playerskin, "_cl_playerskin");
// reconstitute the command line for the cmdline externally visible cvar
n = 0;
if (result < 0 || (size_t)result >= buffersize)
{
buffer[buffersize - 1] = '\0';
+ // we could be inside Con_Printf
+ if (result < 0)
+ Sys_Printf("dpvsnprintf: output error, buffer size %zu\n", buffersize);
+ else
+ Sys_Printf("dpvsnprintf: truncated to %zu bytes: \"%s\"\n", buffersize - 1, buffer);
return -1;
}
============
*/
size_t
-COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
+COM_StringLengthNoColors(const char *s, size_t size_s, qbool *valid)
{
const char *end = size_s ? (s + size_s) : NULL;
size_t len = 0;
all characters until the zero terminator.
============
*/
-qboolean
-COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qboolean escape_carets)
+qbool
+COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qbool escape_carets)
{
#define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return false; } } while(0)
const char *end = size_in ? (in + size_in) : NULL;
#undef APPEND
}
-char *InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
-{
- int pos = 0, j;
- size_t keylength;
- if (!key)
- key = "";
- keylength = strlen(key);
- if (valuelength < 1 || !value)
- {
- Con_Printf("InfoString_GetValue: no room in value\n");
- return NULL;
- }
- value[0] = 0;
- if (strchr(key, '\\'))
- {
- Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
- return NULL;
- }
- if (strchr(key, '\"'))
- {
- Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
- return NULL;
- }
- if (!key[0])
- {
- Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
- return NULL;
- }
- while (buffer[pos] == '\\')
- {
- if (!memcmp(buffer + pos+1, key, keylength) &&
- (buffer[pos+1 + keylength] == 0 ||
- buffer[pos+1 + keylength] == '\\'))
- {
- pos += 1 + (int)keylength; // Skip \key
- if (buffer[pos] == '\\') pos++; // Skip \ before value.
- for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
- value[j] = buffer[pos+j];
- value[j] = 0;
- return value;
- }
- if (buffer[pos] == '\\') pos++; // Skip \ before value.
- for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
- if (buffer[pos] == '\\') pos++; // Skip \ before value.
- for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
- }
- // if we reach this point the key was not found
- return NULL;
-}
-void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
+/*
+============
+String Copying
+
+The glibc implementation of memccpy is several times faster than old functions that
+copy one byte at a time (even at -O3) and its advantage increases with string length.
+============
+*/
+#ifdef WIN32
+ // memccpy() is standard in POSIX.1-2001, POSIX.1-2008, SVr4, 4.3BSD, C23.
+ // Microsoft supports it, but apparently complains if we use it.
+ #pragma warning(disable : 4996)
+#endif
+
+/** Chain-copies a string with truncation and efficiency (compared to strlcat()).
+ * The destination ends at an absolute pointer instead of a relative offset
+ * and a pointer to the \0 terminator is returned on success.
+ * Truncates, warns, and returns the end pointer on overflow or unterminated source.
+ * Guarantees \0 termination. end = dst + sizeof(src[])
+ */
+char *dp_stpecpy(char *dst, char *end, const char *src)
{
- int pos = 0, pos2;
- size_t keylength;
- if (!key)
- key = "";
- if (!value)
- value = "";
- keylength = strlen(key);
- if (strchr(key, '\\') || strchr(value, '\\'))
- {
- Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
- return;
- }
- if (strchr(key, '\"') || strchr(value, '\"'))
- {
- Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
- return;
- }
- if (!key[0])
- {
- Con_Printf("InfoString_SetValue: can not set a key with no name\n");
- return;
- }
- while (buffer[pos] == '\\')
- {
- if (!memcmp(buffer + pos+1, key, keylength) &&
- (buffer[pos+1 + keylength] == 0 ||
- buffer[pos+1 + keylength] == '\\'))
- break;
- if (buffer[pos] == '\\') pos++; // Skip \ before value.
- for (;buffer[pos] && buffer[pos] != '\\';pos++);
- if (buffer[pos] == '\\') pos++; // Skip \ before value.
- for (;buffer[pos] && buffer[pos] != '\\';pos++);
- }
- // if we found the key, find the end of it because we will be replacing it
- pos2 = pos;
- if (buffer[pos] == '\\')
- {
- pos2 += 1 + (int)keylength; // Skip \key
- if (buffer[pos2] == '\\') pos2++; // Skip \ before value.
- for (;buffer[pos2] && buffer[pos2] != '\\';pos2++);
- }
- if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
- {
- Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
- return;
- }
- if (value[0])
- {
- // set the key/value and append the remaining text
- char tempbuffer[MAX_INPUTLINE];
- strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
- dpsnprintf(buffer + pos, bufferlength - pos, "\\%s\\%s%s", key, value, tempbuffer);
- }
- else
- {
- // just remove the key from the text
- strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
- }
+ char *p = (char *)memccpy(dst, src, '\0', end - dst);
+
+ if (p)
+ return p - 1;
+ end[-1] = '\0';
+ Con_Printf(CON_WARN "%s: src string unterminated or truncated to %zu bytes: \"%s\"\n", __func__, dst == end ? 0 : (end - dst) - 1, dst);
+ return end;
}
-void InfoString_Print(char *buffer)
+/** Copies a measured byte sequence (unterminated string) to a null-terminated string.
+ * Returns a pointer to the \0 terminator. Guarantees \0 termination.
+ * Compared to ustr2stp(): truncates and warns on overflow.
+ */
+char *dp_ustr2stp(char *dst, size_t dsize, const char *src, size_t ssize)
{
- int i;
- char key[MAX_INPUTLINE];
- char value[MAX_INPUTLINE];
- while (*buffer)
+ if (ssize >= dsize)
{
- if (*buffer != '\\')
- {
- Con_Printf("InfoString_Print: corrupt string\n");
- return;
- }
- for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
- if (i < (int)sizeof(key)-1)
- key[i++] = *buffer;
- key[i] = 0;
- if (*buffer != '\\')
- {
- Con_Printf("InfoString_Print: corrupt string\n");
- return;
- }
- for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
- if (i < (int)sizeof(value)-1)
- value[i++] = *buffer;
- value[i] = 0;
- // empty value is an error case
- Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
+ ssize = dsize - 1;
+ Con_Printf(CON_WARN "%s: src string truncated to %zu bytes: \"%.*s\"\n", __func__, ssize, (int)ssize, src);
}
+ memcpy(dst, src, ssize);
+ dst[ssize] = '\0';
+ return &dst[ssize];
}
-//========================================================
-// strlcat and strlcpy, from OpenBSD
-
-/*
- * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+/** Copies a string, like strlcpy() but with a better return: the number of bytes copied
+ * excluding the \0 terminator. Truncates and warns on overflow or unterminated source,
+ * whereas strlcpy() truncates silently and overreads (possibly segfaulting).
+ * Guarantees \0 termination.
+ * See also: dp_stpecpy() and dp_ustr2stp().
*/
-
-/* $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $ */
-/* $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $ */
-
-
-#ifndef HAVE_STRLCAT
-size_t
-strlcat(char *dst, const char *src, size_t siz)
+size_t dp__strlcpy(char *dst, const char *src, size_t dsize, const char *func, unsigned line)
{
- register char *d = dst;
- register const char *s = src;
- register size_t n = siz;
- size_t dlen;
-
- /* Find the end of dst and adjust bytes left but don't go past end */
- while (n-- != 0 && *d != '\0')
- d++;
- dlen = d - dst;
- n = siz - dlen;
-
- if (n == 0)
- return(dlen + strlen(s));
- while (*s != '\0') {
- if (n != 1) {
- *d++ = *s;
- n--;
- }
- s++;
- }
- *d = '\0';
+ char *p = (char *)memccpy(dst, src, '\0', dsize);
- return(dlen + (s - src)); /* count does not include NUL */
+ if (p)
+ return (p - 1) - dst;
+ dst[dsize - 1] = '\0';
+ Con_Printf(CON_WARN "%s:%u: src string unterminated or truncated to %zu bytes: \"%s\"\n", func, line, dsize - 1, dst);
+ return dsize - 1;
}
-#endif // #ifndef HAVE_STRLCAT
-
-#ifndef HAVE_STRLCPY
-size_t
-strlcpy(char *dst, const char *src, size_t siz)
+/** Catenates a string, like strlcat() but with a better return: the number of bytes copied
+ * excluding the \0 terminator. Truncates and warns on overflow or unterminated source,
+ * whereas strlcat() truncates silently and overreads (possibly segfaulting).
+ * Guarantees \0 termination.
+ * Inefficient like any strcat(), please use memcpy(), dp_stpecpy() or dp_strlcpy() instead.
+ */
+size_t dp__strlcat(char *dst, const char *src, size_t dsize, const char *func, unsigned line)
{
- register char *d = dst;
- register const char *s = src;
- register size_t n = siz;
-
- /* Copy as many bytes as will fit */
- if (n != 0 && --n != 0) {
- do {
- if ((*d++ = *s++) == 0)
- break;
- } while (--n != 0);
- }
+ char *p = (char *)memchr(dst, '\0', dsize) ?: dst;
+ size_t offset = p - dst;
- /* Not enough room in dst, add NUL and traverse rest of src */
- if (n == 0) {
- if (siz != 0)
- *d = '\0'; /* NUL-terminate dst */
- while (*s++)
- ;
- }
-
- return(s - src - 1); /* count does not include NUL */
+ return dp__strlcpy(p, src, dsize - offset, func, line) + offset;
}
-#endif // #ifndef HAVE_STRLCPY
+
void FindFraction(double val, int *num, int *denom, int denomMax)
{
while(COM_ParseToken_QuakeC(&in, false))
{
tokens[line] = lines[line];
- strlcpy(lines[line++], com_token, sizeof(lines[0]));
+ dp_strlcpy(lines[line++], com_token, sizeof(lines[0]));
if(!COM_ParseToken_QuakeC(&in, false))
return NULL;
if(!strcmp(com_token, "}"))