6739: Add templated `str_append` function for arrays with fixed size, add tests, ensure proper buffer size is used with DDNet server filter r=def- a=Robyt3


## Checklist

- [X] Tested the change ingame
- [ ] Provided screenshots if it is a visual change
- [ ] Tested in combination with possibly related configuration options
- [X] Written a unit test (especially base/) or added coverage to integration test
- [X] Considered possible null pointers and out of bounds array indexing
- [X] Changed no physics that affect existing maps
- [ ] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional)


Co-authored-by: Robert Müller <robytemueller@gmail.com>
This commit is contained in:
bors[bot] 2023-06-13 23:12:18 +00:00 committed by GitHub
commit 0d45120f5c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 162 additions and 112 deletions

View file

@ -3991,7 +3991,7 @@ int open_file(const char *path)
{
if(!fs_getcwd(workingDir, sizeof(workingDir)))
return 0;
str_append(workingDir, "/", sizeof(workingDir));
str_append(workingDir, "/");
}
else
workingDir[0] = '\0';

View file

@ -1194,6 +1194,23 @@ std::string windows_format_system_message(unsigned long error);
*/
void str_append(char *dst, const char *src, int dst_size);
/**
* Appends a string to a fixed-size array of chars.
*
* @ingroup Strings
*
* @param dst Array that shall receive the string.
* @param src String to append.
*
* @remark The strings are treated as zero-terminated strings.
* @remark Guarantees that dst string will contain zero-termination.
*/
template<int N>
void str_append(char (&dst)[N], const char *src)
{
str_append(dst, src, N);
}
/**
* Copies a string to another.
*
@ -1210,6 +1227,23 @@ void str_append(char *dst, const char *src, int dst_size);
*/
int str_copy(char *dst, const char *src, int dst_size);
/**
* Copies a string to a fixed-size array of chars.
*
* @ingroup Strings
*
* @param dst Array that shall receive the string.
* @param src String to be copied.
*
* @remark The strings are treated as zero-terminated strings.
* @remark Guarantees that dst string will contain zero-termination.
*/
template<int N>
void str_copy(char (&dst)[N], const char *src)
{
str_copy(dst, src, N);
}
/**
* Truncates a utf8 encoded string to a given length.
*
@ -2780,23 +2814,6 @@ bool shell_unregister_application(const char *executable, bool *updated);
void shell_update();
#endif
/**
* Copies a string to a fixed-size array of chars.
*
* @ingroup Strings
*
* @param dst Array that shall receive the string.
* @param src String to be copied.
*
* @remark The strings are treated as zero-terminated strings.
* @remark Guarantees that dst string will contain zero-termination.
*/
template<int N>
void str_copy(char (&dst)[N], const char *src)
{
str_copy(dst, src, N);
}
template<>
struct std::hash<NETADDR>
{

View file

@ -2165,7 +2165,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket, int Conn, bool Dummy)
if(g_Config.m_ClRunOnJoin[0])
{
str_format(aBuf, sizeof(aBuf), ";%s", g_Config.m_ClRunOnJoin);
str_append(aBufMsg, aBuf, sizeof(aBufMsg));
str_append(aBufMsg, aBuf);
}
if(g_Config.m_ClDummyDefaultEyes || g_Config.m_ClPlayerDefaultEyes)
{
@ -2195,7 +2195,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket, int Conn, bool Dummy)
if(aBufEmote[0])
{
str_format(aBuf, sizeof(aBuf), ";%s", aBufEmote);
str_append(aBufMsg, aBuf, sizeof(aBufMsg));
str_append(aBufMsg, aBuf);
}
}
MsgP.m_pMessage = aBufMsg;
@ -4892,8 +4892,8 @@ void CClient::RequestDDNetInfo()
{
char aEscaped[128];
EscapeUrl(aEscaped, sizeof(aEscaped), PlayerName());
str_append(aUrl, "?name=", sizeof(aUrl));
str_append(aUrl, aEscaped, sizeof(aUrl));
str_append(aUrl, "?name=");
str_append(aUrl, aEscaped);
}
// Use ipv4 so we can know the ingame ip addresses of players before they join game servers

View file

@ -168,13 +168,13 @@ void CFriends::ConfigSaveCallback(IConfigManager *pConfigManager, void *pUserDat
{
str_copy(aBuf, pSelf->m_Foes ? "add_foe " : "add_friend ");
str_append(aBuf, "\"", sizeof(aBuf));
str_append(aBuf, "\"");
char *pDst = aBuf + str_length(aBuf);
str_escape(&pDst, pSelf->m_aFriends[i].m_aName, pEnd);
str_append(aBuf, "\" \"", sizeof(aBuf));
str_append(aBuf, "\" \"");
pDst = aBuf + str_length(aBuf);
str_escape(&pDst, pSelf->m_aFriends[i].m_aClan, pEnd);
str_append(aBuf, "\"", sizeof(aBuf));
str_append(aBuf, "\"");
pConfigManager->WriteLine(aBuf);
}

View file

@ -672,13 +672,13 @@ int CGraphics_Threaded::LoadPNG(CImageInfo *pImg, const char *pFilename, int Sto
{
if(!First)
{
str_append(Warning.m_aWarningMsg, ", ", sizeof(Warning.m_aWarningMsg));
str_append(Warning.m_aWarningMsg, ", ");
}
str_append(Warning.m_aWarningMsg, EXPLANATION[i], sizeof(Warning.m_aWarningMsg));
str_append(Warning.m_aWarningMsg, EXPLANATION[i]);
First = false;
}
}
str_append(Warning.m_aWarningMsg, " unsupported", sizeof(Warning.m_aWarningMsg));
str_append(Warning.m_aWarningMsg, " unsupported");
m_vWarnings.emplace_back(Warning);
}
}

View file

@ -1415,17 +1415,17 @@ int CServerBrowser::LoadingProgression() const
return 100.0f * Loaded / Servers;
}
void CServerBrowser::DDNetFilterAdd(char *pFilter, const char *pName)
void CServerBrowser::DDNetFilterAdd(char *pFilter, int FilterSize, const char *pName) const
{
if(DDNetFiltered(pFilter, pName))
return;
char aBuf[128];
str_format(aBuf, sizeof(aBuf), ",%s", pName);
str_append(pFilter, aBuf, 128);
str_append(pFilter, aBuf, FilterSize);
}
void CServerBrowser::DDNetFilterRem(char *pFilter, const char *pName)
void CServerBrowser::DDNetFilterRem(char *pFilter, int FilterSize, const char *pName) const
{
if(!DDNetFiltered(pFilter, pName))
return;
@ -1443,12 +1443,12 @@ void CServerBrowser::DDNetFilterRem(char *pFilter, const char *pName)
{
char aBuf2[128];
str_format(aBuf2, sizeof(aBuf2), ",%s", aToken);
str_append(pFilter, aBuf2, 128);
str_append(pFilter, aBuf2, FilterSize);
}
}
}
bool CServerBrowser::DDNetFiltered(char *pFilter, const char *pName)
bool CServerBrowser::DDNetFiltered(const char *pFilter, const char *pName) const
{
return str_in_list(pFilter, ",", pName); // country not excluded
}
@ -1468,7 +1468,7 @@ void CServerBrowser::CountryFilterClean(int Network)
{
char aBuf[128];
str_format(aBuf, sizeof(aBuf), ",%s", pName);
str_append(aNewList, aBuf, sizeof(aNewList));
str_append(aNewList, aBuf);
}
}
}
@ -1489,7 +1489,7 @@ void CServerBrowser::TypeFilterClean(int Network)
{
char aBuf[128];
str_format(aBuf, sizeof(aBuf), ",%s", pName);
str_append(aNewList, aBuf, sizeof(aNewList));
str_append(aNewList, aBuf);
}
}

View file

@ -114,9 +114,9 @@ public:
int NumTypes(int Network) override { return m_aNetworks[Network].m_NumTypes; }
const char *GetType(int Network, int Index) override { return m_aNetworks[Network].m_aTypes[Index]; }
void DDNetFilterAdd(char *pFilter, const char *pName) override;
void DDNetFilterRem(char *pFilter, const char *pName) override;
bool DDNetFiltered(char *pFilter, const char *pName) override;
void DDNetFilterAdd(char *pFilter, int FilterSize, const char *pName) const override;
void DDNetFilterRem(char *pFilter, int FilterSize, const char *pName) const override;
bool DDNetFiltered(const char *pFilter, const char *pName) const override;
void CountryFilterClean(int Network) override;
void TypeFilterClean(int Network) override;

View file

@ -1031,7 +1031,7 @@ public:
void AppendTextContainer(STextContainerIndex TextContainerIndex, CTextCursor *pCursor, const char *pText, int Length = -1) override
{
STextContainer &TextContainer = GetTextContainer(TextContainerIndex);
str_append(TextContainer.m_aDebugText, pText, sizeof(TextContainer.m_aDebugText));
str_append(TextContainer.m_aDebugText, pText);
// calculate the font size of the displayed glyphs
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;

View file

@ -315,7 +315,7 @@ void CUpdater::PerformUpdate()
char aBuf[512];
str_copy(aBuf, pFile, sizeof(aBuf)); // SDL
str_copy(aBuf + len - 4, "-" PLAT_NAME, sizeof(aBuf) - len + 4); // -win32
str_append(aBuf, pFile + len - 4, sizeof(aBuf)); // .dll
str_append(aBuf, pFile + len - 4); // .dll
FetchFile(aBuf, pFile);
#endif
// Ignore DLL downloads on other platforms
@ -326,7 +326,7 @@ void CUpdater::PerformUpdate()
char aBuf[512];
str_copy(aBuf, pFile, sizeof(aBuf)); // libsteam_api
str_copy(aBuf + len - 3, "-" PLAT_NAME, sizeof(aBuf) - len + 3); // -linux-x86_64
str_append(aBuf, pFile + len - 3, sizeof(aBuf)); // .so
str_append(aBuf, pFile + len - 3); // .so
FetchFile(aBuf, pFile);
#endif
// Ignore DLL downloads on other platforms, on Linux we statically link anyway

View file

@ -1293,11 +1293,11 @@ void CServer::SendRconLogLine(int ClientID, const CLogMessage *pMessage)
{
str_append(aLine, pLine, pStart - pLine + 1);
str_append(aLine, pStart + 2, pStart - pLine + pEnd - pStart - 1);
str_append(aLine, pEnd + 2, sizeof(aLine));
str_append(aLine, pEnd + 2);
str_append(aLineWithoutIps, pLine, pStart - pLine + 1);
str_append(aLineWithoutIps, "XXX", sizeof(aLineWithoutIps));
str_append(aLineWithoutIps, pEnd + 2, sizeof(aLineWithoutIps));
str_append(aLineWithoutIps, "XXX");
str_append(aLineWithoutIps, pEnd + 2);
pLine = aLine;
pLineWithoutIps = aLineWithoutIps;
@ -2319,12 +2319,12 @@ void CServer::UpdateRegisterServerInfo()
m_aClients[i].m_Score.value_or(-9999),
JsonBool(GameServer()->IsClientPlayer(i)),
aExtraPlayerInfo);
str_append(aInfo, aClientInfo, sizeof(aInfo));
str_append(aInfo, aClientInfo);
FirstPlayer = false;
}
}
str_append(aInfo, "]}", sizeof(aInfo));
str_append(aInfo, "]}");
m_pRegister->OnNewInfo(aInfo);
}

View file

@ -165,9 +165,9 @@ public:
virtual int NumTypes(int Network) = 0;
virtual const char *GetType(int Network, int Index) = 0;
virtual void DDNetFilterAdd(char *pFilter, const char *pName) = 0;
virtual void DDNetFilterRem(char *pFilter, const char *pName) = 0;
virtual bool DDNetFiltered(char *pFilter, const char *pName) = 0;
virtual void DDNetFilterAdd(char *pFilter, int FilterSize, const char *pName) const = 0;
virtual void DDNetFilterRem(char *pFilter, int FilterSize, const char *pName) const = 0;
virtual bool DDNetFiltered(const char *pFilter, const char *pName) const = 0;
virtual void CountryFilterClean(int Network) = 0;
virtual void TypeFilterClean(int Network) = 0;
virtual int GetCurrentType() = 0;

View file

@ -732,9 +732,9 @@ void CConsole::ConCommandStatus(IResult *pResult, void *pUser)
if(Used > 0)
{
Used += 2;
str_append(aBuf, ", ", sizeof(aBuf));
str_append(aBuf, ", ");
}
str_append(aBuf, pCommand->m_pName, sizeof(aBuf));
str_append(aBuf, pCommand->m_pName);
Used += Length;
}
else
@ -927,7 +927,7 @@ void CConsole::ConToggle(IConsole::IResult *pResult, void *pUser)
str_format(aBuf, sizeof(aBuf), "%s \"", pResult->GetString(0));
char *pDst = aBuf + str_length(aBuf);
str_escape(&pDst, pStr, aBuf + sizeof(aBuf));
str_append(aBuf, "\"", sizeof(aBuf));
str_append(aBuf, "\"");
pConsole->ExecuteLine(aBuf);
aBuf[0] = 0;
}

View file

@ -92,7 +92,7 @@ void CFifo::Init(IConsole *pConsole, char *pFifoFile, int Flag)
}
str_copy(m_aFilename, "\\\\.\\pipe\\");
str_append(m_aFilename, pFifoFile, sizeof(m_aFilename));
str_append(m_aFilename, pFifoFile);
m_Flag = Flag;
const std::wstring WideFilename = windows_utf8_to_wide(m_aFilename);

View file

@ -112,7 +112,7 @@ public:
{
char aBuffer[IO_MAX_PATH_LENGTH];
str_copy(aBuffer, pArgv0, Pos + 1);
str_append(aBuffer, "/storage.cfg", sizeof(aBuffer));
str_append(aBuffer, "/storage.cfg");
File = io_open(aBuffer, IOFLAG_READ | IOFLAG_SKIP_BOM);
}
@ -294,7 +294,7 @@ public:
return;
}
#if defined(CONF_PLATFORM_MACOS)
str_append(m_aBinarydir, "/../../../DDNet-Server.app/Contents/MacOS", sizeof(m_aBinarydir));
str_append(m_aBinarydir, "/../../../DDNet-Server.app/Contents/MacOS");
str_format(aBuf, sizeof(aBuf), "%s/" PLAT_SERVER_EXEC, m_aBinarydir);
if(fs_is_file(aBuf))
{

View file

@ -419,8 +419,8 @@ const char *CBinds::GetKeyBindModifiersName(int ModifierCombination)
{
if(ModifierCombination & (1 << k))
{
str_append(aModifier, GetModifierName(k), sizeof(aModifier));
str_append(aModifier, "+", sizeof(aModifier));
str_append(aModifier, GetModifierName(k));
str_append(aModifier, "+");
}
}
return aModifier;

View file

@ -299,17 +299,17 @@ bool CChat::OnInput(const IInput::CEvent &Event)
str_truncate(aBuf, sizeof(aBuf), m_Input.GetString(), m_PlaceholderOffset);
// add the command
str_append(aBuf, "/", sizeof(aBuf));
str_append(aBuf, pCompletionCommand->m_pName, sizeof(aBuf));
str_append(aBuf, "/");
str_append(aBuf, pCompletionCommand->m_pName);
// add separator
const char *pSeparator = pCompletionCommand->m_pParams[0] == '\0' ? "" : " ";
str_append(aBuf, pSeparator, sizeof(aBuf));
str_append(aBuf, pSeparator);
if(*pSeparator)
str_append(aBuf, pSeparator, sizeof(aBuf));
str_append(aBuf, pSeparator);
// add part after the name
str_append(aBuf, m_Input.GetString() + m_PlaceholderOffset + m_PlaceholderLength, sizeof(aBuf));
str_append(aBuf, m_Input.GetString() + m_PlaceholderOffset + m_PlaceholderLength);
m_PlaceholderLength = str_length(pSeparator) + str_length(pCompletionCommand->m_pName) + 1;
m_Input.Set(aBuf);
@ -360,7 +360,7 @@ bool CChat::OnInput(const IInput::CEvent &Event)
str_truncate(aBuf, sizeof(aBuf), m_Input.GetString(), m_PlaceholderOffset);
// add the name
str_append(aBuf, pCompletionString, sizeof(aBuf));
str_append(aBuf, pCompletionString);
// add separator
const char *pSeparator = "";
@ -369,10 +369,10 @@ bool CChat::OnInput(const IInput::CEvent &Event)
else if(m_PlaceholderOffset == 0)
pSeparator = ":";
if(*pSeparator)
str_append(aBuf, pSeparator, sizeof(aBuf));
str_append(aBuf, pSeparator);
// add part after the name
str_append(aBuf, m_Input.GetString() + m_PlaceholderOffset + m_PlaceholderLength, sizeof(aBuf));
str_append(aBuf, m_Input.GetString() + m_PlaceholderOffset + m_PlaceholderLength);
m_PlaceholderLength = str_length(pSeparator) + str_length(pCompletionString);
m_Input.Set(aBuf);
@ -883,7 +883,7 @@ void CChat::OnPrepareLines()
str_format(aName, sizeof(aName), "%d: ", m_aLines[r].m_ClientID);
}
str_append(aName, m_aLines[r].m_aName, sizeof(aName));
str_append(aName, m_aLines[r].m_aName);
char aCount[12];
if(m_aLines[r].m_ClientID < 0)

View file

@ -188,10 +188,10 @@ void CGameConsole::CInstance::PossibleArgumentsCompleteCallback(int Index, const
// get command
char aBuf[512];
StrCopyUntilSpace(aBuf, sizeof(aBuf), pInstance->GetString());
str_append(aBuf, " ", sizeof(aBuf));
str_append(aBuf, " ");
// append argument
str_append(aBuf, pStr, sizeof(aBuf));
str_append(aBuf, pStr);
pInstance->m_Input.Set(aBuf);
}
}

View file

@ -1522,7 +1522,7 @@ int CMenus::Render()
char aBufNew[IO_MAX_PATH_LENGTH];
str_format(aBufNew, sizeof(aBufNew), "%s/%s", m_aCurrentDemoFolder, m_DemoRenameInput.GetString());
if(!str_endswith(aBufNew, ".demo"))
str_append(aBufNew, ".demo", sizeof(aBufNew));
str_append(aBufNew, ".demo");
if(Storage()->FileExists(aBufNew, m_vDemos[m_DemolistSelectedIndex].m_StorageType))
{

View file

@ -762,6 +762,7 @@ void CMenus::RenderServerbrowserFilters(CUIRect View)
if(s_ActivePage == 1)
{
char *pFilterExcludeTypes = Network == IServerBrowser::NETWORK_DDNET ? g_Config.m_BrFilterExcludeTypes : g_Config.m_BrFilterExcludeTypesKoG;
const int FilterExcludeTypesSize = Network == IServerBrowser::NETWORK_DDNET ? sizeof(g_Config.m_BrFilterExcludeTypes) : sizeof(g_Config.m_BrFilterExcludeTypesKoG);
int MaxTypes = ServerBrowser()->NumTypes(Network);
int NumTypes = ServerBrowser()->NumTypes(Network);
int PerLine = 3;
@ -811,7 +812,7 @@ void CMenus::RenderServerbrowserFilters(CUIRect View)
for(int j = 0; j < MaxTypes; ++j)
{
if(j != TypeIndex)
ServerBrowser()->DDNetFilterAdd(pFilterExcludeTypes, ServerBrowser()->GetType(Network, j));
ServerBrowser()->DDNetFilterAdd(pFilterExcludeTypes, FilterExcludeTypesSize, ServerBrowser()->GetType(Network, j));
}
}
else
@ -832,11 +833,11 @@ void CMenus::RenderServerbrowserFilters(CUIRect View)
}
else if(Active)
{
ServerBrowser()->DDNetFilterAdd(pFilterExcludeTypes, pName);
ServerBrowser()->DDNetFilterAdd(pFilterExcludeTypes, FilterExcludeTypesSize, pName);
}
else
{
ServerBrowser()->DDNetFilterRem(pFilterExcludeTypes, pName);
ServerBrowser()->DDNetFilterRem(pFilterExcludeTypes, FilterExcludeTypesSize, pName);
}
}
@ -858,6 +859,7 @@ void CMenus::RenderServerbrowserFilters(CUIRect View)
else
{
char *pFilterExcludeCountries = Network == IServerBrowser::NETWORK_DDNET ? g_Config.m_BrFilterExcludeCountries : g_Config.m_BrFilterExcludeCountriesKoG;
const int FilterExcludeCountriesSize = Network == IServerBrowser::NETWORK_DDNET ? sizeof(g_Config.m_BrFilterExcludeCountries) : sizeof(g_Config.m_BrFilterExcludeCountriesKoG);
ServerFilter.HSplitTop(15.0f, &ServerFilter, &ServerFilter);
const float FlagWidth = 40.0f;
@ -907,7 +909,7 @@ void CMenus::RenderServerbrowserFilters(CUIRect View)
for(int j = 0; j < MaxFlags; ++j)
{
if(j != CountryIndex)
ServerBrowser()->DDNetFilterAdd(pFilterExcludeCountries, ServerBrowser()->GetCountryName(Network, j));
ServerBrowser()->DDNetFilterAdd(pFilterExcludeCountries, FilterExcludeCountriesSize, ServerBrowser()->GetCountryName(Network, j));
}
}
else
@ -928,11 +930,11 @@ void CMenus::RenderServerbrowserFilters(CUIRect View)
}
else if(Active)
{
ServerBrowser()->DDNetFilterAdd(pFilterExcludeCountries, pName);
ServerBrowser()->DDNetFilterAdd(pFilterExcludeCountries, FilterExcludeCountriesSize, pName);
}
else
{
ServerBrowser()->DDNetFilterRem(pFilterExcludeCountries, pName);
ServerBrowser()->DDNetFilterRem(pFilterExcludeCountries, FilterExcludeCountriesSize, pName);
}
}

View file

@ -676,7 +676,7 @@ void CMenus::RenderDemoPlayerSliceSavePopup(CUIRect MainView)
{
char aDemoName[IO_MAX_PATH_LENGTH];
DemoPlayer()->GetDemoName(aDemoName, sizeof(aDemoName));
str_append(aDemoName, ".demo", sizeof(aDemoName));
str_append(aDemoName, ".demo");
if(!str_endswith(m_DemoSliceInput.GetString(), ".demo"))
m_DemoSliceInput.Append(".demo");
@ -1160,8 +1160,8 @@ void CMenus::RenderDemoList(CUIRect MainView)
fs_parent_dir(m_aCurrentDemoFolder);
else // sub folder
{
str_append(m_aCurrentDemoFolder, "/", sizeof(m_aCurrentDemoFolder));
str_append(m_aCurrentDemoFolder, m_vDemos[m_DemolistSelectedIndex].m_aFilename, sizeof(m_aCurrentDemoFolder));
str_append(m_aCurrentDemoFolder, "/");
str_append(m_aCurrentDemoFolder, m_vDemos[m_DemolistSelectedIndex].m_aFilename);
m_DemolistStorageType = m_vDemos[m_DemolistSelectedIndex].m_StorageType;
}
DemolistPopulate();

View file

@ -191,7 +191,7 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch
while(TextRender()->TextWidth(TitleFontsize, aBuf, -1, -1.0f) > TitleWidth)
aBuf[str_length(aBuf) - 1] = '\0';
if(str_comp(aBuf, Client()->GetCurrentMap()))
str_append(aBuf, "", sizeof(aBuf));
str_append(aBuf, "");
pTitle = aBuf;
}
}
@ -531,25 +531,25 @@ void CScoreboard::RenderRecordingNotification(float x)
{
str_time((int64_t)m_pClient->DemoRecorder(RECORDER_MANUAL)->Length() * 100, TIME_HOURS, aTime, sizeof(aTime));
str_format(aBuf2, sizeof(aBuf2), "%s %s ", Localize("Manual"), aTime);
str_append(aBuf, aBuf2, sizeof(aBuf));
str_append(aBuf, aBuf2);
}
if(m_pClient->DemoRecorder(RECORDER_RACE)->IsRecording())
{
str_time((int64_t)m_pClient->DemoRecorder(RECORDER_RACE)->Length() * 100, TIME_HOURS, aTime, sizeof(aTime));
str_format(aBuf2, sizeof(aBuf2), "%s %s ", Localize("Race"), aTime);
str_append(aBuf, aBuf2, sizeof(aBuf));
str_append(aBuf, aBuf2);
}
if(m_pClient->DemoRecorder(RECORDER_AUTO)->IsRecording())
{
str_time((int64_t)m_pClient->DemoRecorder(RECORDER_AUTO)->Length() * 100, TIME_HOURS, aTime, sizeof(aTime));
str_format(aBuf2, sizeof(aBuf2), "%s %s ", Localize("Auto"), aTime);
str_append(aBuf, aBuf2, sizeof(aBuf));
str_append(aBuf, aBuf2);
}
if(m_pClient->DemoRecorder(RECORDER_REPLAYS)->IsRecording())
{
str_time((int64_t)m_pClient->DemoRecorder(RECORDER_REPLAYS)->Length() * 100, TIME_HOURS, aTime, sizeof(aTime));
str_format(aBuf2, sizeof(aBuf2), "%s %s ", Localize("Replay"), aTime);
str_append(aBuf, aBuf2, sizeof(aBuf));
str_append(aBuf, aBuf2);
}
if(!aBuf[0])

View file

@ -518,7 +518,7 @@ void CStatboard::FormatStats(char *pDest, size_t DestSize)
pStats->m_FlagGrabs, // Flag grabs
pStats->m_FlagCaptures); // Flag captures
str_append(aPlayerStats, aBuf, sizeof(aPlayerStats));
str_append(aPlayerStats, aBuf);
}
str_format(pDest, DestSize, "%s\n\n%s", aServerStats, aPlayerStats);

View file

@ -78,10 +78,10 @@ void CVoting::CallvoteOption(int OptionID, const char *pReason, bool ForceVote)
str_copy(aBuf, "force_vote option \"");
char *pDst = aBuf + str_length(aBuf);
str_escape(&pDst, pOption->m_aDescription, aBuf + sizeof(aBuf));
str_append(aBuf, "\" \"", sizeof(aBuf));
str_append(aBuf, "\" \"");
pDst = aBuf + str_length(aBuf);
str_escape(&pDst, pReason, aBuf + sizeof(aBuf));
str_append(aBuf, "\"", sizeof(aBuf));
str_append(aBuf, "\"");
Client()->Rcon(aBuf);
}
else
@ -105,7 +105,7 @@ void CVoting::RemovevoteOption(int OptionID)
str_copy(aBuf, "remove_vote \"");
char *pDst = aBuf + str_length(aBuf);
str_escape(&pDst, pOption->m_aDescription, aBuf + sizeof(aBuf));
str_append(aBuf, "\"", sizeof(aBuf));
str_append(aBuf, "\"");
Client()->Rcon(aBuf);
break;
}
@ -121,10 +121,10 @@ void CVoting::AddvoteOption(const char *pDescription, const char *pCommand)
str_copy(aBuf, "add_vote \"");
char *pDst = aBuf + str_length(aBuf);
str_escape(&pDst, pDescription, aBuf + sizeof(aBuf));
str_append(aBuf, "\" \"", sizeof(aBuf));
str_append(aBuf, "\" \"");
pDst = aBuf + str_length(aBuf);
str_escape(&pDst, pCommand, aBuf + sizeof(aBuf));
str_append(aBuf, "\"", sizeof(aBuf));
str_append(aBuf, "\"");
Client()->Rcon(aBuf);
}

View file

@ -4440,16 +4440,16 @@ void CGameContext::OnUpdatePlayerServerInfo(char *aBuf, int BufSize, int ID)
apPartNames[i],
EscapeJson(aCSkinName, sizeof(aCSkinName), TeeInfo.m_apSkinPartNames[i]));
str_append(aJsonSkin, aPartBuf, sizeof(aJsonSkin));
str_append(aJsonSkin, aPartBuf);
if(TeeInfo.m_aUseCustomColors[i])
{
str_format(aPartBuf, sizeof(aPartBuf),
",\"color\":%d",
TeeInfo.m_aSkinPartColors[i]);
str_append(aJsonSkin, aPartBuf, sizeof(aJsonSkin));
str_append(aJsonSkin, aPartBuf);
}
str_append(aJsonSkin, "}", sizeof(aJsonSkin));
str_append(aJsonSkin, "}");
}
}

View file

@ -581,7 +581,7 @@ char *CSaveTeam::GetString()
{
char aBuf[1024];
str_format(aBuf, sizeof(aBuf), "\n%s", m_pSavedTees[i].GetString(this));
str_append(m_aString, aBuf, sizeof(m_aString));
str_append(m_aString, aBuf);
}
if(m_pSwitchers && m_HighestSwitchNumber)
@ -590,7 +590,7 @@ char *CSaveTeam::GetString()
{
char aBuf[64];
str_format(aBuf, sizeof(aBuf), "\n%d\t%d\t%d", m_pSwitchers[i].m_Status, m_pSwitchers[i].m_EndTime, m_pSwitchers[i].m_Type);
str_append(m_aString, aBuf, sizeof(m_aString));
str_append(m_aString, aBuf);
}
}

View file

@ -215,7 +215,7 @@ bool CScoreWorker::MapVote(IDbConnection *pSqlServer, const ISqlData *pGameData,
char aMapPrefix[128];
str_copy(aMapPrefix, pData->m_aName, sizeof(aMapPrefix));
str_append(aMapPrefix, "%", sizeof(aMapPrefix));
str_append(aMapPrefix, "%");
char aBuf[768];
str_format(aBuf, sizeof(aBuf),
@ -275,7 +275,7 @@ bool CScoreWorker::MapInfo(IDbConnection *pSqlServer, const ISqlData *pGameData,
char aMapPrefix[128];
str_copy(aMapPrefix, pData->m_aName, sizeof(aMapPrefix));
str_append(aMapPrefix, "%", sizeof(aMapPrefix));
str_append(aMapPrefix, "%");
char aCurrentTimestamp[512];
pSqlServer->ToUnixTimestamp("CURRENT_TIMESTAMP", aCurrentTimestamp, sizeof(aCurrentTimestamp));
@ -876,12 +876,12 @@ bool CScoreWorker::ShowTeamRank(IDbConnection *pSqlServer, const ISqlData *pGame
char aFormattedNames[512] = "";
for(unsigned int Name = 0; Name < Teamrank.m_NumNames; Name++)
{
str_append(aFormattedNames, Teamrank.m_aaNames[Name], sizeof(aFormattedNames));
str_append(aFormattedNames, Teamrank.m_aaNames[Name]);
if(Name < Teamrank.m_NumNames - 2)
str_append(aFormattedNames, ", ", sizeof(aFormattedNames));
str_append(aFormattedNames, ", ");
else if(Name < Teamrank.m_NumNames - 1)
str_append(aFormattedNames, " & ", sizeof(aFormattedNames));
str_append(aFormattedNames, " & ");
}
if(g_Config.m_SvHideScore)
@ -1051,11 +1051,11 @@ bool CScoreWorker::ShowTeamTop5(IDbConnection *pSqlServer, const ISqlData *pGame
{
char aName[MAX_NAME_LENGTH];
pSqlServer->GetString(1, aName, sizeof(aName));
str_append(aNames, aName, sizeof(aNames));
str_append(aNames, aName);
if(i < TeamSize - 2)
str_append(aNames, ", ", sizeof(aNames));
str_append(aNames, ", ");
else if(i == TeamSize - 2)
str_append(aNames, " & ", sizeof(aNames));
str_append(aNames, " & ");
if(pSqlServer->Step(&Last, pError, ErrorSize))
{
return true;
@ -1143,12 +1143,12 @@ bool CScoreWorker::ShowPlayerTeamTop5(IDbConnection *pSqlServer, const ISqlData
char aFormattedNames[512] = "";
for(unsigned int Name = 0; Name < Teamrank.m_NumNames; Name++)
{
str_append(aFormattedNames, Teamrank.m_aaNames[Name], sizeof(aFormattedNames));
str_append(aFormattedNames, Teamrank.m_aaNames[Name]);
if(Name < Teamrank.m_NumNames - 2)
str_append(aFormattedNames, ", ", sizeof(aFormattedNames));
str_append(aFormattedNames, ", ");
else if(Name < Teamrank.m_NumNames - 1)
str_append(aFormattedNames, " & ", sizeof(aFormattedNames));
str_append(aFormattedNames, " & ");
}
str_format(paMessages[Line], sizeof(paMessages[Line]), "%d. %s Team Time: %s",
@ -1776,11 +1776,11 @@ bool CScoreWorker::GetSaves(IDbConnection *pSqlServer, const ISqlData *pGameData
auto *paMessages = pResult->m_Data.m_aaMessages;
char aSaveLike[128] = "";
str_append(aSaveLike, "%\n", sizeof(aSaveLike));
str_append(aSaveLike, "%\n");
sqlstr::EscapeLike(aSaveLike + str_length(aSaveLike),
pData->m_aRequestingPlayer,
sizeof(aSaveLike) - str_length(aSaveLike));
str_append(aSaveLike, "\t%", sizeof(aSaveLike));
str_append(aSaveLike, "\t%");
char aCurrentTimestamp[512];
pSqlServer->ToUnixTimestamp("CURRENT_TIMESTAMP", aCurrentTimestamp, sizeof(aCurrentTimestamp));

View file

@ -161,9 +161,9 @@ void CGameTeams::OnCharacterStart(int ClientID)
if(First)
First = false;
else
str_append(aBuf, ", ", sizeof(aBuf));
str_append(aBuf, ", ");
str_append(aBuf, GameServer()->Server()->ClientName(i), sizeof(aBuf));
str_append(aBuf, GameServer()->Server()->ClientName(i));
}
}
}
@ -270,9 +270,9 @@ void CGameTeams::Tick()
{
if(aPlayerNames[0])
{
str_append(aPlayerNames, ", ", sizeof(aPlayerNames));
str_append(aPlayerNames, ", ");
}
str_append(aPlayerNames, Server()->ClientName(j), sizeof(aPlayerNames));
str_append(aPlayerNames, Server()->ClientName(j));
NumPlayersNotStarted += 1;
}
}

View file

@ -586,6 +586,37 @@ TEST(Str, Copy)
EXPECT_STREQ(aBuf, "DDNet最好了");
str_copy(aBuf, pStr, 16);
EXPECT_STREQ(aBuf, "DDNet最好了");
str_copy(aBuf, pStr);
EXPECT_STREQ(aBuf, "DDNet最好了");
}
TEST(Str, Append)
{
char aBuf[64];
aBuf[0] = '\0';
str_append(aBuf, "DDNet最好了", 7);
EXPECT_STREQ(aBuf, "DDNet");
str_append(aBuf, "", 8);
EXPECT_STREQ(aBuf, "DDNet");
str_append(aBuf, "", 9);
EXPECT_STREQ(aBuf, "DDNet最");
str_append(aBuf, "", 10);
EXPECT_STREQ(aBuf, "DDNet最");
str_append(aBuf, "", 11);
EXPECT_STREQ(aBuf, "DDNet最");
str_append(aBuf, "", 12);
EXPECT_STREQ(aBuf, "DDNet最好");
str_append(aBuf, "", 13);
EXPECT_STREQ(aBuf, "DDNet最好");
str_append(aBuf, "", 14);
EXPECT_STREQ(aBuf, "DDNet最好");
str_append(aBuf, "", 15);
EXPECT_STREQ(aBuf, "DDNet最好了");
str_append(aBuf, "", 16);
EXPECT_STREQ(aBuf, "DDNet最好了");
aBuf[0] = '\0';
str_append(aBuf, "DDNet最好了");
EXPECT_STREQ(aBuf, "DDNet最好了");
}
TEST(Str, Utf8Stats)

View file

@ -27,7 +27,7 @@ inline void ProcessItem(const char *pItemName, IStorage *pStorage)
str_copy(aConfig, pItemName, sizeof(aConfig));
aConfig[Len - sizeof(".map")] = 0;
str_append(aConfig, ".cfg", sizeof(aConfig));
str_append(aConfig, ".cfg");
dbg_msg("config_common", "processing '%s'", pItemName);
Process(pStorage, pItemName, aConfig);
}