]> git.xonotic.org Git - xonotic/darkplaces.git/blob - common.c
common: Split off the gameinfo stuff to com_game.c
[xonotic/darkplaces.git] / common.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
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.
8
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.
12
13 See the GNU General Public License for more details.
14
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.
18
19 */
20 // common.c -- misc functions used in client and server
21
22 #include <stdlib.h>
23 #include <fcntl.h>
24 #ifndef WIN32
25 #include <unistd.h>
26 #endif
27
28 #include "quakedef.h"
29 #include "utf8lib.h"
30
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"};
33
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"};
37
38 char com_token[MAX_INPUTLINE];
39
40 //===========================================================================
41
42 void SZ_Clear (sizebuf_t *buf)
43 {
44         buf->cursize = 0;
45 }
46
47 unsigned char *SZ_GetSpace (sizebuf_t *buf, int length)
48 {
49         unsigned char *data;
50
51         if (buf->cursize + length > buf->maxsize)
52         {
53                 if (!buf->allowoverflow)
54                         Host_Error ("SZ_GetSpace: overflow without allowoverflow set");
55
56                 if (length > buf->maxsize)
57                         Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
58
59                 buf->overflowed = true;
60                 Con_Print("SZ_GetSpace: overflow\n");
61                 SZ_Clear (buf);
62         }
63
64         data = buf->data + buf->cursize;
65         buf->cursize += length;
66
67         return data;
68 }
69
70 void SZ_Write (sizebuf_t *buf, const unsigned char *data, int length)
71 {
72         memcpy (SZ_GetSpace(buf,length),data,length);
73 }
74
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
77 // all of darkplaces.
78
79 static const char *hexchar = "0123456789ABCDEF";
80 void Com_HexDumpToConsole(const unsigned char *data, int size)
81 {
82         int i, j, n;
83         char text[1024];
84         char *cur, *flushpointer;
85         const unsigned char *d;
86         cur = text;
87         flushpointer = text + 512;
88         for (i = 0;i < size;)
89         {
90                 n = 16;
91                 if (n > size - i)
92                         n = size - i;
93                 d = data + i;
94                 // print offset
95                 *cur++ = hexchar[(i >> 12) & 15];
96                 *cur++ = hexchar[(i >>  8) & 15];
97                 *cur++ = hexchar[(i >>  4) & 15];
98                 *cur++ = hexchar[(i >>  0) & 15];
99                 *cur++ = ':';
100                 // print hex
101                 for (j = 0;j < 16;j++)
102                 {
103                         if (j < n)
104                         {
105                                 *cur++ = hexchar[(d[j] >> 4) & 15];
106                                 *cur++ = hexchar[(d[j] >> 0) & 15];
107                         }
108                         else
109                         {
110                                 *cur++ = ' ';
111                                 *cur++ = ' ';
112                         }
113                         if ((j & 3) == 3)
114                                 *cur++ = ' ';
115                 }
116                 // print text
117                 for (j = 0;j < 16;j++)
118                 {
119                         if (j < n)
120                         {
121                                 // color change prefix character has to be treated specially
122                                 if (d[j] == STRING_COLOR_TAG)
123                                 {
124                                         *cur++ = STRING_COLOR_TAG;
125                                         *cur++ = STRING_COLOR_TAG;
126                                 }
127                                 else if (d[j] >= (unsigned char) ' ')
128                                         *cur++ = d[j];
129                                 else
130                                         *cur++ = '.';
131                         }
132                         else
133                                 *cur++ = ' ';
134                 }
135                 *cur++ = '\n';
136                 i += n;
137                 if (cur >= flushpointer || i >= size)
138                 {
139                         *cur++ = 0;
140                         Con_Print(text);
141                         cur = text;
142                 }
143         }
144 }
145
146 void SZ_HexDumpToConsole(const sizebuf_t *buf)
147 {
148         Com_HexDumpToConsole(buf->data, buf->cursize);
149 }
150
151
152 //============================================================================
153
154 /*
155 ==============
156 COM_Wordwrap
157
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.
163
164 In case no single character fits into the given width, the wordWidth function
165 must return the width of exactly one character.
166
167 Wrapped lines get the isContinuation flag set and are continuationWidth less wide.
168
169 The sum of the return values of the processLine function will be returned.
170 ==============
171 */
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)
173 {
174         // Logic is as follows:
175         //
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.
182
183         qboolean isContinuation = false;
184         float spaceWidth;
185         const char *startOfLine = string;
186         const char *cursor = string;
187         const char *end = string + length;
188         float spaceUsedInLine = 0;
189         float spaceUsedForWord;
190         int result = 0;
191         size_t wordLen;
192         size_t dummy;
193
194         dummy = 0;
195         wordWidth(passthroughCW, NULL, &dummy, -1);
196         dummy = 1;
197         spaceWidth = wordWidth(passthroughCW, " ", &dummy, -1);
198
199         for(;;)
200         {
201                 char ch = (cursor < end) ? *cursor : 0;
202                 switch(ch)
203                 {
204                         case 0: // end of string
205                                 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
206                                 goto out;
207                         case '\n': // end of line
208                                 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
209                                 isContinuation = false;
210                                 ++cursor;
211                                 startOfLine = cursor;
212                                 break;
213                         case ' ': // space
214                                 ++cursor;
215                                 spaceUsedInLine += spaceWidth;
216                                 break;
217                         default: // word
218                                 wordLen = 1;
219                                 while(cursor + wordLen < end)
220                                 {
221                                         switch(cursor[wordLen])
222                                         {
223                                                 case 0:
224                                                 case '\n':
225                                                 case ' ':
226                                                         goto out_inner;
227                                                 default:
228                                                         ++wordLen;
229                                                         break;
230                                         }
231                                 }
232                                 out_inner:
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
235                                 {
236                                         wordLen = 1;
237                                         spaceUsedForWord = maxWidth + 1; // too high, forces it in a line of itself
238                                 }
239                                 if(spaceUsedInLine + spaceUsedForWord <= maxWidth || cursor == startOfLine)
240                                 {
241                                         // we can simply append it
242                                         cursor += wordLen;
243                                         spaceUsedInLine += spaceUsedForWord;
244                                 }
245                                 else
246                                 {
247                                         // output current line
248                                         result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
249                                         isContinuation = true;
250                                         startOfLine = cursor;
251                                         cursor += wordLen;
252                                         spaceUsedInLine = continuationWidth + spaceUsedForWord;
253                                 }
254                 }
255         }
256         out:
257
258         return result;
259
260 /*
261         qboolean isContinuation = false;
262         float currentWordSpace = 0;
263         const char *currentWord = 0;
264         float minReserve = 0;
265
266         float spaceUsedInLine = 0;
267         const char *currentLine = 0;
268         const char *currentLineEnd = 0;
269         float currentLineFinalWhitespace = 0;
270         const char *p;
271
272         int result = 0;
273         minReserve = charWidth(passthroughCW, 0);
274         minReserve += charWidth(passthroughCW, ' ');
275
276         if(maxWidth < continuationWidth + minReserve)
277                 maxWidth = continuationWidth + minReserve;
278
279         charWidth(passthroughCW, 0);
280
281         for(p = string; p < string + length; ++p)
282         {
283                 char c = *p;
284                 float w = charWidth(passthroughCW, c);
285
286                 if(!currentWord)
287                 {
288                         currentWord = p;
289                         currentWordSpace = 0;
290                 }
291
292                 if(!currentLine)
293                 {
294                         currentLine = p;
295                         spaceUsedInLine = isContinuation ? continuationWidth : 0;
296                         currentLineEnd = 0;
297                 }
298
299                 if(c == ' ')
300                 {
301                         // 1. I can add the word AND a space - then just append it.
302                         if(spaceUsedInLine + currentWordSpace + w <= maxWidth)
303                         {
304                                 currentLineEnd = p; // note: space not included here
305                                 currentLineFinalWhitespace = w;
306                                 spaceUsedInLine += currentWordSpace + w;
307                         }
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)
310                         {
311                                 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
312                                 currentLine = 0;
313                                 isContinuation = true;
314                         }
315                         // 3. Otherwise, output current line and go to next one, where I can add the word.
316                         else if(continuationWidth + currentWordSpace + w <= maxWidth)
317                         {
318                                 if(currentLineEnd)
319                                         result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
320                                 currentLine = currentWord;
321                                 spaceUsedInLine = continuationWidth + currentWordSpace + w;
322                                 currentLineEnd = p;
323                                 currentLineFinalWhitespace = w;
324                                 isContinuation = true;
325                         }
326                         // 4. We can't even do that? Then output both current and next word as new lines.
327                         else
328                         {
329                                 if(currentLineEnd)
330                                 {
331                                         result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
332                                         isContinuation = true;
333                                 }
334                                 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
335                                 currentLine = 0;
336                                 isContinuation = true;
337                         }
338                         currentWord = 0;
339                 }
340                 else if(c == '\n')
341                 {
342                         // 1. I can add the word - then do it.
343                         if(spaceUsedInLine + currentWordSpace <= maxWidth)
344                         {
345                                 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
346                         }
347                         // 2. Otherwise, output current line, next one and make tabula rasa.
348                         else
349                         {
350                                 if(currentLineEnd)
351                                 {
352                                         processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
353                                         isContinuation = true;
354                                 }
355                                 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
356                         }
357                         currentWord = 0;
358                         currentLine = 0;
359                         isContinuation = false;
360                 }
361                 else
362                 {
363                         currentWordSpace += w;
364                         if(
365                                 spaceUsedInLine + currentWordSpace > maxWidth // can't join this line...
366                                 &&
367                                 continuationWidth + currentWordSpace > maxWidth // can't join any other line...
368                         )
369                         {
370                                 // this word cannot join ANY line...
371                                 // so output the current line...
372                                 if(currentLineEnd)
373                                 {
374                                         result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
375                                         isContinuation = true;
376                                 }
377
378                                 // then this word's beginning...
379                                 if(isContinuation)
380                                 {
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;
385
386                                         // reset the char width function to a state where no kerning occurs (start of word)
387                                         charWidth(passthroughCW, ' ');
388                                         while(pos <= p)
389                                         {
390                                                 float w = charWidth(passthroughCW, *pos);
391                                                 if(currentWordSpace + w > pieceWidth) // this piece won't fit any more
392                                                 {
393                                                         // print everything until it
394                                                         result += processLine(passthroughPL, currentWord, pos - currentWord, currentWordSpace, true);
395                                                         // go to here
396                                                         currentWord = pos;
397                                                         currentWordSpace = 0;
398                                                 }
399                                                 currentWordSpace += w;
400                                                 ++pos;
401                                         }
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;
407                                         currentLineEnd = 0;
408                                         isContinuation = true;
409                                 }
410                                 else
411                                 {
412                                         // we have a guarantee that it will fix (see if clause)
413                                         result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace - w, isContinuation);
414
415                                         // and use the rest of this word as new start of a line
416                                         currentWordSpace = w;
417                                         currentWord = p;
418                                         spaceUsedInLine = continuationWidth;
419                                         currentLine = p;
420                                         currentLineEnd = 0;
421                                         isContinuation = true;
422                                 }
423                         }
424                 }
425         }
426
427         if(!currentWord)
428         {
429                 currentWord = p;
430                 currentWordSpace = 0;
431         }
432
433         if(currentLine) // Same procedure as \n
434         {
435                 // Can I append the current word?
436                 if(spaceUsedInLine + currentWordSpace <= maxWidth)
437                         result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
438                 else
439                 {
440                         if(currentLineEnd)
441                         {
442                                 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
443                                 isContinuation = true;
444                         }
445                         result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
446                 }
447         }
448
449         return result;
450 */
451 }
452
453 /*
454 ==============
455 COM_ParseToken_Simple
456
457 Parse a token out of a string
458 ==============
459 */
460 int COM_ParseToken_Simple(const char **datapointer, qboolean returnnewline, qboolean parsebackslash, qboolean parsecomments)
461 {
462         int len;
463         int c;
464         const char *data = *datapointer;
465
466         len = 0;
467         com_token[0] = 0;
468
469         if (!data)
470         {
471                 *datapointer = NULL;
472                 return false;
473         }
474
475 // skip whitespace
476 skipwhite:
477         // line endings:
478         // UNIX: \n
479         // Mac: \r
480         // Windows: \r\n
481         for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
482         {
483                 if (*data == 0)
484                 {
485                         // end of file
486                         *datapointer = NULL;
487                         return false;
488                 }
489         }
490
491         // handle Windows line ending
492         if (data[0] == '\r' && data[1] == '\n')
493                 data++;
494
495         if (parsecomments && data[0] == '/' && data[1] == '/')
496         {
497                 // comment
498                 while (*data && *data != '\n' && *data != '\r')
499                         data++;
500                 goto skipwhite;
501         }
502         else if (parsecomments && data[0] == '/' && data[1] == '*')
503         {
504                 // comment
505                 data++;
506                 while (*data && (data[0] != '*' || data[1] != '/'))
507                         data++;
508                 if (*data)
509                         data++;
510                 if (*data)
511                         data++;
512                 goto skipwhite;
513         }
514         else if (*data == '\"')
515         {
516                 // quoted string
517                 for (data++;*data && *data != '\"';data++)
518                 {
519                         c = *data;
520                         if (*data == '\\' && parsebackslash)
521                         {
522                                 data++;
523                                 c = *data;
524                                 if (c == 'n')
525                                         c = '\n';
526                                 else if (c == 't')
527                                         c = '\t';
528                         }
529                         if (len < (int)sizeof(com_token) - 1)
530                                 com_token[len++] = c;
531                 }
532                 com_token[len] = 0;
533                 if (*data == '\"')
534                         data++;
535                 *datapointer = data;
536                 return true;
537         }
538         else if (*data == '\r')
539         {
540                 // translate Mac line ending to UNIX
541                 com_token[len++] = '\n';data++;
542                 com_token[len] = 0;
543                 *datapointer = data;
544                 return true;
545         }
546         else if (*data == '\n')
547         {
548                 // single character
549                 com_token[len++] = *data++;
550                 com_token[len] = 0;
551                 *datapointer = data;
552                 return true;
553         }
554         else
555         {
556                 // regular word
557                 for (;!ISWHITESPACE(*data);data++)
558                         if (len < (int)sizeof(com_token) - 1)
559                                 com_token[len++] = *data;
560                 com_token[len] = 0;
561                 *datapointer = data;
562                 return true;
563         }
564 }
565
566 /*
567 ==============
568 COM_ParseToken_QuakeC
569
570 Parse a token out of a string
571 ==============
572 */
573 int COM_ParseToken_QuakeC(const char **datapointer, qboolean returnnewline)
574 {
575         int len;
576         int c;
577         const char *data = *datapointer;
578
579         len = 0;
580         com_token[0] = 0;
581
582         if (!data)
583         {
584                 *datapointer = NULL;
585                 return false;
586         }
587
588 // skip whitespace
589 skipwhite:
590         // line endings:
591         // UNIX: \n
592         // Mac: \r
593         // Windows: \r\n
594         for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
595         {
596                 if (*data == 0)
597                 {
598                         // end of file
599                         *datapointer = NULL;
600                         return false;
601                 }
602         }
603
604         // handle Windows line ending
605         if (data[0] == '\r' && data[1] == '\n')
606                 data++;
607
608         if (data[0] == '/' && data[1] == '/')
609         {
610                 // comment
611                 while (*data && *data != '\n' && *data != '\r')
612                         data++;
613                 goto skipwhite;
614         }
615         else if (data[0] == '/' && data[1] == '*')
616         {
617                 // comment
618                 data++;
619                 while (*data && (data[0] != '*' || data[1] != '/'))
620                         data++;
621                 if (*data)
622                         data++;
623                 if (*data)
624                         data++;
625                 goto skipwhite;
626         }
627         else if (*data == '\"' || *data == '\'')
628         {
629                 // quoted string
630                 char quote = *data;
631                 for (data++;*data && *data != quote;data++)
632                 {
633                         c = *data;
634                         if (*data == '\\')
635                         {
636                                 data++;
637                                 c = *data;
638                                 if (c == 'n')
639                                         c = '\n';
640                                 else if (c == 't')
641                                         c = '\t';
642                         }
643                         if (len < (int)sizeof(com_token) - 1)
644                                 com_token[len++] = c;
645                 }
646                 com_token[len] = 0;
647                 if (*data == quote)
648                         data++;
649                 *datapointer = data;
650                 return true;
651         }
652         else if (*data == '\r')
653         {
654                 // translate Mac line ending to UNIX
655                 com_token[len++] = '\n';data++;
656                 com_token[len] = 0;
657                 *datapointer = data;
658                 return true;
659         }
660         else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
661         {
662                 // single character
663                 com_token[len++] = *data++;
664                 com_token[len] = 0;
665                 *datapointer = data;
666                 return true;
667         }
668         else
669         {
670                 // regular word
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;
674                 com_token[len] = 0;
675                 *datapointer = data;
676                 return true;
677         }
678 }
679
680 /*
681 ==============
682 COM_ParseToken_VM_Tokenize
683
684 Parse a token out of a string
685 ==============
686 */
687 int COM_ParseToken_VM_Tokenize(const char **datapointer, qboolean returnnewline)
688 {
689         int len;
690         int c;
691         const char *data = *datapointer;
692
693         len = 0;
694         com_token[0] = 0;
695
696         if (!data)
697         {
698                 *datapointer = NULL;
699                 return false;
700         }
701
702 // skip whitespace
703 skipwhite:
704         // line endings:
705         // UNIX: \n
706         // Mac: \r
707         // Windows: \r\n
708         for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
709         {
710                 if (*data == 0)
711                 {
712                         // end of file
713                         *datapointer = NULL;
714                         return false;
715                 }
716         }
717
718         // handle Windows line ending
719         if (data[0] == '\r' && data[1] == '\n')
720                 data++;
721
722         if (data[0] == '/' && data[1] == '/')
723         {
724                 // comment
725                 while (*data && *data != '\n' && *data != '\r')
726                         data++;
727                 goto skipwhite;
728         }
729         else if (data[0] == '/' && data[1] == '*')
730         {
731                 // comment
732                 data++;
733                 while (*data && (data[0] != '*' || data[1] != '/'))
734                         data++;
735                 if (*data)
736                         data++;
737                 if (*data)
738                         data++;
739                 goto skipwhite;
740         }
741         else if (*data == '\"' || *data == '\'')
742         {
743                 char quote = *data;
744                 // quoted string
745                 for (data++;*data && *data != quote;data++)
746                 {
747                         c = *data;
748                         if (*data == '\\')
749                         {
750                                 data++;
751                                 c = *data;
752                                 if (c == 'n')
753                                         c = '\n';
754                                 else if (c == 't')
755                                         c = '\t';
756                         }
757                         if (len < (int)sizeof(com_token) - 1)
758                                 com_token[len++] = c;
759                 }
760                 com_token[len] = 0;
761                 if (*data == quote)
762                         data++;
763                 *datapointer = data;
764                 return true;
765         }
766         else if (*data == '\r')
767         {
768                 // translate Mac line ending to UNIX
769                 com_token[len++] = '\n';data++;
770                 com_token[len] = 0;
771                 *datapointer = data;
772                 return true;
773         }
774         else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
775         {
776                 // single character
777                 com_token[len++] = *data++;
778                 com_token[len] = 0;
779                 *datapointer = data;
780                 return true;
781         }
782         else
783         {
784                 // regular word
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;
788                 com_token[len] = 0;
789                 *datapointer = data;
790                 return true;
791         }
792 }
793
794 /*
795 ==============
796 COM_ParseToken_Console
797
798 Parse a token out of a string, behaving like the qwcl console
799 ==============
800 */
801 int COM_ParseToken_Console(const char **datapointer)
802 {
803         int len;
804         const char *data = *datapointer;
805
806         len = 0;
807         com_token[0] = 0;
808
809         if (!data)
810         {
811                 *datapointer = NULL;
812                 return false;
813         }
814
815 // skip whitespace
816 skipwhite:
817         for (;ISWHITESPACE(*data);data++)
818         {
819                 if (*data == 0)
820                 {
821                         // end of file
822                         *datapointer = NULL;
823                         return false;
824                 }
825         }
826
827         if (*data == '/' && data[1] == '/')
828         {
829                 // comment
830                 while (*data && *data != '\n' && *data != '\r')
831                         data++;
832                 goto skipwhite;
833         }
834         else if (*data == '\"')
835         {
836                 // quoted string
837                 for (data++;*data && *data != '\"';data++)
838                 {
839                         // allow escaped " and \ case
840                         if (*data == '\\' && (data[1] == '\"' || data[1] == '\\'))
841                                 data++;
842                         if (len < (int)sizeof(com_token) - 1)
843                                 com_token[len++] = *data;
844                 }
845                 com_token[len] = 0;
846                 if (*data == '\"')
847                         data++;
848                 *datapointer = data;
849         }
850         else
851         {
852                 // regular word
853                 for (;!ISWHITESPACE(*data);data++)
854                         if (len < (int)sizeof(com_token) - 1)
855                                 com_token[len++] = *data;
856                 com_token[len] = 0;
857                 *datapointer = data;
858         }
859
860         return true;
861 }
862
863
864 /*
865 ================
866 COM_CheckParm
867
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
870 ================
871 */
872 int COM_CheckParm (const char *parm)
873 {
874         int i;
875
876         for (i=1 ; i<sys.argc ; i++)
877         {
878                 if (!sys.argv[i])
879                         continue;               // NEXTSTEP sometimes clears appkit vars.
880                 if (!strcmp (parm,sys.argv[i]))
881                         return i;
882         }
883
884         return 0;
885 }
886
887 //===========================================================================
888
889 /*
890 ================
891 COM_Init
892 ================
893 */
894 void COM_Init_Commands (void)
895 {
896         int i, j, n;
897         char com_cmdline[MAX_INPUTLINE];
898
899         Cvar_RegisterVariable (&registered);
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");
905
906         // reconstitute the command line for the cmdline externally visible cvar
907         n = 0;
908         for (j = 0;(j < MAX_NUM_ARGVS) && (j < sys.argc);j++)
909         {
910                 i = 0;
911                 if (strstr(sys.argv[j], " "))
912                 {
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))
917                                 break;
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++] = '\"';
925                 }
926                 else
927                 {
928                         // This condition checks whether we can allow one
929                         // more character.
930                         while ((n < ((int)sizeof(com_cmdline) - 1)) && sys.argv[j][i])
931                                 com_cmdline[n++] = sys.argv[j][i++];
932                 }
933                 if (n < ((int)sizeof(com_cmdline) - 1))
934                         com_cmdline[n++] = ' ';
935                 else
936                         break;
937         }
938         com_cmdline[n] = 0;
939         Cvar_SetQuick(&cmdline, com_cmdline);
940 }
941
942 /*
943 ============
944 va
945
946 varargs print into provided buffer, returns buffer (so that it can be called in-line, unlike dpsnprintf)
947 ============
948 */
949 char *va(char *buf, size_t buflen, const char *format, ...)
950 {
951         va_list argptr;
952
953         va_start (argptr, format);
954         dpvsnprintf (buf, buflen, format,argptr);
955         va_end (argptr);
956
957         return buf;
958 }
959
960
961 //======================================
962
963 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
964
965 #undef snprintf
966 #undef vsnprintf
967
968 #ifdef WIN32
969 # define snprintf _snprintf
970 # define vsnprintf _vsnprintf
971 #endif
972
973
974 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
975 {
976         va_list args;
977         int result;
978
979         va_start (args, format);
980         result = dpvsnprintf (buffer, buffersize, format, args);
981         va_end (args);
982
983         return result;
984 }
985
986
987 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
988 {
989         int result;
990
991 #if _MSC_VER >= 1400
992         result = _vsnprintf_s (buffer, buffersize, _TRUNCATE, format, args);
993 #else
994         result = vsnprintf (buffer, buffersize, format, args);
995 #endif
996         if (result < 0 || (size_t)result >= buffersize)
997         {
998                 buffer[buffersize - 1] = '\0';
999                 return -1;
1000         }
1001
1002         return result;
1003 }
1004
1005
1006 //======================================
1007
1008 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1009 {
1010         if (size_out == 0)
1011                 return;
1012
1013         if(utf8_enable.integer)
1014         {
1015                 *out = 0;
1016                 while(*in && size_out > 1)
1017                 {
1018                         int n;
1019                         Uchar ch = u8_getchar_utf8_enabled(in, &in);
1020                         ch = u8_tolower(ch);
1021                         n = u8_fromchar(ch, out, size_out);
1022                         if(n <= 0)
1023                                 break;
1024                         out += n;
1025                         size_out -= n;
1026                 }
1027                 return;
1028         }
1029
1030         while (*in && size_out > 1)
1031         {
1032                 if (*in >= 'A' && *in <= 'Z')
1033                         *out++ = *in++ + 'a' - 'A';
1034                 else
1035                         *out++ = *in++;
1036                 size_out--;
1037         }
1038         *out = '\0';
1039 }
1040
1041 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1042 {
1043         if (size_out == 0)
1044                 return;
1045
1046         if(utf8_enable.integer)
1047         {
1048                 *out = 0;
1049                 while(*in && size_out > 1)
1050                 {
1051                         int n;
1052                         Uchar ch = u8_getchar_utf8_enabled(in, &in);
1053                         ch = u8_toupper(ch);
1054                         n = u8_fromchar(ch, out, size_out);
1055                         if(n <= 0)
1056                                 break;
1057                         out += n;
1058                         size_out -= n;
1059                 }
1060                 return;
1061         }
1062
1063         while (*in && size_out > 1)
1064         {
1065                 if (*in >= 'a' && *in <= 'z')
1066                         *out++ = *in++ + 'A' - 'a';
1067                 else
1068                         *out++ = *in++;
1069                 size_out--;
1070         }
1071         *out = '\0';
1072 }
1073
1074 int COM_StringBeginsWith(const char *s, const char *match)
1075 {
1076         for (;*s && *match;s++, match++)
1077                 if (*s != *match)
1078                         return false;
1079         return true;
1080 }
1081
1082 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1083 {
1084         int argc, commentprefixlength;
1085         char *tokenbufend;
1086         const char *l;
1087         argc = 0;
1088         tokenbufend = tokenbuf + tokenbufsize;
1089         l = *text;
1090         commentprefixlength = 0;
1091         if (commentprefix)
1092                 commentprefixlength = (int)strlen(commentprefix);
1093         while (*l && *l != '\n' && *l != '\r')
1094         {
1095                 if (!ISWHITESPACE(*l))
1096                 {
1097                         if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1098                         {
1099                                 while (*l && *l != '\n' && *l != '\r')
1100                                         l++;
1101                                 break;
1102                         }
1103                         if (argc >= maxargc)
1104                                 return -1;
1105                         argv[argc++] = tokenbuf;
1106                         if (*l == '"')
1107                         {
1108                                 l++;
1109                                 while (*l && *l != '"')
1110                                 {
1111                                         if (tokenbuf >= tokenbufend)
1112                                                 return -1;
1113                                         *tokenbuf++ = *l++;
1114                                 }
1115                                 if (*l == '"')
1116                                         l++;
1117                         }
1118                         else
1119                         {
1120                                 while (!ISWHITESPACE(*l))
1121                                 {
1122                                         if (tokenbuf >= tokenbufend)
1123                                                 return -1;
1124                                         *tokenbuf++ = *l++;
1125                                 }
1126                         }
1127                         if (tokenbuf >= tokenbufend)
1128                                 return -1;
1129                         *tokenbuf++ = 0;
1130                 }
1131                 else
1132                         l++;
1133         }
1134         // line endings:
1135         // UNIX: \n
1136         // Mac: \r
1137         // Windows: \r\n
1138         if (*l == '\r')
1139                 l++;
1140         if (*l == '\n')
1141                 l++;
1142         *text = l;
1143         return argc;
1144 }
1145
1146 /*
1147 ============
1148 COM_StringLengthNoColors
1149
1150 calculates the visible width of a color coded string.
1151
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.
1155
1156 valid can be set to NULL if the caller doesn't care.
1157
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.
1160 ============
1161 */
1162 size_t
1163 COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
1164 {
1165         const char *end = size_s ? (s + size_s) : NULL;
1166         size_t len = 0;
1167         for(;;)
1168         {
1169                 switch((s == end) ? 0 : *s)
1170                 {
1171                         case 0:
1172                                 if(valid)
1173                                         *valid = true;
1174                                 return len;
1175                         case STRING_COLOR_TAG:
1176                                 ++s;
1177                                 switch((s == end) ? 0 : *s)
1178                                 {
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]) )
1183                                                 {
1184                                                         s+=3;
1185                                                         break;
1186                                                 }
1187                                                 ++len; // STRING_COLOR_TAG
1188                                                 ++len; // STRING_COLOR_RGB_TAG_CHAR
1189                                                 break;
1190                                         case 0: // ends with unfinished color code!
1191                                                 ++len;
1192                                                 if(valid)
1193                                                         *valid = false;
1194                                                 return len;
1195                                         case STRING_COLOR_TAG: // escaped ^
1196                                                 ++len;
1197                                                 break;
1198                                         case '0': case '1': case '2': case '3': case '4':
1199                                         case '5': case '6': case '7': case '8': case '9': // color code
1200                                                 break;
1201                                         default: // not a color code
1202                                                 ++len; // STRING_COLOR_TAG
1203                                                 ++len; // the character
1204                                                 break;
1205                                 }
1206                                 break;
1207                         default:
1208                                 ++len;
1209                                 break;
1210                 }
1211                 ++s;
1212         }
1213         // never get here
1214 }
1215
1216 /*
1217 ============
1218 COM_StringDecolorize
1219
1220 removes color codes from a string.
1221
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
1224 for example).
1225
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
1230 zero terminated.
1231
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.
1234 ============
1235 */
1236 qboolean
1237 COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qboolean escape_carets)
1238 {
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;
1241         if(size_out < 1)
1242                 return false;
1243         for(;;)
1244         {
1245                 switch((in == end) ? 0 : *in)
1246                 {
1247                         case 0:
1248                                 *out++ = 0;
1249                                 return true;
1250                         case STRING_COLOR_TAG:
1251                                 ++in;
1252                                 switch((in == end) ? 0 : *in)
1253                                 {
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]) )
1258                                                 {
1259                                                         in+=3;
1260                                                         break;
1261                                                 }
1262                                                 APPEND(STRING_COLOR_TAG);
1263                                                 if(escape_carets)
1264                                                         APPEND(STRING_COLOR_TAG);
1265                                                 APPEND(STRING_COLOR_RGB_TAG_CHAR);
1266                                                 break;
1267                                         case 0: // ends with unfinished color code!
1268                                                 APPEND(STRING_COLOR_TAG);
1269                                                 // finish the code by appending another caret when escaping
1270                                                 if(escape_carets)
1271                                                         APPEND(STRING_COLOR_TAG);
1272                                                 *out++ = 0;
1273                                                 return true;
1274                                         case STRING_COLOR_TAG: // escaped ^
1275                                                 APPEND(STRING_COLOR_TAG);
1276                                                 // append a ^ twice when escaping
1277                                                 if(escape_carets)
1278                                                         APPEND(STRING_COLOR_TAG);
1279                                                 break;
1280                                         case '0': case '1': case '2': case '3': case '4':
1281                                         case '5': case '6': case '7': case '8': case '9': // color code
1282                                                 break;
1283                                         default: // not a color code
1284                                                 APPEND(STRING_COLOR_TAG);
1285                                                 APPEND(*in);
1286                                                 break;
1287                                 }
1288                                 break;
1289                         default:
1290                                 APPEND(*in);
1291                                 break;
1292                 }
1293                 ++in;
1294         }
1295         // never get here
1296 #undef APPEND
1297 }
1298
1299 char *InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
1300 {
1301         int pos = 0, j;
1302         size_t keylength;
1303         if (!key)
1304                 key = "";
1305         keylength = strlen(key);
1306         if (valuelength < 1 || !value)
1307         {
1308                 Con_Printf("InfoString_GetValue: no room in value\n");
1309                 return NULL;
1310         }
1311         value[0] = 0;
1312         if (strchr(key, '\\'))
1313         {
1314                 Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
1315                 return NULL;
1316         }
1317         if (strchr(key, '\"'))
1318         {
1319                 Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
1320                 return NULL;
1321         }
1322         if (!key[0])
1323         {
1324                 Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
1325                 return NULL;
1326         }
1327         while (buffer[pos] == '\\')
1328         {
1329                 if (!memcmp(buffer + pos+1, key, keylength) &&
1330                                 (buffer[pos+1 + keylength] == 0 ||
1331                                  buffer[pos+1 + keylength] == '\\'))
1332                 {
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];
1337                         value[j] = 0;
1338                         return value;
1339                 }
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++);
1344         }
1345         // if we reach this point the key was not found
1346         return NULL;
1347 }
1348
1349 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
1350 {
1351         int pos = 0, pos2;
1352         size_t keylength;
1353         if (!key)
1354                 key = "";
1355         if (!value)
1356                 value = "";
1357         keylength = strlen(key);
1358         if (strchr(key, '\\') || strchr(value, '\\'))
1359         {
1360                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
1361                 return;
1362         }
1363         if (strchr(key, '\"') || strchr(value, '\"'))
1364         {
1365                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
1366                 return;
1367         }
1368         if (!key[0])
1369         {
1370                 Con_Printf("InfoString_SetValue: can not set a key with no name\n");
1371                 return;
1372         }
1373         while (buffer[pos] == '\\')
1374         {
1375                 if (!memcmp(buffer + pos+1, key, keylength) &&
1376                                 (buffer[pos+1 + keylength] == 0 ||
1377                                  buffer[pos+1 + keylength] == '\\'))
1378                         break;
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++);
1383         }
1384         // if we found the key, find the end of it because we will be replacing it
1385         pos2 = pos;
1386         if (buffer[pos] == '\\')
1387         {
1388                 pos2 += 1 + (int)keylength;  // Skip \key
1389                 if (buffer[pos2] == '\\') pos2++; // Skip \ before value.
1390                 for (;buffer[pos2] && buffer[pos2] != '\\';pos2++);
1391         }
1392         if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
1393         {
1394                 Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
1395                 return;
1396         }
1397         if (value[0])
1398         {
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);
1403         }
1404         else
1405         {
1406                 // just remove the key from the text
1407                 strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
1408         }
1409 }
1410
1411 void InfoString_Print(char *buffer)
1412 {
1413         int i;
1414         char key[MAX_INPUTLINE];
1415         char value[MAX_INPUTLINE];
1416         while (*buffer)
1417         {
1418                 if (*buffer != '\\')
1419                 {
1420                         Con_Printf("InfoString_Print: corrupt string\n");
1421                         return;
1422                 }
1423                 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
1424                         if (i < (int)sizeof(key)-1)
1425                                 key[i++] = *buffer;
1426                 key[i] = 0;
1427                 if (*buffer != '\\')
1428                 {
1429                         Con_Printf("InfoString_Print: corrupt string\n");
1430                         return;
1431                 }
1432                 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
1433                         if (i < (int)sizeof(value)-1)
1434                                 value[i++] = *buffer;
1435                 value[i] = 0;
1436                 // empty value is an error case
1437                 Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
1438         }
1439 }
1440
1441 //========================================================
1442 // strlcat and strlcpy, from OpenBSD
1443
1444 /*
1445  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
1446  *
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.
1450  *
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.
1458  */
1459
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 $     */
1462
1463
1464 #ifndef HAVE_STRLCAT
1465 size_t
1466 strlcat(char *dst, const char *src, size_t siz)
1467 {
1468         register char *d = dst;
1469         register const char *s = src;
1470         register size_t n = siz;
1471         size_t dlen;
1472
1473         /* Find the end of dst and adjust bytes left but don't go past end */
1474         while (n-- != 0 && *d != '\0')
1475                 d++;
1476         dlen = d - dst;
1477         n = siz - dlen;
1478
1479         if (n == 0)
1480                 return(dlen + strlen(s));
1481         while (*s != '\0') {
1482                 if (n != 1) {
1483                         *d++ = *s;
1484                         n--;
1485                 }
1486                 s++;
1487         }
1488         *d = '\0';
1489
1490         return(dlen + (s - src));       /* count does not include NUL */
1491 }
1492 #endif  // #ifndef HAVE_STRLCAT
1493
1494
1495 #ifndef HAVE_STRLCPY
1496 size_t
1497 strlcpy(char *dst, const char *src, size_t siz)
1498 {
1499         register char *d = dst;
1500         register const char *s = src;
1501         register size_t n = siz;
1502
1503         /* Copy as many bytes as will fit */
1504         if (n != 0 && --n != 0) {
1505                 do {
1506                         if ((*d++ = *s++) == 0)
1507                                 break;
1508                 } while (--n != 0);
1509         }
1510
1511         /* Not enough room in dst, add NUL and traverse rest of src */
1512         if (n == 0) {
1513                 if (siz != 0)
1514                         *d = '\0';              /* NUL-terminate dst */
1515                 while (*s++)
1516                         ;
1517         }
1518
1519         return(s - src - 1);    /* count does not include NUL */
1520 }
1521
1522 #endif  // #ifndef HAVE_STRLCPY
1523
1524 void FindFraction(double val, int *num, int *denom, int denomMax)
1525 {
1526         int i;
1527         double bestdiff;
1528         // initialize
1529         bestdiff = fabs(val);
1530         *num = 0;
1531         *denom = 1;
1532
1533         for(i = 1; i <= denomMax; ++i)
1534         {
1535                 int inum = (int) floor(0.5 + val * i);
1536                 double diff = fabs(val - inum / (double)i);
1537                 if(diff < bestdiff)
1538                 {
1539                         bestdiff = diff;
1540                         *num = inum;
1541                         *denom = i;
1542                 }
1543         }
1544 }
1545
1546 // decodes an XPM from C syntax
1547 char **XPM_DecodeString(const char *in)
1548 {
1549         static char *tokens[257];
1550         static char lines[257][512];
1551         size_t line = 0;
1552
1553         // skip until "{" token
1554         while(COM_ParseToken_QuakeC(&in, false) && strcmp(com_token, "{"));
1555
1556         // now, read in succession: string, comma-or-}
1557         while(COM_ParseToken_QuakeC(&in, false))
1558         {
1559                 tokens[line] = lines[line];
1560                 strlcpy(lines[line++], com_token, sizeof(lines[0]));
1561                 if(!COM_ParseToken_QuakeC(&in, false))
1562                         return NULL;
1563                 if(!strcmp(com_token, "}"))
1564                         break;
1565                 if(strcmp(com_token, ","))
1566                         return NULL;
1567                 if(line >= sizeof(tokens) / sizeof(tokens[0]))
1568                         return NULL;
1569         }
1570
1571         return tokens;
1572 }
1573
1574 static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1575 static void base64_3to4(const unsigned char *in, unsigned char *out, int bytes)
1576 {
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 : '=';
1588 }
1589
1590 size_t base64_encode(unsigned char *buf, size_t buflen, size_t outbuflen)
1591 {
1592         size_t blocks, i;
1593         // expand the out-buffer
1594         blocks = (buflen + 2) / 3;
1595         if(blocks*4 > outbuflen)
1596                 return 0;
1597         for(i = blocks; i > 0; )
1598         {
1599                 --i;
1600                 base64_3to4(buf + 3*i, buf + 4*i, (int)(buflen - 3*i));
1601         }
1602         return blocks * 4;
1603 }