1223: Add `str_startswith` and `str_endswith` r=Learath2 a=heinrich5991

Add tests, use these functions in the code base.

Co-authored-by: heinrich5991 <heinrich5991@gmail.com>
This commit is contained in:
bors[bot] 2018-07-26 12:19:37 +00:00
commit 484e0d1703
16 changed files with 174 additions and 73 deletions

View file

@ -2392,6 +2392,39 @@ int str_comp_filenames(const char *a, const char *b)
return *a - *b;
}
const char *str_startswith(const char *str, const char *prefix)
{
int prefixl = str_length(prefix);
if(str_comp_num(str, prefix, prefixl) == 0)
{
return str + prefixl;
}
else
{
return 0;
}
}
const char *str_endswith(const char *str, const char *suffix)
{
int strl = str_length(str);
int suffixl = str_length(suffix);
const char *strsuffix;
if(strl < suffixl)
{
return 0;
}
strsuffix = str + strl - suffixl;
if(str_comp(strsuffix, suffix) == 0)
{
return strsuffix;
}
else
{
return 0;
}
}
static int min3(int a, int b, int c)
{
int min = a;

View file

@ -943,7 +943,7 @@ void net_unix_close(UNIXSOCKET sock);
dst_size - Size of the buffer of the dst string.
Remarks:
- The strings are treated as zero-termineted strings.
- The strings are treated as zero-terminated strings.
- Guarantees that dst string will contain zero-termination.
*/
void str_append(char *dst, const char *src, int dst_size);
@ -958,7 +958,7 @@ void str_append(char *dst, const char *src, int dst_size);
dst_size - Size of the buffer dst.
Remarks:
- The strings are treated as zero-termineted strings.
- The strings are treated as zero-terminated strings.
- Guarantees that dst string will contain zero-termination.
*/
void str_copy(char *dst, const char *src, int dst_size);
@ -990,7 +990,7 @@ int str_length(const char *str);
Remarks:
- See the C manual for syntax for the printf formatting string.
- The strings are treated as zero-termineted strings.
- The strings are treated as zero-terminated strings.
- Guarantees that dst string will contain zero-termination.
*/
int str_format(char *buffer, int buffer_size, const char *format, ...)
@ -1008,7 +1008,7 @@ GNUC_ATTRIBUTE((format(printf, 3, 4)));
Trimmed string
Remarks:
- The strings are treated as zero-termineted strings.
- The strings are treated as zero-terminated strings.
*/
char *str_trim_words(char *str, int words);
@ -1020,7 +1020,7 @@ char *str_trim_words(char *str, int words);
str - String to sanitize.
Remarks:
- The strings are treated as zero-termineted strings.
- The strings are treated as zero-terminated strings.
*/
void str_sanitize_strong(char *str);
@ -1032,7 +1032,7 @@ void str_sanitize_strong(char *str);
str - String to sanitize.
Remarks:
- The strings are treated as zero-termineted strings.
- The strings are treated as zero-terminated strings.
*/
void str_sanitize_cc(char *str);
@ -1045,7 +1045,7 @@ void str_sanitize_cc(char *str);
str - String to sanitize.
Remarks:
- The strings are treated as zero-termineted strings.
- The strings are treated as zero-terminated strings.
*/
void str_sanitize(char *str);
@ -1057,7 +1057,7 @@ void str_sanitize(char *str);
str - String to sanitize.
Remarks:
- The strings are treated as zero-termineted strings.
- The strings are treated as zero-terminated strings.
*/
void str_sanitize_filename(char *str);
@ -1073,7 +1073,7 @@ void str_sanitize_filename(char *str);
within the string.
Remarks:
- The strings are treated as zero-termineted strings.
- The strings are treated as zero-terminated strings.
*/
char *str_skip_to_whitespace(char *str);
@ -1089,7 +1089,7 @@ char *str_skip_to_whitespace(char *str);
within the string.
Remarks:
- The strings are treated as zero-termineted strings.
- The strings are treated as zero-terminated strings.
*/
char *str_skip_whitespaces(char *str);
@ -1108,7 +1108,7 @@ char *str_skip_whitespaces(char *str);
Remarks:
- Only garanted to work with a-z/A-Z.
- The strings are treated as zero-termineted strings.
- The strings are treated as zero-terminated strings.
*/
int str_comp_nocase(const char *a, const char *b);
@ -1128,7 +1128,7 @@ int str_comp_nocase(const char *a, const char *b);
Remarks:
- Only garanted to work with a-z/A-Z.
- The strings are treated as zero-termineted strings.
- The strings are treated as zero-terminated strings.
*/
int str_comp_nocase_num(const char *a, const char *b, const int num);
@ -1146,7 +1146,7 @@ int str_comp_nocase_num(const char *a, const char *b, const int num);
>0 - String a is greater then string b
Remarks:
- The strings are treated as zero-termineted strings.
- The strings are treated as zero-terminated strings.
*/
int str_comp(const char *a, const char *b);
@ -1165,7 +1165,7 @@ int str_comp(const char *a, const char *b);
>0 - String a is greater then string b
Remarks:
- The strings are treated as zero-termineted strings.
- The strings are treated as zero-terminated strings.
*/
int str_comp_num(const char *a, const char *b, const int num);
@ -1183,10 +1183,44 @@ int str_comp_num(const char *a, const char *b, const int num);
>0 - String a is greater then string b
Remarks:
- The strings are treated as zero-termineted strings.
- The strings are treated as zero-terminated strings.
*/
int str_comp_filenames(const char *a, const char *b);
/*
Function: str_startswith
Checks whether the string begins with a certain prefix.
Parameter:
str - String to check.
prefix - Prefix to look for.
Returns:
A pointer to the string str after the string prefix, or 0 if
the string prefix isn't a prefix of the string str.
Remarks:
- The strings are treated as zero-terminated strings.
*/
const char *str_startswith(const char *str, const char *prefix);
/*
Function: str_endswith
Checks whether the string ends with a certain suffix.
Parameter:
str - String to check.
suffix - Suffix to look for.
Returns:
A pointer to the beginning of the suffix in the string str, or
0 if the string suffix isn't a suffix of the string str.
Remarks:
- The strings are treated as zero-terminated strings.
*/
const char *str_endswith(const char *str, const char *suffix);
/*
Function: str_utf8_dist
Computes the edit distance between two strings.
@ -1259,7 +1293,7 @@ int str_utf32_dist_buffer(const int *a, int a_len, const int *b, int b_len, int
Remarks:
- Only garanted to work with a-z/A-Z.
- The strings are treated as zero-termineted strings.
- The strings are treated as zero-terminated strings.
*/
const char *str_find_nocase(const char *haystack, const char *needle);
@ -1276,7 +1310,7 @@ const char *str_find_nocase(const char *haystack, const char *needle);
Returns NULL of needle could not be found.
Remarks:
- The strings are treated as zero-termineted strings.
- The strings are treated as zero-terminated strings.
*/
const char *str_find(const char *haystack, const char *needle);
@ -1451,7 +1485,7 @@ char *fs_getcwd(char *buffer, int buffer_size);
Returns 0 on success, 1 on failure.
Remarks:
- The string is treated as zero-termineted string.
- The string is treated as zero-terminated string.
*/
int fs_parent_dir(char *path);

View file

@ -318,13 +318,11 @@ bool CConsole::LineIsValid(const char *pStr)
void CConsole::ExecuteLineStroked(int Stroke, const char *pStr, int ClientID, bool InterpretSemicolons)
{
static const char s_aMulticommandPrefix[] = "mc;";
static const int s_PrefixLength = str_length(s_aMulticommandPrefix);
if(str_length(pStr) >= s_PrefixLength
&& str_comp_num(pStr, s_aMulticommandPrefix, s_PrefixLength) == 0)
const char *pWithoutPrefix = str_startswith(pStr, "mc;");
if(pWithoutPrefix)
{
InterpretSemicolons = true;
pStr += s_PrefixLength;
pStr = pWithoutPrefix;
}
while(pStr && *pStr)
{

View file

@ -106,8 +106,11 @@ public:
while((pLine = LineReader.Get()))
{
if(str_length(pLine) > 9 && !str_comp_num(pLine, "add_path ", 9))
AddPath(pLine+9);
const char *pLineWithoutPrefix = str_startswith(pLine, "add_path ");
if(pLineWithoutPrefix)
{
AddPath(pLine);
}
}
io_close(File);
@ -290,7 +293,8 @@ public:
{
return io_open(pFilename, Flags);
}
if(str_comp_num(pFilename, "mapres/../skins/", 16) == 0) {
if(str_startswith(pFilename, "mapres/../skins/"))
{
pFilename = pFilename + 10; // just start from skins/
}
if(pFilename[0] == '/' || pFilename[0] == '\\' || str_find(pFilename, "../") != NULL || str_find(pFilename, "..\\") != NULL

View file

@ -126,16 +126,15 @@ void CMenus::RenderDemoPlayer(CUIRect MainView)
str_copy(m_aDemoPlayerPopupHint, Localize("Please use a different name"), sizeof(m_aDemoPlayerPopupHint));
else
{
int len = str_length(m_aCurrentDemoFile);
if(len < 5 || str_comp_nocase(&m_aCurrentDemoFile[len-5], ".demo"))
if(!str_endswith(m_aCurrentDemoFile, ".demo"))
str_append(m_aCurrentDemoFile, ".demo", sizeof(m_aCurrentDemoFile));
char aPath[512];
str_format(aPath, sizeof(aPath), "%s/%s", m_aCurrentDemoFolder, m_aCurrentDemoFile);
IOHANDLE DemoFile = Storage()->OpenFile(aPath, IOFLAG_READ, IStorage::TYPE_SAVE);
const char* pStr = Localize("File already exists, do you want to overwrite it?");
if(DemoFile && str_comp_num(m_aDemoPlayerPopupHint, pStr, sizeof(m_aDemoPlayerPopupHint)) != 0)
const char *pStr = Localize("File already exists, do you want to overwrite it?");
if(DemoFile && str_comp(m_aDemoPlayerPopupHint, pStr) != 0)
{
io_close(DemoFile);
str_copy(m_aDemoPlayerPopupHint, pStr, sizeof(m_aDemoPlayerPopupHint));
@ -680,11 +679,12 @@ int CMenus::UiDoListboxEnd(float *pScrollValue, bool *pItemActivated, bool *pLis
int CMenus::DemolistFetchCallback(const char *pName, time_t Date, int IsDir, int StorageType, void *pUser)
{
CMenus *pSelf = (CMenus *)pUser;
int Length = str_length(pName);
if((pName[0] == '.' && (pName[1] == 0 ||
(pName[1] == '.' && pName[2] == 0 && !str_comp(pSelf->m_aCurrentDemoFolder, "demos")))) ||
(!IsDir && (Length < 5 || str_comp(pName+Length-5, ".demo"))))
if(str_comp(pName, ".") == 0
|| (str_comp(pName, "..") == 0 && str_comp(pSelf->m_aCurrentDemoFolder, "demos") == 0)
|| (!IsDir && !str_endswith(pName, ".demo")))
{
return 0;
}
CDemoItem Item;
str_copy(Item.m_aFilename, pName, sizeof(Item.m_aFilename));
@ -695,7 +695,7 @@ int CMenus::DemolistFetchCallback(const char *pName, time_t Date, int IsDir, int
}
else
{
str_copy(Item.m_aName, pName, min(static_cast<int>(sizeof(Item.m_aName)), Length-4));
str_copy(Item.m_aName, pName, min(static_cast<int>(sizeof(Item.m_aName)), str_length(pName) - 4));
Item.m_InfosLoaded = false;
Item.m_Date = Date;
}

View file

@ -839,9 +839,8 @@ void CMenus::RenderInGameNetwork(CUIRect MainView)
int CMenus::GhostlistFetchCallback(const char *pName, int IsDir, int StorageType, void *pUser)
{
CMenus *pSelf = (CMenus *)pUser;
int Length = str_length(pName);
const char *pMap = pSelf->Client()->GetCurrentMap();
if(IsDir || Length < 4 || str_comp(pName+Length-4, ".gho") != 0 || str_comp_num(pName, pMap, str_length(pMap)) != 0)
if(IsDir || !str_endswith(pName, ".gho") || !str_startswith(pName, pMap))
return 0;
char aFilename[256];

View file

@ -175,7 +175,7 @@ int CRaceDemo::RaceDemolistFetchCallback(const char *pName, time_t Date, int IsD
CDemoListParam *pParam = (CDemoListParam*) pUser;
int Length = str_length(pName);
int MapLen = str_length(pParam->pMap);
if(IsDir || Length < 5 || str_comp(pName + Length - 5, ".demo") != 0 || str_comp_num(pName, pParam->pMap, MapLen) != 0 || pName[MapLen] != '_')
if(IsDir || !str_endswith(pName, ".demo") || !str_startswith(pName, pParam->pMap) || pName[MapLen] != '_')
return 0;
CDemoItem Item;

View file

@ -32,20 +32,19 @@ int CSkins::SkinScan(const char *pName, int IsDir, int DirType, void *pUser)
{
CSkins *pSelf = (CSkins *)pUser;
int l = str_length(pName);
if(l < 4 || IsDir || str_comp(pName+l-4, ".png") != 0)
if(IsDir || !str_endswith(pName, ".png"))
return 0;
char aFilenameWithoutPng[128];
str_copy(aFilenameWithoutPng, pName, sizeof(aFilenameWithoutPng));
aFilenameWithoutPng[str_length(aFilenameWithoutPng) - 4] = 0;
char aNameWithoutPng[128];
str_copy(aNameWithoutPng, pName, sizeof(aNameWithoutPng));
aNameWithoutPng[str_length(aNameWithoutPng) - 4] = 0;
// Don't add duplicate skins (one from user's config directory, other from
// client itself)
for(int i = 0; i < pSelf->Num(); i++)
{
const char *pExName = pSelf->Get(i)->m_aName;
if(str_comp_num(pExName, pName, l-4) == 0 && str_length(pExName) == l-4)
if(str_comp(pExName, aNameWithoutPng) == 0)
return 0;
}
@ -60,7 +59,7 @@ int CSkins::SkinScan(const char *pName, int IsDir, int DirType, void *pUser)
}
CSkin Skin;
Skin.m_IsVanilla = IsVanillaSkin(aFilenameWithoutPng);
Skin.m_IsVanilla = IsVanillaSkin(aNameWithoutPng);
Skin.m_OrgTexture = pSelf->Graphics()->LoadTextureRaw(Info.m_Width, Info.m_Height, Info.m_Format, Info.m_pData, Info.m_Format, 0);
int BodySize = 96; // body size
@ -137,7 +136,7 @@ int CSkins::SkinScan(const char *pName, int IsDir, int DirType, void *pUser)
free(Info.m_pData);
// set skin data
str_copy(Skin.m_aName, pName, min((int)sizeof(Skin.m_aName),l-3));
str_copy(Skin.m_aName, aNameWithoutPng, sizeof(Skin.m_aName));
if(g_Config.m_Debug)
{
str_format(aBuf, sizeof(aBuf), "load skin %s", Skin.m_aName);

View file

@ -88,7 +88,7 @@ void CStatboard::OnMessage(int MsgType, void *pRawMsg)
p += str_length(pLookFor);
str_copy(aName, p, sizeof(aName));
// remove capture time
if(str_comp(aName+str_length(aName)-9, " seconds)") == 0)
if(str_endswith(aName, " seconds)"))
{
char *c = aName+str_length(aName)-10;
while(c > aName)

View file

@ -51,14 +51,14 @@ void CAutoMapper::Load(const char* pTileName)
int RunID = pCurrentConf->m_aRuns.add(NewRun);
pCurrentRun = &pCurrentConf->m_aRuns[RunID];
}
else if(!str_comp_num(pLine, "NewRun", 6))
else if(str_startswith(pLine, "NewRun"))
{
// add new run
CRun NewRun;
int RunID = pCurrentConf->m_aRuns.add(NewRun);
pCurrentRun = &pCurrentConf->m_aRuns[RunID];
}
else if(!str_comp_num(pLine, "Index", 5) && pCurrentRun)
else if(str_startswith(pLine, "Index") && pCurrentRun)
{
// new index
int ID = 0;
@ -108,7 +108,7 @@ void CAutoMapper::Load(const char* pTileName)
int IndexRuleID = pCurrentRun->m_aIndexRules.add(NewIndexRule);
pCurrentIndex = &pCurrentRun->m_aIndexRules[IndexRuleID];
}
else if(!str_comp_num(pLine, "Pos", 3) && pCurrentIndex)
else if(str_startswith(pLine, "Pos") && pCurrentIndex)
{
int x = 0, y = 0;
char aValue[128];
@ -215,7 +215,7 @@ void CAutoMapper::Load(const char* pTileName)
pCurrentIndex->m_aRules.add(NewPosRule);
}
}
else if(!str_comp_num(pLine, "Random", 6) && pCurrentIndex)
else if(str_startswith(pLine, "Random") && pCurrentIndex)
{
float Value;
char Specifier = ' ';
@ -229,7 +229,7 @@ void CAutoMapper::Load(const char* pTileName)
pCurrentIndex->m_RandomProbability = 1.0 / Value;
}
}
else if(!str_comp_num(pLine, "NoDefaultRule", 13) && pCurrentIndex)
else if(str_startswith(pLine, "NoDefaultRule") && pCurrentIndex)
{
pCurrentIndex->m_DefaultRule = false;
}

View file

@ -900,9 +900,8 @@ void CEditor::CallbackSaveMap(const char *pFileName, int StorageType, void *pUse
{
CEditor *pEditor = static_cast<CEditor*>(pUser);
char aBuf[1024];
const int Length = str_length(pFileName);
// add map extension
if(Length <= 4 || pFileName[Length-4] != '.' || str_comp_nocase(pFileName+Length-3, "map"))
if(!str_endswith(pFileName, ".map"))
{
str_format(aBuf, sizeof(aBuf), "%s.map", pFileName);
pFileName = aBuf;
@ -924,9 +923,8 @@ void CEditor::CallbackSaveCopyMap(const char *pFileName, int StorageType, void *
{
CEditor *pEditor = static_cast<CEditor*>(pUser);
char aBuf[1024];
const int Length = str_length(pFileName);
// add map extension
if(Length <= 4 || pFileName[Length-4] != '.' || str_comp_nocase(pFileName+Length-3, "map"))
if(!str_endswith(pFileName, ".map"))
{
str_format(aBuf, sizeof(aBuf), "%s.map", pFileName);
pFileName = aBuf;
@ -3725,9 +3723,9 @@ static int EditorListdirCallback(const char *pName, int IsDir, int StorageType,
int Length = str_length(pName);
if((pName[0] == '.' && (pName[1] == 0 ||
(pName[1] == '.' && pName[2] == 0 && (!str_comp(pEditor->m_pFileDialogPath, "maps") || !str_comp(pEditor->m_pFileDialogPath, "mapres"))))) ||
(!IsDir && ((pEditor->m_FileDialogFileType == CEditor::FILETYPE_MAP && (Length < 4 || str_comp(pName+Length-4, ".map"))) ||
(pEditor->m_FileDialogFileType == CEditor::FILETYPE_IMG && (Length < 4 || str_comp(pName+Length-4, ".png"))) ||
(pEditor->m_FileDialogFileType == CEditor::FILETYPE_SOUND && (Length < 5 || str_comp(pName+Length-5, ".opus"))))))
(!IsDir && ((pEditor->m_FileDialogFileType == CEditor::FILETYPE_MAP && !str_endswith(pName, ".map")) ||
(pEditor->m_FileDialogFileType == CEditor::FILETYPE_IMG && !str_endswith(pName, ".png")) ||
(pEditor->m_FileDialogFileType == CEditor::FILETYPE_SOUND && !str_endswith(pName, ".opus")))))
return 0;
CEditor::CFilelistItem Item;
@ -3912,8 +3910,7 @@ void CEditor::RenderFileDialog()
if(m_FileDialogFileType == CEditor::FILETYPE_IMG && m_FilePreviewImage == 0 && m_FilesSelectedIndex > -1)
{
int Length = str_length(m_FileList[m_FilesSelectedIndex].m_aFilename);
if(Length >= 4 && !str_comp(m_FileList[m_FilesSelectedIndex].m_aFilename+Length-4, ".png"))
if(str_endswith(m_FileList[m_FilesSelectedIndex].m_aFilename, ".png"))
{
char aBuffer[1024];
str_format(aBuffer, sizeof(aBuffer), "%s/%s", m_pFileDialogPath, m_FileList[m_FilesSelectedIndex].m_aFilename);

View file

@ -515,8 +515,8 @@ void CGameContext::SendVoteStatus(int ClientID, int Total, int Yes, int No)
void CGameContext::AbortVoteKickOnDisconnect(int ClientID)
{
if(m_VoteCloseTime && ((!str_comp_num(m_aVoteCommand, "kick ", 5) && str_toint(&m_aVoteCommand[5]) == ClientID) ||
(!str_comp_num(m_aVoteCommand, "set_team ", 9) && str_toint(&m_aVoteCommand[9]) == ClientID)))
if(m_VoteCloseTime && ((str_startswith(m_aVoteCommand, "kick ") && str_toint(&m_aVoteCommand[5]) == ClientID) ||
(str_startswith(m_aVoteCommand, "set_team ") && str_toint(&m_aVoteCommand[9]) == ClientID)))
m_VoteCloseTime = -1;
}
@ -1419,7 +1419,7 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
SendChatTarget(ClientID, "Invalid option");
return;
}
if(!Authed && (str_comp_num(pOption->m_aCommand, "sv_map ", 7) == 0 || str_comp_num(pOption->m_aCommand, "change_map ", 11) == 0 || str_comp_num(pOption->m_aCommand, "random_map", 10) == 0 || str_comp_num(pOption->m_aCommand, "random_unfinished_map", 21) == 0) && time_get() < m_LastMapVote + (time_freq() * g_Config.m_SvVoteMapTimeDelay))
if(!Authed && (str_startswith(pOption->m_aCommand, "sv_map ") || str_startswith(pOption->m_aCommand, "change_map ") || str_startswith(pOption->m_aCommand, "random_map") || str_startswith(pOption->m_aCommand, "random_unfinished_map")) && time_get() < m_LastMapVote + (time_freq() * g_Config.m_SvVoteMapTimeDelay))
{
str_format(aChatmsg, sizeof(aChatmsg), "There's a %d second delay between map-votes, please wait %d seconds.", g_Config.m_SvVoteMapTimeDelay, (int)(((m_LastMapVote+(g_Config.m_SvVoteMapTimeDelay * time_freq()))/time_freq())-(time_get()/time_freq())));
SendChatTarget(ClientID, aChatmsg);
@ -1431,10 +1431,10 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
pOption->m_aDescription, aReason);
str_format(aDesc, sizeof(aDesc), "%s", pOption->m_aDescription);
if((str_comp_num(pOption->m_aCommand, "random_map", 10) == 0 || str_comp_num(pOption->m_aCommand, "random_unfinished_map", 21) == 0) && str_length(aReason) == 1 && aReason[0] >= '1' && aReason[0] <= '5')
if((str_startswith(pOption->m_aCommand, "random_map") || str_startswith(pOption->m_aCommand, "random_unfinished_map")) && str_length(aReason) == 1 && aReason[0] >= '1' && aReason[0] <= '5')
{
int stars = aReason[0] - '0';
str_format(aCmd, sizeof(aCmd), "%s %d", pOption->m_aCommand, stars);
int Stars = aReason[0] - '0';
str_format(aCmd, sizeof(aCmd), "%s %d", pOption->m_aCommand, Stars);
}
else
{

View file

@ -554,8 +554,7 @@ void CGameTeams::OnFinish(CPlayer* Player)
}
if (CallSaveScore)
if (g_Config.m_SvNamelessScore || str_comp_num(Server()->ClientName(Player->GetCID()), "nameless tee",
12) != 0)
if (g_Config.m_SvNamelessScore || !str_startswith(Server()->ClientName(Player->GetCID()), "nameless tee"))
GameServer()->Score()->SaveScore(Player->GetCID(), Time,
GetCpCurrent(Player));
@ -565,8 +564,7 @@ void CGameTeams::OnFinish(CPlayer* Player)
|| Time < GameServer()->m_pController->m_CurrentRecord)
{
// check for nameless
if (g_Config.m_SvNamelessScore || str_comp_num(Server()->ClientName(Player->GetCID()), "nameless tee",
12) != 0)
if (g_Config.m_SvNamelessScore || !str_startswith(Server()->ClientName(Player->GetCID()), "nameless tee"))
{
GameServer()->m_pController->m_CurrentRecord = Time;
//dbg_msg("character", "Finish");

View file

@ -54,3 +54,42 @@ TEST(Str, Utf8CompConfusables)
EXPECT_FALSE(str_utf8_comp_confusable("o", "x") == 0);
EXPECT_TRUE(str_utf8_comp_confusable("aceiou", "ąçęįǫų") == 0);
}
TEST(Str, Startswith)
{
EXPECT_TRUE(str_startswith("abcdef", "abc"));
EXPECT_FALSE(str_startswith("abc", "abcdef"));
EXPECT_TRUE(str_startswith("xyz", ""));
EXPECT_FALSE(str_startswith("", "xyz"));
EXPECT_FALSE(str_startswith("house", "home"));
EXPECT_FALSE(str_startswith("blackboard", "board"));
EXPECT_TRUE(str_startswith("поплавать", "по"));
EXPECT_FALSE(str_startswith("плавать", "по"));
static const char ABCDEFG[] = "abcdefg";
static const char ABC[] = "abc";
EXPECT_EQ(str_startswith(ABCDEFG, ABC) - ABCDEFG, str_length(ABC));
}
TEST(Str, Endswith)
{
EXPECT_TRUE(str_endswith("abcdef", "def"));
EXPECT_FALSE(str_endswith("def", "abcdef"));
EXPECT_TRUE(str_endswith("xyz", ""));
EXPECT_FALSE(str_endswith("", "xyz"));
EXPECT_FALSE(str_endswith("rhyme", "mine"));
EXPECT_FALSE(str_endswith("blackboard", "black"));
EXPECT_TRUE(str_endswith("люди", "юди"));
EXPECT_FALSE(str_endswith("люди", "любовь"));
static const char ABCDEFG[] = "abcdefg";
static const char DEFG[] = "defg";
EXPECT_EQ(str_endswith(ABCDEFG, DEFG) - ABCDEFG,
str_length(ABCDEFG) - str_length(DEFG));
}

View file

@ -21,7 +21,7 @@ int main(int argc, const char **argv)
continue;
}
if(Len < sizeof(".map") || str_comp(argv[i] + Len - sizeof(".map"), ".map") != 0)
if(!str_endswith(argv[i], ".map"))
{
dbg_msg("config_common", "can't process non-map file '%s'", argv[i]);
continue;

View file

@ -71,7 +71,7 @@ bool Process(IStorage *pStorage, char **pMapNames)
IntsToStr(pTilemap[i]->m_aName, sizeof(pTilemap[i]->m_aName)/sizeof(int), aName[i]);
}
if(str_comp_num(aName[0], aName[1], sizeof(aName[0])) != 0 || pTilemap[0]->m_Width != pTilemap[1]->m_Width || pTilemap[0]->m_Height != pTilemap[1]->m_Height)
if(str_comp(aName[0], aName[1]) != 0 || pTilemap[0]->m_Width != pTilemap[1]->m_Width || pTilemap[0]->m_Height != pTilemap[1]->m_Height)
{
dbg_msg("map_compare", "different tile layers:");
for(int i = 0; i < 2; ++i)