2 Copyright (C) 1996-1997 Id Software, Inc.
3 Copyright (C) 2000-2020 DarkPlaces contributors
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 See the GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 // common.c -- misc functions used in client and server
32 cvar_t registered = {CF_CLIENT | CF_SERVER, "registered","0", "indicates if this is running registered quake (whether gfx/pop.lmp was found)"};
33 cvar_t cmdline = {CF_CLIENT | CF_SERVER, "cmdline","0", "contains commandline the engine was launched with"};
35 // FIXME: Find a better place for these.
36 cvar_t cl_playermodel = {CF_CLIENT | CF_SERVER | CF_USERINFO | CF_ARCHIVE, "playermodel", "", "current player model in Nexuiz/Xonotic"};
37 cvar_t cl_playerskin = {CF_CLIENT | CF_SERVER | CF_USERINFO | CF_ARCHIVE, "playerskin", "", "current player skin in Nexuiz/Xonotic"};
39 char com_token[MAX_INPUTLINE];
40 unsigned com_token_len;
42 //===========================================================================
44 void SZ_Clear (sizebuf_t *buf)
49 unsigned char *SZ_GetSpace (sizebuf_t *buf, int length)
53 if (buf->cursize + length > buf->maxsize)
55 if (!buf->allowoverflow)
56 Host_Error ("SZ_GetSpace: overflow without allowoverflow set");
58 if (length > buf->maxsize)
59 Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
61 buf->overflowed = true;
62 Con_Print("SZ_GetSpace: overflow\n");
66 data = buf->data + buf->cursize;
67 buf->cursize += length;
72 void SZ_Write (sizebuf_t *buf, const unsigned char *data, int length)
74 memcpy (SZ_GetSpace(buf,length),data,length);
77 // LadyHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my
78 // attention, it has been eradicated from here, its only (former) use in
81 static const char *hexchar = "0123456789ABCDEF";
82 void Com_HexDumpToConsole(const unsigned char *data, int size)
86 char *cur, *flushpointer;
87 const unsigned char *d;
89 flushpointer = text + 512;
97 *cur++ = hexchar[(i >> 12) & 15];
98 *cur++ = hexchar[(i >> 8) & 15];
99 *cur++ = hexchar[(i >> 4) & 15];
100 *cur++ = hexchar[(i >> 0) & 15];
103 for (j = 0;j < 16;j++)
107 *cur++ = hexchar[(d[j] >> 4) & 15];
108 *cur++ = hexchar[(d[j] >> 0) & 15];
119 for (j = 0;j < 16;j++)
123 // color change prefix character has to be treated specially
124 if (d[j] == STRING_COLOR_TAG)
126 *cur++ = STRING_COLOR_TAG;
127 *cur++ = STRING_COLOR_TAG;
129 else if (d[j] >= (unsigned char) ' ')
139 if (cur >= flushpointer || i >= size)
148 void SZ_HexDumpToConsole(const sizebuf_t *buf)
150 Com_HexDumpToConsole(buf->data, buf->cursize);
154 //============================================================================
160 Word wraps a string. The wordWidth function is guaranteed to be called exactly
161 once for each word in the string, so it may be stateful, no idea what that
162 would be good for any more. At the beginning of the string, it will be called
163 for the char 0 to initialize a clean state, and then once with the string " "
164 (a space) so the routine knows how long a space is.
166 In case no single character fits into the given width, the wordWidth function
167 must return the width of exactly one character.
169 Wrapped lines get the isContinuation flag set and are continuationWidth less wide.
171 The sum of the return values of the processLine function will be returned.
174 int COM_Wordwrap(const char *string, size_t length, float continuationWidth, float maxWidth, COM_WordWidthFunc_t wordWidth, void *passthroughCW, COM_LineProcessorFunc processLine, void *passthroughPL)
176 // Logic is as follows:
178 // For each word or whitespace:
179 // Newline found? Output current line, advance to next line. This is not a continuation. Continue.
180 // Space found? Always add it to the current line, no matter if it fits.
181 // Word found? Check if current line + current word fits.
182 // If it fits, append it. Continue.
183 // If it doesn't fit, output current line, advance to next line. Append the word. This is a continuation. Continue.
185 qbool isContinuation = false;
187 const char *startOfLine = string;
188 const char *cursor = string;
189 const char *end = string + length;
190 float spaceUsedInLine = 0;
191 float spaceUsedForWord;
197 wordWidth(passthroughCW, NULL, &dummy, -1);
199 spaceWidth = wordWidth(passthroughCW, " ", &dummy, -1);
203 char ch = (cursor < end) ? *cursor : 0;
206 case 0: // end of string
207 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
209 case '\n': // end of line
210 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
211 isContinuation = false;
213 startOfLine = cursor;
217 spaceUsedInLine += spaceWidth;
221 while(cursor + wordLen < end)
223 switch(cursor[wordLen])
235 spaceUsedForWord = wordWidth(passthroughCW, cursor, &wordLen, maxWidth - continuationWidth); // this may have reduced wordLen when it won't fit - but this is GOOD. TODO fix words that do fit in a non-continuation line
236 if(wordLen < 1) // cannot happen according to current spec of wordWidth
239 spaceUsedForWord = maxWidth + 1; // too high, forces it in a line of itself
241 if(spaceUsedInLine + spaceUsedForWord <= maxWidth || cursor == startOfLine)
243 // we can simply append it
245 spaceUsedInLine += spaceUsedForWord;
249 // output current line
250 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
251 isContinuation = true;
252 startOfLine = cursor;
254 spaceUsedInLine = continuationWidth + spaceUsedForWord;
263 qbool isContinuation = false;
264 float currentWordSpace = 0;
265 const char *currentWord = 0;
266 float minReserve = 0;
268 float spaceUsedInLine = 0;
269 const char *currentLine = 0;
270 const char *currentLineEnd = 0;
271 float currentLineFinalWhitespace = 0;
275 minReserve = charWidth(passthroughCW, 0);
276 minReserve += charWidth(passthroughCW, ' ');
278 if(maxWidth < continuationWidth + minReserve)
279 maxWidth = continuationWidth + minReserve;
281 charWidth(passthroughCW, 0);
283 for(p = string; p < string + length; ++p)
286 float w = charWidth(passthroughCW, c);
291 currentWordSpace = 0;
297 spaceUsedInLine = isContinuation ? continuationWidth : 0;
303 // 1. I can add the word AND a space - then just append it.
304 if(spaceUsedInLine + currentWordSpace + w <= maxWidth)
306 currentLineEnd = p; // note: space not included here
307 currentLineFinalWhitespace = w;
308 spaceUsedInLine += currentWordSpace + w;
310 // 2. I can just add the word - then append it, output current line and go to next one.
311 else if(spaceUsedInLine + currentWordSpace <= maxWidth)
313 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
315 isContinuation = true;
317 // 3. Otherwise, output current line and go to next one, where I can add the word.
318 else if(continuationWidth + currentWordSpace + w <= maxWidth)
321 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
322 currentLine = currentWord;
323 spaceUsedInLine = continuationWidth + currentWordSpace + w;
325 currentLineFinalWhitespace = w;
326 isContinuation = true;
328 // 4. We can't even do that? Then output both current and next word as new lines.
333 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
334 isContinuation = true;
336 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
338 isContinuation = true;
344 // 1. I can add the word - then do it.
345 if(spaceUsedInLine + currentWordSpace <= maxWidth)
347 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
349 // 2. Otherwise, output current line, next one and make tabula rasa.
354 processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
355 isContinuation = true;
357 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
361 isContinuation = false;
365 currentWordSpace += w;
367 spaceUsedInLine + currentWordSpace > maxWidth // can't join this line...
369 continuationWidth + currentWordSpace > maxWidth // can't join any other line...
372 // this word cannot join ANY line...
373 // so output the current line...
376 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
377 isContinuation = true;
380 // then this word's beginning...
383 // it may not fit, but we know we have to split it into maxWidth - continuationWidth pieces
384 float pieceWidth = maxWidth - continuationWidth;
385 const char *pos = currentWord;
386 currentWordSpace = 0;
388 // reset the char width function to a state where no kerning occurs (start of word)
389 charWidth(passthroughCW, ' ');
392 float w = charWidth(passthroughCW, *pos);
393 if(currentWordSpace + w > pieceWidth) // this piece won't fit any more
395 // print everything until it
396 result += processLine(passthroughPL, currentWord, pos - currentWord, currentWordSpace, true);
399 currentWordSpace = 0;
401 currentWordSpace += w;
404 // now we have a currentWord that fits... set up its next line
405 // currentWordSpace has been set
406 // currentWord has been set
407 spaceUsedInLine = continuationWidth;
408 currentLine = currentWord;
410 isContinuation = true;
414 // we have a guarantee that it will fix (see if clause)
415 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace - w, isContinuation);
417 // and use the rest of this word as new start of a line
418 currentWordSpace = w;
420 spaceUsedInLine = continuationWidth;
423 isContinuation = true;
432 currentWordSpace = 0;
435 if(currentLine) // Same procedure as \n
437 // Can I append the current word?
438 if(spaceUsedInLine + currentWordSpace <= maxWidth)
439 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
444 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
445 isContinuation = true;
447 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
457 COM_ParseToken_Simple
459 Parse a token out of a string
460 Writes the token and its strlen to the com_token and com_token_len globals.
463 qbool COM_ParseToken_Simple(const char **datapointer, qbool returnnewline, qbool parsebackslash, qbool parsecomments)
467 const char *data = *datapointer;
469 com_token_len = len = 0;
484 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
494 // handle Windows line ending
495 if (data[0] == '\r' && data[1] == '\n')
498 if (parsecomments && data[0] == '/' && data[1] == '/')
501 while (*data && *data != '\n' && *data != '\r')
505 else if (parsecomments && data[0] == '/' && data[1] == '*')
509 while (*data && (data[0] != '*' || data[1] != '/'))
517 else if (*data == '\"')
520 for (data++;*data && *data != '\"';data++)
523 if (*data == '\\' && parsebackslash)
532 if (len < (int)sizeof(com_token) - 1)
533 com_token[len++] = c;
535 com_token[len] = '\0';
542 else if (*data == '\r')
544 // translate Mac line ending to UNIX
545 com_token[len++] = '\n';data++;
546 com_token[len] = '\0';
551 else if (*data == '\n')
554 com_token[len++] = *data++;
555 com_token[len] = '\0';
563 for (;!ISWHITESPACE(*data);data++)
564 if (len < (int)sizeof(com_token) - 1)
565 com_token[len++] = *data;
566 com_token[len] = '\0';
575 COM_ParseToken_QuakeC
577 Parse a token out of a string
578 Writes the token and its strlen to the com_token and com_token_len globals.
581 qbool COM_ParseToken_QuakeC(const char **datapointer, qbool returnnewline)
585 const char *data = *datapointer;
587 com_token_len = len = 0;
602 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
612 // handle Windows line ending
613 if (data[0] == '\r' && data[1] == '\n')
616 if (data[0] == '/' && data[1] == '/')
619 while (*data && *data != '\n' && *data != '\r')
623 else if (data[0] == '/' && data[1] == '*')
627 while (*data && (data[0] != '*' || data[1] != '/'))
635 else if (*data == '\"' || *data == '\'')
639 for (data++;*data && *data != quote;data++)
651 if (len < (int)sizeof(com_token) - 1)
652 com_token[len++] = c;
654 com_token[len] = '\0';
661 else if (*data == '\r')
663 // translate Mac line ending to UNIX
664 com_token[len++] = '\n';data++;
665 com_token[len] = '\0';
670 else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
673 com_token[len++] = *data++;
674 com_token[len] = '\0';
682 for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
683 if (len < (int)sizeof(com_token) - 1)
684 com_token[len++] = *data;
685 com_token[len] = '\0';
694 COM_ParseToken_VM_Tokenize
696 Parse a token out of a string
697 Writes the token and its strlen to the com_token and com_token_len globals.
700 qbool COM_ParseToken_VM_Tokenize(const char **datapointer, qbool returnnewline)
704 const char *data = *datapointer;
706 com_token_len = len = 0;
721 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
731 // handle Windows line ending
732 if (data[0] == '\r' && data[1] == '\n')
735 if (data[0] == '/' && data[1] == '/')
738 while (*data && *data != '\n' && *data != '\r')
742 else if (data[0] == '/' && data[1] == '*')
746 while (*data && (data[0] != '*' || data[1] != '/'))
754 else if (*data == '\"' || *data == '\'')
758 for (data++;*data && *data != quote;data++)
770 if (len < (int)sizeof(com_token) - 1)
771 com_token[len++] = c;
773 com_token[len] = '\0';
780 else if (*data == '\r')
782 // translate Mac line ending to UNIX
783 com_token[len++] = '\n';data++;
784 com_token[len] = '\0';
789 else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
792 com_token[len++] = *data++;
793 com_token[len] = '\0';
801 for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
802 if (len < (int)sizeof(com_token) - 1)
803 com_token[len++] = *data;
804 com_token[len] = '\0';
813 COM_ParseToken_Console
815 Parse a token out of a string, behaving like the qwcl console
816 Writes the token and its strlen to the com_token and com_token_len globals.
819 qbool COM_ParseToken_Console(const char **datapointer)
822 const char *data = *datapointer;
824 com_token_len = len = 0;
835 for (;ISWHITESPACE(*data);data++)
845 if (*data == '/' && data[1] == '/')
848 while (*data && *data != '\n' && *data != '\r')
852 else if (*data == '\"')
855 for (data++;*data && *data != '\"';data++)
857 // allow escaped " and \ case
858 if (*data == '\\' && (data[1] == '\"' || data[1] == '\\'))
860 if (len < (int)sizeof(com_token) - 1)
861 com_token[len++] = *data;
863 com_token[len] = '\0';
872 for (;!ISWHITESPACE(*data);data++)
873 if (len < (int)sizeof(com_token) - 1)
874 com_token[len++] = *data;
875 com_token[len] = '\0';
887 Used by view and sv_user
890 float Com_CalcRoll (const vec3_t angles, const vec3_t velocity, const vec_t angleval, const vec_t velocityval)
892 vec3_t forward, right, up;
896 AngleVectors (angles, forward, right, up);
897 side = DotProduct (velocity, right);
898 sign = side < 0 ? -1 : 1;
901 if (side < velocityval)
902 side = side * angleval / velocityval;
910 //===========================================================================
917 void COM_Init_Commands (void)
920 char com_cmdline[MAX_INPUTLINE];
922 Cvar_RegisterVariable (®istered);
923 Cvar_RegisterVariable (&cmdline);
924 Cvar_RegisterVariable(&cl_playermodel);
925 Cvar_RegisterVirtual(&cl_playermodel, "_cl_playermodel");
926 Cvar_RegisterVariable(&cl_playerskin);
927 Cvar_RegisterVirtual(&cl_playerskin, "_cl_playerskin");
929 // reconstitute the command line for the cmdline externally visible cvar
931 for (j = 0;(j < MAX_NUM_ARGVS) && (j < sys.argc);j++)
934 if (strstr(sys.argv[j], " "))
936 // arg contains whitespace, store quotes around it
937 // This condition checks whether we can allow to put
938 // in two quote characters.
939 if (n >= ((int)sizeof(com_cmdline) - 2))
941 com_cmdline[n++] = '\"';
942 // This condition checks whether we can allow one
943 // more character and a quote character.
944 while ((n < ((int)sizeof(com_cmdline) - 2)) && sys.argv[j][i])
945 // FIXME: Doesn't quote special characters.
946 com_cmdline[n++] = sys.argv[j][i++];
947 com_cmdline[n++] = '\"';
951 // This condition checks whether we can allow one
953 while ((n < ((int)sizeof(com_cmdline) - 1)) && sys.argv[j][i])
954 com_cmdline[n++] = sys.argv[j][i++];
956 if (n < ((int)sizeof(com_cmdline) - 1))
957 com_cmdline[n++] = ' ';
962 Cvar_SetQuick(&cmdline, com_cmdline);
969 varargs print into provided buffer, returns buffer (so that it can be called in-line, unlike dpsnprintf)
972 char *va(char *buf, size_t buflen, const char *format, ...)
976 va_start (argptr, format);
977 dpvsnprintf (buf, buflen, format,argptr);
984 //======================================
986 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
992 # define snprintf _snprintf
993 # define vsnprintf _vsnprintf
997 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1002 va_start (args, format);
1003 result = dpvsnprintf (buffer, buffersize, format, args);
1010 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1014 #if _MSC_VER >= 1400
1015 result = _vsnprintf_s (buffer, buffersize, _TRUNCATE, format, args);
1017 result = vsnprintf (buffer, buffersize, format, args);
1019 if (result < 0 || (size_t)result >= buffersize)
1021 buffer[buffersize - 1] = '\0';
1022 // we could be inside Con_Printf
1024 Sys_Printf("dpvsnprintf: output error, buffer size %lu\n", (unsigned long)buffersize);
1026 Sys_Printf("dpvsnprintf: truncated to %lu bytes: \"%s\"\n", (unsigned long)buffersize - 1, buffer);
1034 //======================================
1036 size_t COM_ToLowerString(const char *in, char *out, size_t size_out)
1038 const char *out_start = out;
1043 if(utf8_enable.integer)
1046 while(*in && size_out > 1)
1049 Uchar ch = u8_getchar_utf8_enabled(in, &in);
1050 ch = u8_tolower(ch);
1051 n = u8_fromchar(ch, out, size_out);
1052 out += n; // before the break so the return is correct
1057 return out - out_start;
1060 while (*in && size_out > 1)
1062 if (*in >= 'A' && *in <= 'Z')
1063 *out++ = *in++ + 'a' - 'A';
1069 return out - out_start;
1072 size_t COM_ToUpperString(const char *in, char *out, size_t size_out)
1074 const char *out_start = out;
1079 if(utf8_enable.integer)
1082 while(*in && size_out > 1)
1085 Uchar ch = u8_getchar_utf8_enabled(in, &in);
1086 ch = u8_toupper(ch);
1087 n = u8_fromchar(ch, out, size_out);
1088 out += n; // before the break so the return is correct
1093 return out - out_start;
1096 while (*in && size_out > 1)
1098 if (*in >= 'a' && *in <= 'z')
1099 *out++ = *in++ + 'A' - 'a';
1105 return out - out_start;
1108 int COM_StringBeginsWith(const char *s, const char *match)
1110 for (;*s && *match;s++, match++)
1116 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1118 int argc, commentprefixlength;
1122 tokenbufend = tokenbuf + tokenbufsize;
1124 commentprefixlength = 0;
1126 commentprefixlength = (int)strlen(commentprefix);
1127 while (*l && *l != '\n' && *l != '\r')
1129 if (!ISWHITESPACE(*l))
1131 if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1133 while (*l && *l != '\n' && *l != '\r')
1137 if (argc >= maxargc)
1139 argv[argc++] = tokenbuf;
1143 while (*l && *l != '"')
1145 if (tokenbuf >= tokenbufend)
1154 while (!ISWHITESPACE(*l))
1156 if (tokenbuf >= tokenbufend)
1161 if (tokenbuf >= tokenbufend)
1182 COM_StringLengthNoColors
1184 calculates the visible width of a color coded string.
1186 *valid is filled with TRUE if the string is a valid colored string (that is, if
1187 it does not end with an unfinished color code). If it gets filled with FALSE, a
1188 fix would be adding a STRING_COLOR_TAG at the end of the string.
1190 valid can be set to NULL if the caller doesn't care.
1192 For size_s, specify the maximum number of characters from s to use, or 0 to use
1193 all characters until the zero terminator.
1197 COM_StringLengthNoColors(const char *s, size_t size_s, qbool *valid)
1199 const char *end = size_s ? (s + size_s) : NULL;
1203 switch((s == end) ? 0 : *s)
1209 case STRING_COLOR_TAG:
1211 switch((s == end) ? 0 : *s)
1213 case STRING_COLOR_RGB_TAG_CHAR:
1214 if (s+1 != end && isxdigit(s[1]) &&
1215 s+2 != end && isxdigit(s[2]) &&
1216 s+3 != end && isxdigit(s[3]) )
1221 ++len; // STRING_COLOR_TAG
1222 ++len; // STRING_COLOR_RGB_TAG_CHAR
1224 case 0: // ends with unfinished color code!
1229 case STRING_COLOR_TAG: // escaped ^
1232 case '0': case '1': case '2': case '3': case '4':
1233 case '5': case '6': case '7': case '8': case '9': // color code
1235 default: // not a color code
1236 ++len; // STRING_COLOR_TAG
1237 ++len; // the character
1252 COM_StringDecolorize
1254 removes color codes from a string.
1256 If escape_carets is true, the resulting string will be safe for printing. If
1257 escape_carets is false, the function will just strip color codes (for logging
1259 Returns the number of bytes written to the *out buffer excluding the \0 terminator.
1261 If the output buffer size did not suffice for converting, the function returns 0.
1262 Generally, if escape_carets is false, the output buffer needs
1263 strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)*1.5+2
1264 bytes. In any case, the function makes sure that the resulting string is
1267 For size_in, specify the maximum number of characters from in to use, or 0 to use
1268 all characters until the zero terminator.
1271 size_t COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qbool escape_carets)
1273 #define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return 0; } } while(0)
1275 const char *out_start = out;
1276 const char *end = size_in ? (in + size_in) : NULL;
1282 switch((in == end) ? 0 : *in)
1286 return out - out_start;
1287 case STRING_COLOR_TAG:
1289 switch((in == end) ? 0 : *in)
1291 case STRING_COLOR_RGB_TAG_CHAR:
1292 if (in+1 != end && isxdigit(in[1]) &&
1293 in+2 != end && isxdigit(in[2]) &&
1294 in+3 != end && isxdigit(in[3]) )
1299 APPEND(STRING_COLOR_TAG);
1301 APPEND(STRING_COLOR_TAG);
1302 APPEND(STRING_COLOR_RGB_TAG_CHAR);
1304 case 0: // ends with unfinished color code!
1305 APPEND(STRING_COLOR_TAG);
1306 // finish the code by appending another caret when escaping
1308 APPEND(STRING_COLOR_TAG);
1310 return out - out_start;
1311 case STRING_COLOR_TAG: // escaped ^
1312 APPEND(STRING_COLOR_TAG);
1313 // append a ^ twice when escaping
1315 APPEND(STRING_COLOR_TAG);
1317 case '0': case '1': case '2': case '3': case '4':
1318 case '5': case '6': case '7': case '8': case '9': // color code
1320 default: // not a color code
1321 APPEND(STRING_COLOR_TAG);
1341 The glibc implementation of memccpy is several times faster than old functions that
1342 copy one byte at a time (even at -O3) and its advantage increases with string length.
1346 // memccpy() is standard in POSIX.1-2001, POSIX.1-2008, SVr4, 4.3BSD, C23.
1347 // Microsoft supports it, but apparently complains if we use it.
1349 #define memccpy _memccpy
1352 /** Chain-copies a string with truncation and efficiency (compared to strlcat()).
1353 * The destination ends at an absolute pointer instead of a relative offset
1354 * and a pointer to the \0 terminator is returned on success.
1355 * Truncates, warns, and returns the end pointer on overflow or unterminated source.
1356 * Guarantees \0 termination. end = dst + sizeof(src[])
1358 char *dp_stpecpy(char *dst, char *end, const char *src)
1360 char *p = (char *)memccpy(dst, src, '\0', end - dst);
1365 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);
1369 /** Copies a measured byte sequence (unterminated string) to a null-terminated string.
1370 * Returns a pointer to the \0 terminator. Guarantees \0 termination.
1371 * Compared to ustr2stp(): truncates and warns on overflow.
1373 char *dp_ustr2stp(char *dst, size_t dsize, const char *src, size_t slen)
1378 Con_Printf(CON_WARN "%s: src string truncated to %lu bytes: \"%.*s\"\n", __func__, (unsigned long)slen, (int)slen, src);
1380 memcpy(dst, src, slen);
1385 /** Copies a string, like strlcpy() but with a better return: the number of bytes copied
1386 * excluding the \0 terminator. Truncates and warns on overflow or unterminated source,
1387 * whereas strlcpy() truncates silently and overreads (possibly segfaulting).
1388 * Guarantees \0 termination.
1389 * See also: dp_stpecpy() and dp_ustr2stp().
1391 size_t dp__strlcpy(char *dst, const char *src, size_t dsize, const char *func, unsigned line)
1393 char *p = (char *)memccpy(dst, src, '\0', dsize);
1396 return (p - 1) - dst;
1397 dst[dsize - 1] = '\0';
1398 Con_Printf(CON_WARN "%s:%u: src string unterminated or truncated to %lu bytes: \"%s\"\n", func, line, (unsigned long)dsize - 1, dst);
1402 /** Catenates a string, like strlcat() but with a better return: the number of bytes copied
1403 * excluding the \0 terminator. Truncates and warns on overflow or unterminated source,
1404 * whereas strlcat() truncates silently and overreads (possibly segfaulting).
1405 * Guarantees \0 termination.
1406 * Inefficient like any strcat(), please use memcpy(), dp_stpecpy() or dp_strlcpy() instead.
1408 size_t dp__strlcat(char *dst, const char *src, size_t dsize, const char *func, unsigned line)
1411 char *p = (char *)memchr(dst, '\0', dsize);
1416 return dp__strlcpy(p, src, dsize - offset, func, line) + offset;
1421 void FindFraction(double val, int *num, int *denom, int denomMax)
1426 bestdiff = fabs(val);
1430 for(i = 1; i <= denomMax; ++i)
1432 int inum = (int) floor(0.5 + val * i);
1433 double diff = fabs(val - inum / (double)i);
1443 // decodes an XPM from C syntax
1444 char **XPM_DecodeString(const char *in)
1446 static char *tokens[257];
1447 static char lines[257][512];
1450 // skip until "{" token
1451 while(COM_ParseToken_QuakeC(&in, false) && strcmp(com_token, "{"));
1453 // now, read in succession: string, comma-or-}
1454 while(COM_ParseToken_QuakeC(&in, false))
1456 tokens[line] = lines[line];
1457 dp_strlcpy(lines[line++], com_token, sizeof(lines[0]));
1458 if(!COM_ParseToken_QuakeC(&in, false))
1460 if(!strcmp(com_token, "}"))
1462 if(strcmp(com_token, ","))
1464 if(line >= sizeof(tokens) / sizeof(tokens[0]))
1471 static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1472 static void base64_3to4(const unsigned char *in, unsigned char *out, int bytes)
1474 unsigned char i0 = (bytes > 0) ? in[0] : 0;
1475 unsigned char i1 = (bytes > 1) ? in[1] : 0;
1476 unsigned char i2 = (bytes > 2) ? in[2] : 0;
1477 unsigned char o0 = base64[i0 >> 2];
1478 unsigned char o1 = base64[((i0 << 4) | (i1 >> 4)) & 077];
1479 unsigned char o2 = base64[((i1 << 2) | (i2 >> 6)) & 077];
1480 unsigned char o3 = base64[i2 & 077];
1481 out[0] = (bytes > 0) ? o0 : '?';
1482 out[1] = (bytes > 0) ? o1 : '?';
1483 out[2] = (bytes > 1) ? o2 : '=';
1484 out[3] = (bytes > 2) ? o3 : '=';
1487 size_t base64_encode(unsigned char *buf, size_t buflen, size_t outbuflen)
1490 // expand the out-buffer
1491 blocks = (buflen + 2) / 3;
1492 if(blocks*4 > outbuflen)
1494 for(i = blocks; i > 0; )
1497 base64_3to4(buf + 3*i, buf + 4*i, (int)(buflen - 3*i));