]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - common.c
physics: fix and refactor unsticking
[xonotic/darkplaces.git] / common.c
index 03b7087ef9016182582d45267bbab4abdb3b7471..f4e459af3e104d8a883a3a1447f86f19f044fa2a 100644 (file)
--- a/common.c
+++ b/common.c
@@ -1,5 +1,6 @@
 /*
 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
@@ -28,14 +29,15 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #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];
+unsigned com_token_len;
 
 //===========================================================================
 
@@ -180,7 +182,7 @@ int COM_Wordwrap(const char *string, size_t length, float continuationWidth, flo
        //     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;
@@ -258,7 +260,7 @@ int COM_Wordwrap(const char *string, size_t length, float continuationWidth, flo
        return result;
 
 /*
-       qboolean isContinuation = false;
+       qbool isContinuation = false;
        float currentWordSpace = 0;
        const char *currentWord = 0;
        float minReserve = 0;
@@ -455,16 +457,17 @@ int COM_Wordwrap(const char *string, size_t length, float continuationWidth, flo
 COM_ParseToken_Simple
 
 Parse a token out of a string
+Writes the token and its strlen to the com_token and com_token_len globals.
 ==============
 */
-int COM_ParseToken_Simple(const char **datapointer, qboolean returnnewline, qboolean parsebackslash, qboolean parsecomments)
+qbool COM_ParseToken_Simple(const char **datapointer, qbool returnnewline, qbool parsebackslash, qbool parsecomments)
 {
        int len;
        int c;
        const char *data = *datapointer;
 
-       len = 0;
-       com_token[0] = 0;
+       com_token_len = len = 0;
+       com_token[0] = '\0';
 
        if (!data)
        {
@@ -529,7 +532,8 @@ skipwhite:
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = c;
                }
-               com_token[len] = 0;
+               com_token[len] = '\0';
+               com_token_len = len;
                if (*data == '\"')
                        data++;
                *datapointer = data;
@@ -539,7 +543,8 @@ skipwhite:
        {
                // translate Mac line ending to UNIX
                com_token[len++] = '\n';data++;
-               com_token[len] = 0;
+               com_token[len] = '\0';
+               com_token_len = len;
                *datapointer = data;
                return true;
        }
@@ -547,7 +552,8 @@ skipwhite:
        {
                // single character
                com_token[len++] = *data++;
-               com_token[len] = 0;
+               com_token[len] = '\0';
+               com_token_len = len;
                *datapointer = data;
                return true;
        }
@@ -557,7 +563,8 @@ skipwhite:
                for (;!ISWHITESPACE(*data);data++)
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = *data;
-               com_token[len] = 0;
+               com_token[len] = '\0';
+               com_token_len = len;
                *datapointer = data;
                return true;
        }
@@ -568,16 +575,17 @@ skipwhite:
 COM_ParseToken_QuakeC
 
 Parse a token out of a string
+Writes the token and its strlen to the com_token and com_token_len globals.
 ==============
 */
-int COM_ParseToken_QuakeC(const char **datapointer, qboolean returnnewline)
+qbool COM_ParseToken_QuakeC(const char **datapointer, qbool returnnewline)
 {
        int len;
        int c;
        const char *data = *datapointer;
 
-       len = 0;
-       com_token[0] = 0;
+       com_token_len = len = 0;
+       com_token[0] = '\0';
 
        if (!data)
        {
@@ -643,7 +651,8 @@ skipwhite:
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = c;
                }
-               com_token[len] = 0;
+               com_token[len] = '\0';
+               com_token_len = len;
                if (*data == quote)
                        data++;
                *datapointer = data;
@@ -653,7 +662,8 @@ skipwhite:
        {
                // translate Mac line ending to UNIX
                com_token[len++] = '\n';data++;
-               com_token[len] = 0;
+               com_token[len] = '\0';
+               com_token_len = len;
                *datapointer = data;
                return true;
        }
@@ -661,7 +671,8 @@ skipwhite:
        {
                // single character
                com_token[len++] = *data++;
-               com_token[len] = 0;
+               com_token[len] = '\0';
+               com_token_len = len;
                *datapointer = data;
                return true;
        }
@@ -671,7 +682,8 @@ skipwhite:
                for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = *data;
-               com_token[len] = 0;
+               com_token[len] = '\0';
+               com_token_len = len;
                *datapointer = data;
                return true;
        }
@@ -682,16 +694,17 @@ skipwhite:
 COM_ParseToken_VM_Tokenize
 
 Parse a token out of a string
+Writes the token and its strlen to the com_token and com_token_len globals.
 ==============
 */
-int COM_ParseToken_VM_Tokenize(const char **datapointer, qboolean returnnewline)
+qbool COM_ParseToken_VM_Tokenize(const char **datapointer, qbool returnnewline)
 {
        int len;
        int c;
        const char *data = *datapointer;
 
-       len = 0;
-       com_token[0] = 0;
+       com_token_len = len = 0;
+       com_token[0] = '\0';
 
        if (!data)
        {
@@ -757,7 +770,8 @@ skipwhite:
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = c;
                }
-               com_token[len] = 0;
+               com_token[len] = '\0';
+               com_token_len = len;
                if (*data == quote)
                        data++;
                *datapointer = data;
@@ -767,7 +781,8 @@ skipwhite:
        {
                // translate Mac line ending to UNIX
                com_token[len++] = '\n';data++;
-               com_token[len] = 0;
+               com_token[len] = '\0';
+               com_token_len = len;
                *datapointer = data;
                return true;
        }
@@ -775,7 +790,8 @@ skipwhite:
        {
                // single character
                com_token[len++] = *data++;
-               com_token[len] = 0;
+               com_token[len] = '\0';
+               com_token_len = len;
                *datapointer = data;
                return true;
        }
@@ -785,7 +801,8 @@ skipwhite:
                for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = *data;
-               com_token[len] = 0;
+               com_token[len] = '\0';
+               com_token_len = len;
                *datapointer = data;
                return true;
        }
@@ -796,15 +813,16 @@ skipwhite:
 COM_ParseToken_Console
 
 Parse a token out of a string, behaving like the qwcl console
+Writes the token and its strlen to the com_token and com_token_len globals.
 ==============
 */
-int COM_ParseToken_Console(const char **datapointer)
+qbool COM_ParseToken_Console(const char **datapointer)
 {
        int len;
        const char *data = *datapointer;
 
-       len = 0;
-       com_token[0] = 0;
+       com_token_len = len = 0;
+       com_token[0] = '\0';
 
        if (!data)
        {
@@ -842,7 +860,8 @@ skipwhite:
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = *data;
                }
-               com_token[len] = 0;
+               com_token[len] = '\0';
+               com_token_len = len;
                if (*data == '\"')
                        data++;
                *datapointer = data;
@@ -853,37 +872,14 @@ skipwhite:
                for (;!ISWHITESPACE(*data);data++)
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = *data;
-               com_token[len] = 0;
+               com_token[len] = '\0';
+               com_token_len = len;
                *datapointer = data;
        }
 
        return true;
 }
 
-
-/*
-================
-COM_CheckParm
-
-Returns the position (1 to argc-1) in the program's argument list
-where the given parameter apears, or 0 if not present
-================
-*/
-int COM_CheckParm (const char *parm)
-{
-       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;
-}
-
 /*
 ===============
 Com_CalcRoll
@@ -893,11 +889,11 @@ Used by view and sv_user
 */
 float Com_CalcRoll (const vec3_t angles, const vec3_t velocity, const vec_t angleval, const vec_t velocityval)
 {
-       vec3_t  right;
+       vec3_t  forward, right, up;
        float   sign;
        float   side;
 
-       AngleVectors (angles, NULL, right, NULL);
+       AngleVectors (angles, forward, right, up);
        side = DotProduct (velocity, right);
        sign = side < 0 ? -1 : 1;
        side = fabs(side);
@@ -926,9 +922,9 @@ void COM_Init_Commands (void)
        Cvar_RegisterVariable (&registered);
        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;
@@ -1023,6 +1019,11 @@ int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list ar
        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 %lu\n", (unsigned long)buffersize);
+               else
+                       Sys_Printf("dpvsnprintf: truncated to %lu bytes: \"%s\"\n", (unsigned long)buffersize - 1, buffer);
                return -1;
        }
 
@@ -1032,10 +1033,12 @@ int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list ar
 
 //======================================
 
-void COM_ToLowerString (const char *in, char *out, size_t size_out)
+size_t COM_ToLowerString(const char *in, char *out, size_t size_out)
 {
+       const char *out_start = out;
+
        if (size_out == 0)
-               return;
+               return 0;
 
        if(utf8_enable.integer)
        {
@@ -1046,12 +1049,12 @@ void COM_ToLowerString (const char *in, char *out, size_t size_out)
                        Uchar ch = u8_getchar_utf8_enabled(in, &in);
                        ch = u8_tolower(ch);
                        n = u8_fromchar(ch, out, size_out);
+                       out += n; // before the break so the return is correct
                        if(n <= 0)
                                break;
-                       out += n;
                        size_out -= n;
                }
-               return;
+               return out - out_start;
        }
 
        while (*in && size_out > 1)
@@ -1063,12 +1066,15 @@ void COM_ToLowerString (const char *in, char *out, size_t size_out)
                size_out--;
        }
        *out = '\0';
+       return out - out_start;
 }
 
-void COM_ToUpperString (const char *in, char *out, size_t size_out)
+size_t COM_ToUpperString(const char *in, char *out, size_t size_out)
 {
+       const char *out_start = out;
+
        if (size_out == 0)
-               return;
+               return 0;
 
        if(utf8_enable.integer)
        {
@@ -1079,12 +1085,12 @@ void COM_ToUpperString (const char *in, char *out, size_t size_out)
                        Uchar ch = u8_getchar_utf8_enabled(in, &in);
                        ch = u8_toupper(ch);
                        n = u8_fromchar(ch, out, size_out);
+                       out += n; // before the break so the return is correct
                        if(n <= 0)
                                break;
-                       out += n;
                        size_out -= n;
                }
-               return;
+               return out - out_start;
        }
 
        while (*in && size_out > 1)
@@ -1096,6 +1102,7 @@ void COM_ToUpperString (const char *in, char *out, size_t size_out)
                size_out--;
        }
        *out = '\0';
+       return out - out_start;
 }
 
 int COM_StringBeginsWith(const char *s, const char *match)
@@ -1187,7 +1194,7 @@ all characters until the zero terminator.
 ============
 */
 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;
@@ -1249,9 +1256,10 @@ removes color codes from a string.
 If escape_carets is true, the resulting string will be safe for printing. If
 escape_carets is false, the function will just strip color codes (for logging
 for example).
+Returns the number of bytes written to the *out buffer excluding the \0 terminator.
 
-If the output buffer size did not suffice for converting, the function returns
-FALSE. Generally, if escape_carets is false, the output buffer needs
+If the output buffer size did not suffice for converting, the function returns 0.
+Generally, if escape_carets is false, the output buffer needs
 strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)*1.5+2
 bytes. In any case, the function makes sure that the resulting string is
 zero terminated.
@@ -1260,20 +1268,22 @@ For size_in, specify the maximum number of characters from in to use, or 0 to us
 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)
+size_t 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)
+#define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return 0; } } while(0)
+
+       const char *out_start = out;
        const char *end = size_in ? (in + size_in) : NULL;
+
        if(size_out < 1)
-               return false;
+               return 0;
        for(;;)
        {
                switch((in == end) ? 0 : *in)
                {
                        case 0:
-                               *out++ = 0;
-                               return true;
+                               *out = '\0';
+                               return out - out_start;
                        case STRING_COLOR_TAG:
                                ++in;
                                switch((in == end) ? 0 : *in)
@@ -1296,8 +1306,8 @@ COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out,
                                                // finish the code by appending another caret when escaping
                                                if(escape_carets)
                                                        APPEND(STRING_COLOR_TAG);
-                                               *out++ = 0;
-                                               return true;
+                                               *out = '\0';
+                                               return out - out_start;
                                        case STRING_COLOR_TAG: // escaped ^
                                                APPEND(STRING_COLOR_TAG);
                                                // append a ^ twice when escaping
@@ -1323,230 +1333,90 @@ COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out,
 #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.
+       #undef memccpy
+       #define memccpy _memccpy
+#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 %lu bytes: \"%s\"\n", __func__, (unsigned long)(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 slen)
 {
-       int i;
-       char key[MAX_INPUTLINE];
-       char value[MAX_INPUTLINE];
-       while (*buffer)
+       if (slen >= 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");
+               slen = dsize - 1;
+               Con_Printf(CON_WARN "%s: src string truncated to %lu bytes: \"%.*s\"\n", __func__, (unsigned long)slen, (int)slen, src);
        }
+       memcpy(dst, src, slen);
+       dst[slen] = '\0';
+       return &dst[slen];
 }
 
-//========================================================
-// 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 %lu bytes: \"%s\"\n", func, line, (unsigned long)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);
-       }
+       size_t offset;
+       char *p = (char *)memchr(dst, '\0', dsize);
 
-       /* 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 */
+       if (!p)
+               p = dst;
+       offset = p - dst;
+       return dp__strlcpy(p, src, dsize - offset, func, line) + offset;
 }
 
-#endif  // #ifndef HAVE_STRLCPY
+
 
 void FindFraction(double val, int *num, int *denom, int denomMax)
 {
@@ -1584,7 +1454,7 @@ char **XPM_DecodeString(const char *in)
        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, "}"))