2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 // common.c -- misc functions used in client and server
31 cvar_t registered = {CVAR_CLIENT | CVAR_SERVER, "registered","0", "indicates if this is running registered quake (whether gfx/pop.lmp was found)"};
32 cvar_t cmdline = {CVAR_CLIENT | CVAR_SERVER, "cmdline","0", "contains commandline the engine was launched with"};
34 // FIXME: Find a better place for these.
35 cvar_t cl_playermodel = {CVAR_CLIENT | CVAR_SERVER | CVAR_USERINFO | CVAR_SAVE, "playermodel", "", "current player model in Nexuiz/Xonotic"};
36 cvar_t cl_playerskin = {CVAR_CLIENT | CVAR_SERVER | CVAR_USERINFO | CVAR_SAVE, "playerskin", "", "current player skin in Nexuiz/Xonotic"};
38 char com_token[MAX_INPUTLINE];
40 //===========================================================================
42 void SZ_Clear (sizebuf_t *buf)
47 unsigned char *SZ_GetSpace (sizebuf_t *buf, int length)
51 if (buf->cursize + length > buf->maxsize)
53 if (!buf->allowoverflow)
54 Host_Error ("SZ_GetSpace: overflow without allowoverflow set");
56 if (length > buf->maxsize)
57 Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
59 buf->overflowed = true;
60 Con_Print("SZ_GetSpace: overflow\n");
64 data = buf->data + buf->cursize;
65 buf->cursize += length;
70 void SZ_Write (sizebuf_t *buf, const unsigned char *data, int length)
72 memcpy (SZ_GetSpace(buf,length),data,length);
75 // LadyHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my
76 // attention, it has been eradicated from here, its only (former) use in
79 static const char *hexchar = "0123456789ABCDEF";
80 void Com_HexDumpToConsole(const unsigned char *data, int size)
84 char *cur, *flushpointer;
85 const unsigned char *d;
87 flushpointer = text + 512;
95 *cur++ = hexchar[(i >> 12) & 15];
96 *cur++ = hexchar[(i >> 8) & 15];
97 *cur++ = hexchar[(i >> 4) & 15];
98 *cur++ = hexchar[(i >> 0) & 15];
101 for (j = 0;j < 16;j++)
105 *cur++ = hexchar[(d[j] >> 4) & 15];
106 *cur++ = hexchar[(d[j] >> 0) & 15];
117 for (j = 0;j < 16;j++)
121 // color change prefix character has to be treated specially
122 if (d[j] == STRING_COLOR_TAG)
124 *cur++ = STRING_COLOR_TAG;
125 *cur++ = STRING_COLOR_TAG;
127 else if (d[j] >= (unsigned char) ' ')
137 if (cur >= flushpointer || i >= size)
146 void SZ_HexDumpToConsole(const sizebuf_t *buf)
148 Com_HexDumpToConsole(buf->data, buf->cursize);
152 //============================================================================
158 Word wraps a string. The wordWidth function is guaranteed to be called exactly
159 once for each word in the string, so it may be stateful, no idea what that
160 would be good for any more. At the beginning of the string, it will be called
161 for the char 0 to initialize a clean state, and then once with the string " "
162 (a space) so the routine knows how long a space is.
164 In case no single character fits into the given width, the wordWidth function
165 must return the width of exactly one character.
167 Wrapped lines get the isContinuation flag set and are continuationWidth less wide.
169 The sum of the return values of the processLine function will be returned.
172 int COM_Wordwrap(const char *string, size_t length, float continuationWidth, float maxWidth, COM_WordWidthFunc_t wordWidth, void *passthroughCW, COM_LineProcessorFunc processLine, void *passthroughPL)
174 // Logic is as follows:
176 // For each word or whitespace:
177 // Newline found? Output current line, advance to next line. This is not a continuation. Continue.
178 // Space found? Always add it to the current line, no matter if it fits.
179 // Word found? Check if current line + current word fits.
180 // If it fits, append it. Continue.
181 // If it doesn't fit, output current line, advance to next line. Append the word. This is a continuation. Continue.
183 qboolean isContinuation = false;
185 const char *startOfLine = string;
186 const char *cursor = string;
187 const char *end = string + length;
188 float spaceUsedInLine = 0;
189 float spaceUsedForWord;
195 wordWidth(passthroughCW, NULL, &dummy, -1);
197 spaceWidth = wordWidth(passthroughCW, " ", &dummy, -1);
201 char ch = (cursor < end) ? *cursor : 0;
204 case 0: // end of string
205 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
207 case '\n': // end of line
208 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
209 isContinuation = false;
211 startOfLine = cursor;
215 spaceUsedInLine += spaceWidth;
219 while(cursor + wordLen < end)
221 switch(cursor[wordLen])
233 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
234 if(wordLen < 1) // cannot happen according to current spec of wordWidth
237 spaceUsedForWord = maxWidth + 1; // too high, forces it in a line of itself
239 if(spaceUsedInLine + spaceUsedForWord <= maxWidth || cursor == startOfLine)
241 // we can simply append it
243 spaceUsedInLine += spaceUsedForWord;
247 // output current line
248 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
249 isContinuation = true;
250 startOfLine = cursor;
252 spaceUsedInLine = continuationWidth + spaceUsedForWord;
261 qboolean isContinuation = false;
262 float currentWordSpace = 0;
263 const char *currentWord = 0;
264 float minReserve = 0;
266 float spaceUsedInLine = 0;
267 const char *currentLine = 0;
268 const char *currentLineEnd = 0;
269 float currentLineFinalWhitespace = 0;
273 minReserve = charWidth(passthroughCW, 0);
274 minReserve += charWidth(passthroughCW, ' ');
276 if(maxWidth < continuationWidth + minReserve)
277 maxWidth = continuationWidth + minReserve;
279 charWidth(passthroughCW, 0);
281 for(p = string; p < string + length; ++p)
284 float w = charWidth(passthroughCW, c);
289 currentWordSpace = 0;
295 spaceUsedInLine = isContinuation ? continuationWidth : 0;
301 // 1. I can add the word AND a space - then just append it.
302 if(spaceUsedInLine + currentWordSpace + w <= maxWidth)
304 currentLineEnd = p; // note: space not included here
305 currentLineFinalWhitespace = w;
306 spaceUsedInLine += currentWordSpace + w;
308 // 2. I can just add the word - then append it, output current line and go to next one.
309 else if(spaceUsedInLine + currentWordSpace <= maxWidth)
311 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
313 isContinuation = true;
315 // 3. Otherwise, output current line and go to next one, where I can add the word.
316 else if(continuationWidth + currentWordSpace + w <= maxWidth)
319 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
320 currentLine = currentWord;
321 spaceUsedInLine = continuationWidth + currentWordSpace + w;
323 currentLineFinalWhitespace = w;
324 isContinuation = true;
326 // 4. We can't even do that? Then output both current and next word as new lines.
331 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
332 isContinuation = true;
334 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
336 isContinuation = true;
342 // 1. I can add the word - then do it.
343 if(spaceUsedInLine + currentWordSpace <= maxWidth)
345 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
347 // 2. Otherwise, output current line, next one and make tabula rasa.
352 processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
353 isContinuation = true;
355 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
359 isContinuation = false;
363 currentWordSpace += w;
365 spaceUsedInLine + currentWordSpace > maxWidth // can't join this line...
367 continuationWidth + currentWordSpace > maxWidth // can't join any other line...
370 // this word cannot join ANY line...
371 // so output the current line...
374 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
375 isContinuation = true;
378 // then this word's beginning...
381 // it may not fit, but we know we have to split it into maxWidth - continuationWidth pieces
382 float pieceWidth = maxWidth - continuationWidth;
383 const char *pos = currentWord;
384 currentWordSpace = 0;
386 // reset the char width function to a state where no kerning occurs (start of word)
387 charWidth(passthroughCW, ' ');
390 float w = charWidth(passthroughCW, *pos);
391 if(currentWordSpace + w > pieceWidth) // this piece won't fit any more
393 // print everything until it
394 result += processLine(passthroughPL, currentWord, pos - currentWord, currentWordSpace, true);
397 currentWordSpace = 0;
399 currentWordSpace += w;
402 // now we have a currentWord that fits... set up its next line
403 // currentWordSpace has been set
404 // currentWord has been set
405 spaceUsedInLine = continuationWidth;
406 currentLine = currentWord;
408 isContinuation = true;
412 // we have a guarantee that it will fix (see if clause)
413 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace - w, isContinuation);
415 // and use the rest of this word as new start of a line
416 currentWordSpace = w;
418 spaceUsedInLine = continuationWidth;
421 isContinuation = true;
430 currentWordSpace = 0;
433 if(currentLine) // Same procedure as \n
435 // Can I append the current word?
436 if(spaceUsedInLine + currentWordSpace <= maxWidth)
437 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
442 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
443 isContinuation = true;
445 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
455 COM_ParseToken_Simple
457 Parse a token out of a string
460 int COM_ParseToken_Simple(const char **datapointer, qboolean returnnewline, qboolean parsebackslash, qboolean parsecomments)
464 const char *data = *datapointer;
481 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
491 // handle Windows line ending
492 if (data[0] == '\r' && data[1] == '\n')
495 if (parsecomments && data[0] == '/' && data[1] == '/')
498 while (*data && *data != '\n' && *data != '\r')
502 else if (parsecomments && data[0] == '/' && data[1] == '*')
506 while (*data && (data[0] != '*' || data[1] != '/'))
514 else if (*data == '\"')
517 for (data++;*data && *data != '\"';data++)
520 if (*data == '\\' && parsebackslash)
529 if (len < (int)sizeof(com_token) - 1)
530 com_token[len++] = c;
538 else if (*data == '\r')
540 // translate Mac line ending to UNIX
541 com_token[len++] = '\n';data++;
546 else if (*data == '\n')
549 com_token[len++] = *data++;
557 for (;!ISWHITESPACE(*data);data++)
558 if (len < (int)sizeof(com_token) - 1)
559 com_token[len++] = *data;
568 COM_ParseToken_QuakeC
570 Parse a token out of a string
573 int COM_ParseToken_QuakeC(const char **datapointer, qboolean returnnewline)
577 const char *data = *datapointer;
594 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
604 // handle Windows line ending
605 if (data[0] == '\r' && data[1] == '\n')
608 if (data[0] == '/' && data[1] == '/')
611 while (*data && *data != '\n' && *data != '\r')
615 else if (data[0] == '/' && data[1] == '*')
619 while (*data && (data[0] != '*' || data[1] != '/'))
627 else if (*data == '\"' || *data == '\'')
631 for (data++;*data && *data != quote;data++)
643 if (len < (int)sizeof(com_token) - 1)
644 com_token[len++] = c;
652 else if (*data == '\r')
654 // translate Mac line ending to UNIX
655 com_token[len++] = '\n';data++;
660 else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
663 com_token[len++] = *data++;
671 for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
672 if (len < (int)sizeof(com_token) - 1)
673 com_token[len++] = *data;
682 COM_ParseToken_VM_Tokenize
684 Parse a token out of a string
687 int COM_ParseToken_VM_Tokenize(const char **datapointer, qboolean returnnewline)
691 const char *data = *datapointer;
708 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
718 // handle Windows line ending
719 if (data[0] == '\r' && data[1] == '\n')
722 if (data[0] == '/' && data[1] == '/')
725 while (*data && *data != '\n' && *data != '\r')
729 else if (data[0] == '/' && data[1] == '*')
733 while (*data && (data[0] != '*' || data[1] != '/'))
741 else if (*data == '\"' || *data == '\'')
745 for (data++;*data && *data != quote;data++)
757 if (len < (int)sizeof(com_token) - 1)
758 com_token[len++] = c;
766 else if (*data == '\r')
768 // translate Mac line ending to UNIX
769 com_token[len++] = '\n';data++;
774 else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
777 com_token[len++] = *data++;
785 for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
786 if (len < (int)sizeof(com_token) - 1)
787 com_token[len++] = *data;
796 COM_ParseToken_Console
798 Parse a token out of a string, behaving like the qwcl console
801 int COM_ParseToken_Console(const char **datapointer)
804 const char *data = *datapointer;
817 for (;ISWHITESPACE(*data);data++)
827 if (*data == '/' && data[1] == '/')
830 while (*data && *data != '\n' && *data != '\r')
834 else if (*data == '\"')
837 for (data++;*data && *data != '\"';data++)
839 // allow escaped " and \ case
840 if (*data == '\\' && (data[1] == '\"' || data[1] == '\\'))
842 if (len < (int)sizeof(com_token) - 1)
843 com_token[len++] = *data;
853 for (;!ISWHITESPACE(*data);data++)
854 if (len < (int)sizeof(com_token) - 1)
855 com_token[len++] = *data;
868 Returns the position (1 to argc-1) in the program's argument list
869 where the given parameter apears, or 0 if not present
872 int COM_CheckParm (const char *parm)
876 for (i=1 ; i<sys.argc ; i++)
879 continue; // NEXTSTEP sometimes clears appkit vars.
880 if (!strcmp (parm,sys.argv[i]))
887 //===========================================================================
894 void COM_Init_Commands (void)
897 char com_cmdline[MAX_INPUTLINE];
899 Cvar_RegisterVariable (®istered);
900 Cvar_RegisterVariable (&cmdline);
901 Cvar_RegisterVariable(&cl_playermodel);
902 Cvar_RegisterAlias(&cl_playermodel, "_cl_playermodel");
903 Cvar_RegisterVariable(&cl_playerskin);
904 Cvar_RegisterAlias(&cl_playerskin, "_cl_playerskin");
906 // reconstitute the command line for the cmdline externally visible cvar
908 for (j = 0;(j < MAX_NUM_ARGVS) && (j < sys.argc);j++)
911 if (strstr(sys.argv[j], " "))
913 // arg contains whitespace, store quotes around it
914 // This condition checks whether we can allow to put
915 // in two quote characters.
916 if (n >= ((int)sizeof(com_cmdline) - 2))
918 com_cmdline[n++] = '\"';
919 // This condition checks whether we can allow one
920 // more character and a quote character.
921 while ((n < ((int)sizeof(com_cmdline) - 2)) && sys.argv[j][i])
922 // FIXME: Doesn't quote special characters.
923 com_cmdline[n++] = sys.argv[j][i++];
924 com_cmdline[n++] = '\"';
928 // This condition checks whether we can allow one
930 while ((n < ((int)sizeof(com_cmdline) - 1)) && sys.argv[j][i])
931 com_cmdline[n++] = sys.argv[j][i++];
933 if (n < ((int)sizeof(com_cmdline) - 1))
934 com_cmdline[n++] = ' ';
939 Cvar_SetQuick(&cmdline, com_cmdline);
946 varargs print into provided buffer, returns buffer (so that it can be called in-line, unlike dpsnprintf)
949 char *va(char *buf, size_t buflen, const char *format, ...)
953 va_start (argptr, format);
954 dpvsnprintf (buf, buflen, format,argptr);
961 //======================================
963 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
969 # define snprintf _snprintf
970 # define vsnprintf _vsnprintf
974 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
979 va_start (args, format);
980 result = dpvsnprintf (buffer, buffersize, format, args);
987 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
992 result = _vsnprintf_s (buffer, buffersize, _TRUNCATE, format, args);
994 result = vsnprintf (buffer, buffersize, format, args);
996 if (result < 0 || (size_t)result >= buffersize)
998 buffer[buffersize - 1] = '\0';
1006 //======================================
1008 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1013 if(utf8_enable.integer)
1016 while(*in && size_out > 1)
1019 Uchar ch = u8_getchar_utf8_enabled(in, &in);
1020 ch = u8_tolower(ch);
1021 n = u8_fromchar(ch, out, size_out);
1030 while (*in && size_out > 1)
1032 if (*in >= 'A' && *in <= 'Z')
1033 *out++ = *in++ + 'a' - 'A';
1041 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1046 if(utf8_enable.integer)
1049 while(*in && size_out > 1)
1052 Uchar ch = u8_getchar_utf8_enabled(in, &in);
1053 ch = u8_toupper(ch);
1054 n = u8_fromchar(ch, out, size_out);
1063 while (*in && size_out > 1)
1065 if (*in >= 'a' && *in <= 'z')
1066 *out++ = *in++ + 'A' - 'a';
1074 int COM_StringBeginsWith(const char *s, const char *match)
1076 for (;*s && *match;s++, match++)
1082 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1084 int argc, commentprefixlength;
1088 tokenbufend = tokenbuf + tokenbufsize;
1090 commentprefixlength = 0;
1092 commentprefixlength = (int)strlen(commentprefix);
1093 while (*l && *l != '\n' && *l != '\r')
1095 if (!ISWHITESPACE(*l))
1097 if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1099 while (*l && *l != '\n' && *l != '\r')
1103 if (argc >= maxargc)
1105 argv[argc++] = tokenbuf;
1109 while (*l && *l != '"')
1111 if (tokenbuf >= tokenbufend)
1120 while (!ISWHITESPACE(*l))
1122 if (tokenbuf >= tokenbufend)
1127 if (tokenbuf >= tokenbufend)
1148 COM_StringLengthNoColors
1150 calculates the visible width of a color coded string.
1152 *valid is filled with TRUE if the string is a valid colored string (that is, if
1153 it does not end with an unfinished color code). If it gets filled with FALSE, a
1154 fix would be adding a STRING_COLOR_TAG at the end of the string.
1156 valid can be set to NULL if the caller doesn't care.
1158 For size_s, specify the maximum number of characters from s to use, or 0 to use
1159 all characters until the zero terminator.
1163 COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
1165 const char *end = size_s ? (s + size_s) : NULL;
1169 switch((s == end) ? 0 : *s)
1175 case STRING_COLOR_TAG:
1177 switch((s == end) ? 0 : *s)
1179 case STRING_COLOR_RGB_TAG_CHAR:
1180 if (s+1 != end && isxdigit(s[1]) &&
1181 s+2 != end && isxdigit(s[2]) &&
1182 s+3 != end && isxdigit(s[3]) )
1187 ++len; // STRING_COLOR_TAG
1188 ++len; // STRING_COLOR_RGB_TAG_CHAR
1190 case 0: // ends with unfinished color code!
1195 case STRING_COLOR_TAG: // escaped ^
1198 case '0': case '1': case '2': case '3': case '4':
1199 case '5': case '6': case '7': case '8': case '9': // color code
1201 default: // not a color code
1202 ++len; // STRING_COLOR_TAG
1203 ++len; // the character
1218 COM_StringDecolorize
1220 removes color codes from a string.
1222 If escape_carets is true, the resulting string will be safe for printing. If
1223 escape_carets is false, the function will just strip color codes (for logging
1226 If the output buffer size did not suffice for converting, the function returns
1227 FALSE. Generally, if escape_carets is false, the output buffer needs
1228 strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)*1.5+2
1229 bytes. In any case, the function makes sure that the resulting string is
1232 For size_in, specify the maximum number of characters from in to use, or 0 to use
1233 all characters until the zero terminator.
1237 COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qboolean escape_carets)
1239 #define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return false; } } while(0)
1240 const char *end = size_in ? (in + size_in) : NULL;
1245 switch((in == end) ? 0 : *in)
1250 case STRING_COLOR_TAG:
1252 switch((in == end) ? 0 : *in)
1254 case STRING_COLOR_RGB_TAG_CHAR:
1255 if (in+1 != end && isxdigit(in[1]) &&
1256 in+2 != end && isxdigit(in[2]) &&
1257 in+3 != end && isxdigit(in[3]) )
1262 APPEND(STRING_COLOR_TAG);
1264 APPEND(STRING_COLOR_TAG);
1265 APPEND(STRING_COLOR_RGB_TAG_CHAR);
1267 case 0: // ends with unfinished color code!
1268 APPEND(STRING_COLOR_TAG);
1269 // finish the code by appending another caret when escaping
1271 APPEND(STRING_COLOR_TAG);
1274 case STRING_COLOR_TAG: // escaped ^
1275 APPEND(STRING_COLOR_TAG);
1276 // append a ^ twice when escaping
1278 APPEND(STRING_COLOR_TAG);
1280 case '0': case '1': case '2': case '3': case '4':
1281 case '5': case '6': case '7': case '8': case '9': // color code
1283 default: // not a color code
1284 APPEND(STRING_COLOR_TAG);
1299 char *InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
1305 keylength = strlen(key);
1306 if (valuelength < 1 || !value)
1308 Con_Printf("InfoString_GetValue: no room in value\n");
1312 if (strchr(key, '\\'))
1314 Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
1317 if (strchr(key, '\"'))
1319 Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
1324 Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
1327 while (buffer[pos] == '\\')
1329 if (!memcmp(buffer + pos+1, key, keylength) &&
1330 (buffer[pos+1 + keylength] == 0 ||
1331 buffer[pos+1 + keylength] == '\\'))
1333 pos += 1 + (int)keylength; // Skip \key
1334 if (buffer[pos] == '\\') pos++; // Skip \ before value.
1335 for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
1336 value[j] = buffer[pos+j];
1340 if (buffer[pos] == '\\') pos++; // Skip \ before value.
1341 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1342 if (buffer[pos] == '\\') pos++; // Skip \ before value.
1343 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1345 // if we reach this point the key was not found
1349 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
1357 keylength = strlen(key);
1358 if (strchr(key, '\\') || strchr(value, '\\'))
1360 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
1363 if (strchr(key, '\"') || strchr(value, '\"'))
1365 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
1370 Con_Printf("InfoString_SetValue: can not set a key with no name\n");
1373 while (buffer[pos] == '\\')
1375 if (!memcmp(buffer + pos+1, key, keylength) &&
1376 (buffer[pos+1 + keylength] == 0 ||
1377 buffer[pos+1 + keylength] == '\\'))
1379 if (buffer[pos] == '\\') pos++; // Skip \ before value.
1380 for (;buffer[pos] && buffer[pos] != '\\';pos++);
1381 if (buffer[pos] == '\\') pos++; // Skip \ before value.
1382 for (;buffer[pos] && buffer[pos] != '\\';pos++);
1384 // if we found the key, find the end of it because we will be replacing it
1386 if (buffer[pos] == '\\')
1388 pos2 += 1 + (int)keylength; // Skip \key
1389 if (buffer[pos2] == '\\') pos2++; // Skip \ before value.
1390 for (;buffer[pos2] && buffer[pos2] != '\\';pos2++);
1392 if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
1394 Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
1399 // set the key/value and append the remaining text
1400 char tempbuffer[MAX_INPUTLINE];
1401 strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
1402 dpsnprintf(buffer + pos, bufferlength - pos, "\\%s\\%s%s", key, value, tempbuffer);
1406 // just remove the key from the text
1407 strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
1411 void InfoString_Print(char *buffer)
1414 char key[MAX_INPUTLINE];
1415 char value[MAX_INPUTLINE];
1418 if (*buffer != '\\')
1420 Con_Printf("InfoString_Print: corrupt string\n");
1423 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
1424 if (i < (int)sizeof(key)-1)
1427 if (*buffer != '\\')
1429 Con_Printf("InfoString_Print: corrupt string\n");
1432 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
1433 if (i < (int)sizeof(value)-1)
1434 value[i++] = *buffer;
1436 // empty value is an error case
1437 Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
1441 //========================================================
1442 // strlcat and strlcpy, from OpenBSD
1445 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
1447 * Permission to use, copy, modify, and distribute this software for any
1448 * purpose with or without fee is hereby granted, provided that the above
1449 * copyright notice and this permission notice appear in all copies.
1451 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1452 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1453 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1454 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1455 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1456 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1457 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1460 /* $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $ */
1461 /* $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $ */
1464 #ifndef HAVE_STRLCAT
1466 strlcat(char *dst, const char *src, size_t siz)
1468 register char *d = dst;
1469 register const char *s = src;
1470 register size_t n = siz;
1473 /* Find the end of dst and adjust bytes left but don't go past end */
1474 while (n-- != 0 && *d != '\0')
1480 return(dlen + strlen(s));
1481 while (*s != '\0') {
1490 return(dlen + (s - src)); /* count does not include NUL */
1492 #endif // #ifndef HAVE_STRLCAT
1495 #ifndef HAVE_STRLCPY
1497 strlcpy(char *dst, const char *src, size_t siz)
1499 register char *d = dst;
1500 register const char *s = src;
1501 register size_t n = siz;
1503 /* Copy as many bytes as will fit */
1504 if (n != 0 && --n != 0) {
1506 if ((*d++ = *s++) == 0)
1511 /* Not enough room in dst, add NUL and traverse rest of src */
1514 *d = '\0'; /* NUL-terminate dst */
1519 return(s - src - 1); /* count does not include NUL */
1522 #endif // #ifndef HAVE_STRLCPY
1524 void FindFraction(double val, int *num, int *denom, int denomMax)
1529 bestdiff = fabs(val);
1533 for(i = 1; i <= denomMax; ++i)
1535 int inum = (int) floor(0.5 + val * i);
1536 double diff = fabs(val - inum / (double)i);
1546 // decodes an XPM from C syntax
1547 char **XPM_DecodeString(const char *in)
1549 static char *tokens[257];
1550 static char lines[257][512];
1553 // skip until "{" token
1554 while(COM_ParseToken_QuakeC(&in, false) && strcmp(com_token, "{"));
1556 // now, read in succession: string, comma-or-}
1557 while(COM_ParseToken_QuakeC(&in, false))
1559 tokens[line] = lines[line];
1560 strlcpy(lines[line++], com_token, sizeof(lines[0]));
1561 if(!COM_ParseToken_QuakeC(&in, false))
1563 if(!strcmp(com_token, "}"))
1565 if(strcmp(com_token, ","))
1567 if(line >= sizeof(tokens) / sizeof(tokens[0]))
1574 static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1575 static void base64_3to4(const unsigned char *in, unsigned char *out, int bytes)
1577 unsigned char i0 = (bytes > 0) ? in[0] : 0;
1578 unsigned char i1 = (bytes > 1) ? in[1] : 0;
1579 unsigned char i2 = (bytes > 2) ? in[2] : 0;
1580 unsigned char o0 = base64[i0 >> 2];
1581 unsigned char o1 = base64[((i0 << 4) | (i1 >> 4)) & 077];
1582 unsigned char o2 = base64[((i1 << 2) | (i2 >> 6)) & 077];
1583 unsigned char o3 = base64[i2 & 077];
1584 out[0] = (bytes > 0) ? o0 : '?';
1585 out[1] = (bytes > 0) ? o1 : '?';
1586 out[2] = (bytes > 1) ? o2 : '=';
1587 out[3] = (bytes > 2) ? o3 : '=';
1590 size_t base64_encode(unsigned char *buf, size_t buflen, size_t outbuflen)
1593 // expand the out-buffer
1594 blocks = (buflen + 2) / 3;
1595 if(blocks*4 > outbuflen)
1597 for(i = blocks; i > 0; )
1600 base64_3to4(buf + 3*i, buf + 4*i, (int)(buflen - 3*i));