Merge branch 'map-by-name' of https://git.do.srb2.org/KartKrew/Kart into internal16fixes

This commit is contained in:
toaster 2022-10-27 12:16:11 +01:00
commit 60b8916a96
10 changed files with 596 additions and 99 deletions

View file

@ -353,6 +353,40 @@ size_t COM_CheckParm(const char *check)
return 0; return 0;
} }
/** \brief COM_CheckParm, but checks only the start of each argument.
* E.g. checking for "-no" would match "-noerror" too.
*/
size_t COM_CheckPartialParm(const char *check)
{
int len;
size_t i;
len = strlen(check);
for (i = 1; i < com_argc; i++)
{
if (strncasecmp(check, com_argv[i], len) == 0)
return i;
}
return 0;
}
/** Find the first argument that starts with a hyphen (-).
* \return The index of the argument, or 0
* if there are no such arguments.
*/
size_t COM_FirstOption(void)
{
size_t i;
for (i = 1; i < com_argc; i++)
{
if (com_argv[i][0] == '-')/* options start with a hyphen */
return i;
}
return 0;
}
/** Parses a string into command-line tokens. /** Parses a string into command-line tokens.
* *
* \param ptext A null-terminated string. Does not need to be * \param ptext A null-terminated string. Does not need to be

View file

@ -37,6 +37,8 @@ size_t COM_Argc(void);
const char *COM_Argv(size_t arg); // if argv > argc, returns empty string const char *COM_Argv(size_t arg); // if argv > argc, returns empty string
char *COM_Args(void); char *COM_Args(void);
size_t COM_CheckParm(const char *check); // like M_CheckParm :) size_t COM_CheckParm(const char *check); // like M_CheckParm :)
size_t COM_CheckPartialParm(const char *check);
size_t COM_FirstOption(void);
// match existing command or NULL // match existing command or NULL
const char *COM_CompleteCommand(const char *partial, INT32 skips); const char *COM_CompleteCommand(const char *partial, INT32 skips);

View file

@ -1247,26 +1247,6 @@ void D_SRB2Main(void)
if (M_CheckParm("-server") || dedicated) if (M_CheckParm("-server") || dedicated)
netgame = server = true; netgame = server = true;
if (M_CheckParm("-warp") && M_IsNextParm())
{
const char *word = M_GetNextParm();
char ch; // use this with sscanf to catch non-digits with
if (fastncmp(word, "MAP", 3)) // MAPxx name
pstartmap = M_MapNumber(word[3], word[4]);
else if (sscanf(word, "%d%c", &pstartmap, &ch) != 1) // a plain number
I_Error("Cannot warp to map %s (invalid map name)\n", word);
// Don't check if lump exists just yet because the wads haven't been loaded!
// Just do a basic range check here.
if (pstartmap < 1 || pstartmap > NUMMAPS)
I_Error("Cannot warp to map %d (out of range)\n", pstartmap);
else
{
if (!M_CheckParm("-server"))
G_SetGameModified(true, true);
autostart = true;
}
}
CONS_Printf("Z_Init(): Init zone memory allocation daemon. \n"); CONS_Printf("Z_Init(): Init zone memory allocation daemon. \n");
Z_Init(); Z_Init();
@ -1446,6 +1426,23 @@ void D_SRB2Main(void)
//------------------------------------------------ COMMAND LINE PARAMS //------------------------------------------------ COMMAND LINE PARAMS
// this must be done after loading gamedata,
// to avoid setting off the corrupted gamedata code in G_LoadGameData if a SOC with custom gamedata is added
// -- Monster Iestyn 20/02/20
if (M_CheckParm("-warp") && M_IsNextParm())
{
const char *word = M_GetNextParm();
pstartmap = G_FindMapByNameOrCode(word, 0);
if (! pstartmap)
I_Error("Cannot find a map remotely named '%s'\n", word);
else
{
if (!M_CheckParm("-server"))
G_SetGameModified(true, true);
autostart = true;
}
}
// Initialize CD-Audio // Initialize CD-Audio
if (M_CheckParm("-usecd") && !dedicated) if (M_CheckParm("-usecd") && !dedicated)
I_InitCD(); I_InitCD();

View file

@ -2519,27 +2519,67 @@ void D_PickVote(void)
SendNetXCmd(XD_PICKVOTE, &buf, 2); SendNetXCmd(XD_PICKVOTE, &buf, 2);
} }
static char *
ConcatCommandArgv (int start, int end)
{
char *final;
size_t size;
int i;
char *p;
size = 0;
for (i = start; i < end; ++i)
{
/*
one space after each argument, but terminating
character on final argument
*/
size += strlen(COM_Argv(i)) + 1;
}
final = ZZ_Alloc(size);
p = final;
--end;/* handle the final argument separately */
for (i = start; i < end; ++i)
{
p += sprintf(p, "%s ", COM_Argv(i));
}
/* at this point "end" is actually the last argument's position */
strcpy(p, COM_Argv(end));
return final;
}
//
// Warp to map code. // Warp to map code.
// Called either from map <mapname> console command, or idclev cheat. // Called either from map <mapname> console command, or idclev cheat.
// //
// Largely rewritten by James.
//
static void Command_Map_f(void) static void Command_Map_f(void)
{ {
const char *mapname; size_t first_option;
size_t i; size_t option_force;
INT32 newmapnum; size_t option_gametype;
boolean newresetplayers, newencoremode; size_t option_encore;
INT32 newgametype = gametype; const char *gametypename;
boolean newresetplayers;
// max length of command: map map03 -gametype race -noresetplayers -force -encore boolean mustmodifygame;
// 1 2 3 4 5 6 7
// = 8 arg max INT32 newmapnum;
// i don't know whether this is intrinsic to the system or just someone being weird but
// "noresetplayers" is pretty useless for kart if it turns out this is too close to the limit char * mapname;
if (COM_Argc() < 2 || COM_Argc() > 8) char *realmapname = NULL;
{
CONS_Printf(M_GetText("map <mapname> [-gametype <type> [-force]: warp to map\n")); INT32 newgametype = gametype;
return; boolean newencoremode = cv_kartencore.value;
}
INT32 d;
if (client && !IsPlayerAdmin(consoleplayer)) if (client && !IsPlayerAdmin(consoleplayer))
{ {
@ -2547,99 +2587,129 @@ static void Command_Map_f(void)
return; return;
} }
// internal wad lump always: map command doesn't support external files as in doom legacy option_force = COM_CheckPartialParm("-f");
if (W_CheckNumForName(COM_Argv(1)) == LUMPERROR) option_gametype = COM_CheckPartialParm("-g");
option_encore = COM_CheckPartialParm("-e");
newresetplayers = ! COM_CheckParm("-noresetplayers");
mustmodifygame = !( netgame || multiplayer || majormods );
if (mustmodifygame && !option_force)
{ {
CONS_Alert(CONS_ERROR, M_GetText("Internal game level '%s' not found\n"), COM_Argv(1)); /* May want to be more descriptive? */
CONS_Printf(M_GetText("Sorry, level change disabled in single player.\n"));
return; return;
} }
if (!(netgame || multiplayer) && !majormods)
{
if (COM_CheckParm("-force"))
{
G_SetGameModified(false, true);
}
else
{
CONS_Printf(M_GetText("Sorry, level change disabled in single player.\n"));
return;
}
}
newresetplayers = !COM_CheckParm("-noresetplayers");
if (!newresetplayers && !cv_debug) if (!newresetplayers && !cv_debug)
{ {
CONS_Printf(M_GetText("DEVMODE must be enabled.\n")); CONS_Printf(M_GetText("DEVMODE must be enabled.\n"));
return; return;
} }
mapname = COM_Argv(1); if (option_gametype)
if (strlen(mapname) != 5
|| (newmapnum = M_MapNumber(mapname[3], mapname[4])) == 0)
{ {
CONS_Alert(CONS_ERROR, M_GetText("Invalid level name %s\n"), mapname); if (!multiplayer)
{
CONS_Printf(M_GetText(
"You can't switch gametypes in single player!\n"));
return;
}
else if (COM_Argc() < option_gametype + 2)/* no argument after? */
{
CONS_Alert(CONS_ERROR,
"No gametype name follows parameter '%s'.\n",
COM_Argv(option_gametype));
return;
}
}
if (!( first_option = COM_FirstOption() ))
first_option = COM_Argc();
if (first_option < 2)
{
/* I'm going over the fucking lines and I DON'T CAREEEEE */
CONS_Printf("map <name / [MAP]code / number> [-gametype <type>] [-encore] [-force]:\n");
CONS_Printf(M_GetText(
"Warp to a map, by its name, two character code, with optional \"MAP\" prefix, or by its number (though why would you).\n"
"All parameters are case-insensitive and may be abbreviated.\n"));
return; return;
} }
mapname = ConcatCommandArgv(1, first_option);
newmapnum = G_FindMapByNameOrCode(mapname, &realmapname);
if (newmapnum == 0)
{
CONS_Alert(CONS_ERROR, M_GetText("Could not find any map described as '%s'.\n"), mapname);
Z_Free(mapname);
return;
}
if (mustmodifygame && option_force)
{
G_SetGameModified(false, true);
}
// new gametype value // new gametype value
// use current one by default // use current one by default
i = COM_CheckParm("-gametype"); if (option_gametype)
if (i)
{ {
if (!multiplayer) gametypename = COM_Argv(option_gametype + 1);
{
CONS_Printf(M_GetText("You can't switch gametypes in single player!\n")); newgametype = G_GetGametypeByName(gametypename);
return;
}
newgametype = G_GetGametypeByName(COM_Argv(i+1));
if (newgametype == -1) // reached end of the list with no match if (newgametype == -1) // reached end of the list with no match
{ {
INT32 j = atoi(COM_Argv(i+1)); // assume they gave us a gametype number, which is okay too /* Did they give us a gametype number? That's okay too! */
if (j >= 0 && j < NUMGAMETYPES) if (isdigit(gametypename[0]))
newgametype = (INT16)j; {
d = atoi(gametypename);
if (d >= 0 && d < NUMGAMETYPES)
newgametype = d;
else
{
CONS_Alert(CONS_ERROR,
"Gametype number %d is out of range. Use a number between"
" 0 and %d inclusive. ...Or just use the name. :v\n",
d,
NUMGAMETYPES-1);
Z_Free(realmapname);
Z_Free(mapname);
return;
}
}
else
{
CONS_Alert(CONS_ERROR,
"'%s' is not a gametype.\n",
gametypename);
Z_Free(realmapname);
Z_Free(mapname);
return;
}
} }
} }
// new encoremode value if (!option_force && newgametype == gametype) // SRB2Kart
// use cvar by default
newencoremode = (boolean)cv_kartencore.value;
if (COM_CheckParm("-encore"))
{
if (!M_SecretUnlocked(SECRET_ENCORE) && !newencoremode)
{
CONS_Alert(CONS_NOTICE, M_GetText("You haven't unlocked Encore Mode yet!\n"));
return;
}
newencoremode = !newencoremode;
}
if (!(i = COM_CheckParm("-force")) && newgametype == gametype) // SRB2Kart
newresetplayers = false; // if not forcing and gametypes is the same newresetplayers = false; // if not forcing and gametypes is the same
// don't use a gametype the map doesn't support // don't use a gametype the map doesn't support
if (cv_debug || i || cv_skipmapcheck.value) if (cv_debug || option_force || cv_skipmapcheck.value)
; // The player wants us to trek on anyway. Do so. fromlevelselect = false; // The player wants us to trek on anyway. Do so.
// G_TOLFlag handles both multiplayer gametype and ignores it for !multiplayer // G_TOLFlag handles both multiplayer gametype and ignores it for !multiplayer
// Alternatively, bail if the map header is completely missing anyway. else
else if (!mapheaderinfo[newmapnum-1]
|| !(mapheaderinfo[newmapnum-1]->typeoflevel & G_TOLFlag(newgametype)))
{ {
char gametypestring[32] = "Single Player"; if (!(mapheaderinfo[newmapnum-1]->typeoflevel & G_TOLFlag(newgametype)))
if (multiplayer)
{ {
if (newgametype >= 0 && newgametype < NUMGAMETYPES CONS_Alert(CONS_WARNING, M_GetText("Course %s (%s) doesn't support %s mode!\n(Use -force to override)\n"), realmapname, G_BuildMapName(newmapnum),
&& Gametype_Names[newgametype]) (multiplayer ? gametype_cons_t[newgametype].strvalue : "Single Player"));
strcpy(gametypestring, Gametype_Names[newgametype]); Z_Free(realmapname);
Z_Free(mapname);
return;
} }
CONS_Alert(CONS_WARNING, M_GetText("%s doesn't support %s mode!\n(Use -force to override)\n"), mapname, gametypestring);
return;
} }
// Prevent warping to locked levels // Prevent warping to locked levels
@ -2649,11 +2719,25 @@ static void Command_Map_f(void)
if (!dedicated && M_MapLocked(newmapnum)) if (!dedicated && M_MapLocked(newmapnum))
{ {
CONS_Alert(CONS_NOTICE, M_GetText("You need to unlock this level before you can warp to it!\n")); CONS_Alert(CONS_NOTICE, M_GetText("You need to unlock this level before you can warp to it!\n"));
Z_Free(realmapname);
Z_Free(mapname);
return; return;
} }
if (option_encore)
{
newencoremode = ! newencoremode;
if (! M_SecretUnlocked(SECRET_ENCORE) && newencoremode)
{
CONS_Alert(CONS_NOTICE, M_GetText("You haven't unlocked Encore Mode yet!\n"));
return;
}
}
fromlevelselect = false; fromlevelselect = false;
D_MapChange(newmapnum, newgametype, newencoremode, newresetplayers, 0, false, false); D_MapChange(newmapnum, newgametype, newencoremode, newresetplayers, 0, false, false);
Z_Free(realmapname);
} }
/** Receives a map command and changes the map. /** Receives a map command and changes the map.
@ -2702,7 +2786,9 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
lastgametype = gametype; lastgametype = gametype;
gametype = READUINT8(*cp); gametype = READUINT8(*cp);
if (gametype != lastgametype) if (gametype < 0 || gametype >= NUMGAMETYPES)
gametype = lastgametype;
else if (gametype != lastgametype)
D_GameTypeChanged(lastgametype); // emulate consvar_t behavior for gametype D_GameTypeChanged(lastgametype); // emulate consvar_t behavior for gametype
if (!G_RaceGametype()) if (!G_RaceGametype())

View file

@ -532,6 +532,7 @@ extern boolean capslock;
// if we ever make our alloc stuff... // if we ever make our alloc stuff...
#define ZZ_Alloc(x) Z_Malloc(x, PU_STATIC, NULL) #define ZZ_Alloc(x) Z_Malloc(x, PU_STATIC, NULL)
#define ZZ_Calloc(x) Z_Calloc(x, PU_STATIC, NULL)
// i_system.c, replace getchar() once the keyboard has been appropriated // i_system.c, replace getchar() once the keyboard has been appropriated
INT32 I_GetKey(void); INT32 I_GetKey(void);

View file

@ -139,6 +139,9 @@ typedef long ssize_t;
#define strlwr _strlwr #define strlwr _strlwr
#endif #endif
char *strcasestr(const char *in, const char *what);
#define stristr strcasestr
#if defined (macintosh) //|| defined (__APPLE__) //skip all boolean/Boolean crap #if defined (macintosh) //|| defined (__APPLE__) //skip all boolean/Boolean crap
#define true 1 #define true 1
#define false 0 #define false 0

View file

@ -4866,6 +4866,242 @@ char *G_BuildMapTitle(INT32 mapnum)
return title; return title;
} }
static void measurekeywords(mapsearchfreq_t *fr,
struct searchdim **dimp, UINT8 *cuntp,
const char *s, const char *q, boolean wanttable)
{
char *qp;
char *sp;
if (wanttable)
(*dimp) = Z_Realloc((*dimp), 255 * sizeof (struct searchdim),
PU_STATIC, NULL);
for (qp = strtok(va("%s", q), " ");
qp && fr->total < 255;
qp = strtok(0, " "))
{
if (( sp = strcasestr(s, qp) ))
{
if (wanttable)
{
(*dimp)[(*cuntp)].pos = sp - s;
(*dimp)[(*cuntp)].siz = strlen(qp);
}
(*cuntp)++;
fr->total++;
}
}
if (wanttable)
(*dimp) = Z_Realloc((*dimp), (*cuntp) * sizeof (struct searchdim),
PU_STATIC, NULL);
}
static void writesimplefreq(mapsearchfreq_t *fr, INT32 *frc,
INT32 mapnum, UINT8 pos, UINT8 siz)
{
fr[(*frc)].mapnum = mapnum;
fr[(*frc)].matchd = ZZ_Alloc(sizeof (struct searchdim));
fr[(*frc)].matchd[0].pos = pos;
fr[(*frc)].matchd[0].siz = siz;
fr[(*frc)].matchc = 1;
fr[(*frc)].total = 1;
(*frc)++;
}
INT32 G_FindMap(const char *mapname, char **foundmapnamep,
mapsearchfreq_t **freqp, INT32 *freqcp)
{
INT32 newmapnum = 0;
INT32 mapnum;
INT32 apromapnum = 0;
size_t mapnamelen;
char *realmapname = NULL;
char *newmapname = NULL;
char *apromapname = NULL;
char *aprop = NULL;
mapsearchfreq_t *freq;
boolean wanttable;
INT32 freqc;
UINT8 frequ;
INT32 i;
mapnamelen = strlen(mapname);
/* Count available maps; how ugly. */
for (i = 0, freqc = 0; i < NUMMAPS; ++i)
{
if (mapheaderinfo[i])
freqc++;
}
freq = ZZ_Calloc(freqc * sizeof (mapsearchfreq_t));
wanttable = !!( freqp );
freqc = 0;
for (i = 0, mapnum = 1; i < NUMMAPS; ++i, ++mapnum)
if (mapheaderinfo[i])
{
if (!( realmapname = G_BuildMapTitle(mapnum) ))
continue;
aprop = realmapname;
/* Now that we found a perfect match no need to fucking guess. */
if (strnicmp(realmapname, mapname, mapnamelen) == 0)
{
if (wanttable)
{
writesimplefreq(freq, &freqc, mapnum, 0, mapnamelen);
}
if (newmapnum == 0)
{
newmapnum = mapnum;
newmapname = realmapname;
realmapname = 0;
Z_Free(apromapname);
if (!wanttable)
break;
}
}
else
if (apromapnum == 0 || wanttable)
{
/* LEVEL 1--match keywords verbatim */
if (( aprop = strcasestr(realmapname, mapname) ))
{
if (wanttable)
{
writesimplefreq(freq, &freqc,
mapnum, aprop - realmapname, mapnamelen);
}
if (apromapnum == 0)
{
apromapnum = mapnum;
apromapname = realmapname;
realmapname = 0;
}
}
else/* ...match individual keywords */
{
freq[freqc].mapnum = mapnum;
measurekeywords(&freq[freqc],
&freq[freqc].matchd, &freq[freqc].matchc,
realmapname, mapname, wanttable);
if (freq[freqc].total)
freqc++;
}
}
Z_Free(realmapname);/* leftover old name */
}
if (newmapnum == 0)/* no perfect match--try a substring */
{
newmapnum = apromapnum;
newmapname = apromapname;
}
if (newmapnum == 0)/* calculate most queries met! */
{
frequ = 0;
for (i = 0; i < freqc; ++i)
{
if (freq[i].total > frequ)
{
frequ = freq[i].total;
newmapnum = freq[i].mapnum;
}
}
if (newmapnum)
{
newmapname = G_BuildMapTitle(newmapnum);
}
}
if (freqp)
(*freqp) = freq;
else
Z_Free(freq);
if (freqcp)
(*freqcp) = freqc;
if (foundmapnamep)
(*foundmapnamep) = newmapname;
else
Z_Free(newmapname);
return newmapnum;
}
void G_FreeMapSearch(mapsearchfreq_t *freq, INT32 freqc)
{
INT32 i;
for (i = 0; i < freqc; ++i)
{
Z_Free(freq[i].matchd);
}
Z_Free(freq);
}
INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep)
{
boolean usemapcode = false;
INT32 newmapnum;
size_t mapnamelen;
char *p;
mapnamelen = strlen(mapname);
if (mapnamelen == 2)/* maybe two digit code */
{
if (( newmapnum = M_MapNumber(mapname[0], mapname[1]) ))
usemapcode = true;
}
else if (mapnamelen == 5 && strnicmp(mapname, "MAP", 3) == 0)
{
if (( newmapnum = M_MapNumber(mapname[3], mapname[4]) ))
usemapcode = true;
}
if (!usemapcode)
{
/* Now detect map number in base 10, which no one asked for. */
newmapnum = strtol(mapname, &p, 10);
if (*p == '\0')/* we got it */
{
if (newmapnum < 1 || newmapnum > NUMMAPS)
{
CONS_Alert(CONS_ERROR, M_GetText("Invalid map number %d.\n"), newmapnum);
return 0;
}
usemapcode = true;
}
else
{
newmapnum = G_FindMap(mapname, realmapnamep, NULL, NULL);
}
}
if (usemapcode)
{
/* we can't check mapheaderinfo for this hahahaha */
if (W_CheckNumForName(G_BuildMapName(newmapnum)) == LUMPERROR)
return 0;
if (realmapnamep)
(*realmapnamep) = G_BuildMapTitle(newmapnum);
}
return newmapnum;
}
// //
// DEMO RECORDING // DEMO RECORDING
// //

View file

@ -173,6 +173,30 @@ void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer,
boolean skipprecutscene); boolean skipprecutscene);
char *G_BuildMapTitle(INT32 mapnum); char *G_BuildMapTitle(INT32 mapnum);
struct searchdim
{
UINT8 pos;
UINT8 siz;
};
typedef struct
{
INT16 mapnum;
UINT8 matchc;
struct searchdim *matchd;/* offset that a pattern was matched */
UINT8 keywhc;
struct searchdim *keywhd;/* ...in KEYWORD */
UINT8 total;/* total hits */
}
mapsearchfreq_t;
INT32 G_FindMap(const char *query, char **foundmapnamep,
mapsearchfreq_t **freqp, INT32 *freqc);
void G_FreeMapSearch(mapsearchfreq_t *freq, INT32 freqc);
/* Match map name by search + 2 digit map code or map number. */
INT32 G_FindMapByNameOrCode(const char *query, char **foundmapnamep);
// XMOD spawning // XMOD spawning
mapthing_t *G_FindCTFStart(INT32 playernum); mapthing_t *G_FindCTFStart(INT32 playernum);
mapthing_t *G_FindMatchStart(INT32 playernum); mapthing_t *G_FindMatchStart(INT32 playernum);

111
src/strcasestr.c Normal file
View file

@ -0,0 +1,111 @@
/*
strcasestr -- case insensitive substring searching function.
*/
/*
Copyright 2019 James R.
All rights reserved.
Redistribution and use in source forms, with or without modification, is
permitted provided that the following condition is met:
1. Redistributions of source code must retain the above copyright notice, this
condition and the following disclaimer.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
static inline int
trycmp (char **pp, char *cp,
const char *q, size_t qn)
{
char *p;
p = (*pp);
if (strncasecmp(p, q, qn) == 0)
return 0;
(*pp) = strchr(&p[1], (*cp));
return 1;
}
static inline void
swapp (char ***ppap, char ***ppbp, char **cpap, char **cpbp)
{
char **pp;
char *p;
pp = *ppap;
*ppap = *ppbp;
*ppbp = pp;
p = *cpap;
*cpap = *cpbp;
*cpbp = p;
}
char *
strcasestr (const char *s, const char *q)
{
size_t qn;
char uc;
char lc;
char *up;
char *lp;
char **ppa;
char **ppb;
char *cpa;
char *cpb;
uc = toupper(*q);
lc = tolower(*q);
up = strchr(s, uc);
lp = strchr(s, lc);
if (!( (intptr_t)up|(intptr_t)lp ))
return 0;
if (!lp || ( up && up < lp ))
{
ppa = &up;
ppb = &lp;
cpa = &uc;
cpb = &lc;
}
else
{
ppa = &lp;
ppb = &up;
cpa = &lc;
cpb = &uc;
}
qn = strlen(q);
for (;;)
{
if (trycmp(ppa, cpa, q, qn) == 0)
return (*ppa);
if (!( (intptr_t)up|(intptr_t)lp ))
break;
if (!(*ppa) || ( (*ppb) && (*ppb) < (*ppa) ))
swapp(&ppa, &ppb, &cpa, &cpb);
}
return 0;
}

View file

@ -2,6 +2,7 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 2006 by Graue. // Copyright (C) 2006 by Graue.
// Copyright (C) 2006-2018 by Sonic Team Junior. // Copyright (C) 2006-2018 by Sonic Team Junior.
// Copyright (C) 2019 by James R.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
@ -50,3 +51,5 @@ size_t strlcpy(char *dst, const char *src, size_t siz)
} }
#endif #endif
#include "strcasestr.c"