]> git.xonotic.org Git - xonotic/darkplaces.git/blob - common.c
c36deb2d279fe2639ec95da911696a87d0b9724d
[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 = {CF_CLIENT | CF_SERVER, "registered","0", "indicates if this is running registered quake (whether gfx/pop.lmp was found)"};
32 cvar_t cmdline = {CF_CLIENT | CF_SERVER, "cmdline","0", "contains commandline the engine was launched with"};
33
34 // FIXME: Find a better place for these.
35 cvar_t cl_playermodel = {CF_CLIENT | CF_SERVER | CF_USERINFO | CF_ARCHIVE, "playermodel", "", "current player model in Nexuiz/Xonotic"};
36 cvar_t cl_playerskin = {CF_CLIENT | CF_SERVER | CF_USERINFO | CF_ARCHIVE, "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         qbool 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         qbool 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, qbool returnnewline, qbool parsebackslash, qbool 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, qbool 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, qbool 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 Com_CalcRoll
866
867 Used by view and sv_user
868 ===============
869 */
870 float Com_CalcRoll (const vec3_t angles, const vec3_t velocity, const vec_t angleval, const vec_t velocityval)
871 {
872         vec3_t  forward, right, up;
873         float   sign;
874         float   side;
875
876         AngleVectors (angles, forward, right, up);
877         side = DotProduct (velocity, right);
878         sign = side < 0 ? -1 : 1;
879         side = fabs(side);
880
881         if (side < velocityval)
882                 side = side * angleval / velocityval;
883         else
884                 side = angleval;
885
886         return side*sign;
887
888 }
889
890 //===========================================================================
891
892 /*
893 ================
894 COM_Init
895 ================
896 */
897 void COM_Init_Commands (void)
898 {
899         int i, j, n;
900         char com_cmdline[MAX_INPUTLINE];
901
902         Cvar_RegisterVariable (&registered);
903         Cvar_RegisterVariable (&cmdline);
904         Cvar_RegisterVariable(&cl_playermodel);
905         Cvar_RegisterAlias(&cl_playermodel, "_cl_playermodel");
906         Cvar_RegisterVariable(&cl_playerskin);
907         Cvar_RegisterAlias(&cl_playerskin, "_cl_playerskin");
908
909         // reconstitute the command line for the cmdline externally visible cvar
910         n = 0;
911         for (j = 0;(j < MAX_NUM_ARGVS) && (j < sys.argc);j++)
912         {
913                 i = 0;
914                 if (strstr(sys.argv[j], " "))
915                 {
916                         // arg contains whitespace, store quotes around it
917                         // This condition checks whether we can allow to put
918                         // in two quote characters.
919                         if (n >= ((int)sizeof(com_cmdline) - 2))
920                                 break;
921                         com_cmdline[n++] = '\"';
922                         // This condition checks whether we can allow one
923                         // more character and a quote character.
924                         while ((n < ((int)sizeof(com_cmdline) - 2)) && sys.argv[j][i])
925                                 // FIXME: Doesn't quote special characters.
926                                 com_cmdline[n++] = sys.argv[j][i++];
927                         com_cmdline[n++] = '\"';
928                 }
929                 else
930                 {
931                         // This condition checks whether we can allow one
932                         // more character.
933                         while ((n < ((int)sizeof(com_cmdline) - 1)) && sys.argv[j][i])
934                                 com_cmdline[n++] = sys.argv[j][i++];
935                 }
936                 if (n < ((int)sizeof(com_cmdline) - 1))
937                         com_cmdline[n++] = ' ';
938                 else
939                         break;
940         }
941         com_cmdline[n] = 0;
942         Cvar_SetQuick(&cmdline, com_cmdline);
943 }
944
945 /*
946 ============
947 va
948
949 varargs print into provided buffer, returns buffer (so that it can be called in-line, unlike dpsnprintf)
950 ============
951 */
952 char *va(char *buf, size_t buflen, const char *format, ...)
953 {
954         va_list argptr;
955
956         va_start (argptr, format);
957         dpvsnprintf (buf, buflen, format,argptr);
958         va_end (argptr);
959
960         return buf;
961 }
962
963
964 //======================================
965
966 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
967
968 #undef snprintf
969 #undef vsnprintf
970
971 #ifdef WIN32
972 # define snprintf _snprintf
973 # define vsnprintf _vsnprintf
974 #endif
975
976
977 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
978 {
979         va_list args;
980         int result;
981
982         va_start (args, format);
983         result = dpvsnprintf (buffer, buffersize, format, args);
984         va_end (args);
985
986         return result;
987 }
988
989
990 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
991 {
992         int result;
993
994 #if _MSC_VER >= 1400
995         result = _vsnprintf_s (buffer, buffersize, _TRUNCATE, format, args);
996 #else
997         result = vsnprintf (buffer, buffersize, format, args);
998 #endif
999         if (result < 0 || (size_t)result >= buffersize)
1000         {
1001                 buffer[buffersize - 1] = '\0';
1002                 return -1;
1003         }
1004
1005         return result;
1006 }
1007
1008
1009 //======================================
1010
1011 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1012 {
1013         if (size_out == 0)
1014                 return;
1015
1016         if(utf8_enable.integer)
1017         {
1018                 *out = 0;
1019                 while(*in && size_out > 1)
1020                 {
1021                         int n;
1022                         Uchar ch = u8_getchar_utf8_enabled(in, &in);
1023                         ch = u8_tolower(ch);
1024                         n = u8_fromchar(ch, out, size_out);
1025                         if(n <= 0)
1026                                 break;
1027                         out += n;
1028                         size_out -= n;
1029                 }
1030                 return;
1031         }
1032
1033         while (*in && size_out > 1)
1034         {
1035                 if (*in >= 'A' && *in <= 'Z')
1036                         *out++ = *in++ + 'a' - 'A';
1037                 else
1038                         *out++ = *in++;
1039                 size_out--;
1040         }
1041         *out = '\0';
1042 }
1043
1044 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1045 {
1046         if (size_out == 0)
1047                 return;
1048
1049         if(utf8_enable.integer)
1050         {
1051                 *out = 0;
1052                 while(*in && size_out > 1)
1053                 {
1054                         int n;
1055                         Uchar ch = u8_getchar_utf8_enabled(in, &in);
1056                         ch = u8_toupper(ch);
1057                         n = u8_fromchar(ch, out, size_out);
1058                         if(n <= 0)
1059                                 break;
1060                         out += n;
1061                         size_out -= n;
1062                 }
1063                 return;
1064         }
1065
1066         while (*in && size_out > 1)
1067         {
1068                 if (*in >= 'a' && *in <= 'z')
1069                         *out++ = *in++ + 'A' - 'a';
1070                 else
1071                         *out++ = *in++;
1072                 size_out--;
1073         }
1074         *out = '\0';
1075 }
1076
1077 int COM_StringBeginsWith(const char *s, const char *match)
1078 {
1079         for (;*s && *match;s++, match++)
1080                 if (*s != *match)
1081                         return false;
1082         return true;
1083 }
1084
1085 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1086 {
1087         int argc, commentprefixlength;
1088         char *tokenbufend;
1089         const char *l;
1090         argc = 0;
1091         tokenbufend = tokenbuf + tokenbufsize;
1092         l = *text;
1093         commentprefixlength = 0;
1094         if (commentprefix)
1095                 commentprefixlength = (int)strlen(commentprefix);
1096         while (*l && *l != '\n' && *l != '\r')
1097         {
1098                 if (!ISWHITESPACE(*l))
1099                 {
1100                         if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1101                         {
1102                                 while (*l && *l != '\n' && *l != '\r')
1103                                         l++;
1104                                 break;
1105                         }
1106                         if (argc >= maxargc)
1107                                 return -1;
1108                         argv[argc++] = tokenbuf;
1109                         if (*l == '"')
1110                         {
1111                                 l++;
1112                                 while (*l && *l != '"')
1113                                 {
1114                                         if (tokenbuf >= tokenbufend)
1115                                                 return -1;
1116                                         *tokenbuf++ = *l++;
1117                                 }
1118                                 if (*l == '"')
1119                                         l++;
1120                         }
1121                         else
1122                         {
1123                                 while (!ISWHITESPACE(*l))
1124                                 {
1125                                         if (tokenbuf >= tokenbufend)
1126                                                 return -1;
1127                                         *tokenbuf++ = *l++;
1128                                 }
1129                         }
1130                         if (tokenbuf >= tokenbufend)
1131                                 return -1;
1132                         *tokenbuf++ = 0;
1133                 }
1134                 else
1135                         l++;
1136         }
1137         // line endings:
1138         // UNIX: \n
1139         // Mac: \r
1140         // Windows: \r\n
1141         if (*l == '\r')
1142                 l++;
1143         if (*l == '\n')
1144                 l++;
1145         *text = l;
1146         return argc;
1147 }
1148
1149 /*
1150 ============
1151 COM_StringLengthNoColors
1152
1153 calculates the visible width of a color coded string.
1154
1155 *valid is filled with TRUE if the string is a valid colored string (that is, if
1156 it does not end with an unfinished color code). If it gets filled with FALSE, a
1157 fix would be adding a STRING_COLOR_TAG at the end of the string.
1158
1159 valid can be set to NULL if the caller doesn't care.
1160
1161 For size_s, specify the maximum number of characters from s to use, or 0 to use
1162 all characters until the zero terminator.
1163 ============
1164 */
1165 size_t
1166 COM_StringLengthNoColors(const char *s, size_t size_s, qbool *valid)
1167 {
1168         const char *end = size_s ? (s + size_s) : NULL;
1169         size_t len = 0;
1170         for(;;)
1171         {
1172                 switch((s == end) ? 0 : *s)
1173                 {
1174                         case 0:
1175                                 if(valid)
1176                                         *valid = true;
1177                                 return len;
1178                         case STRING_COLOR_TAG:
1179                                 ++s;
1180                                 switch((s == end) ? 0 : *s)
1181                                 {
1182                                         case STRING_COLOR_RGB_TAG_CHAR:
1183                                                 if (s+1 != end && isxdigit(s[1]) &&
1184                                                         s+2 != end && isxdigit(s[2]) &&
1185                                                         s+3 != end && isxdigit(s[3]) )
1186                                                 {
1187                                                         s+=3;
1188                                                         break;
1189                                                 }
1190                                                 ++len; // STRING_COLOR_TAG
1191                                                 ++len; // STRING_COLOR_RGB_TAG_CHAR
1192                                                 break;
1193                                         case 0: // ends with unfinished color code!
1194                                                 ++len;
1195                                                 if(valid)
1196                                                         *valid = false;
1197                                                 return len;
1198                                         case STRING_COLOR_TAG: // escaped ^
1199                                                 ++len;
1200                                                 break;
1201                                         case '0': case '1': case '2': case '3': case '4':
1202                                         case '5': case '6': case '7': case '8': case '9': // color code
1203                                                 break;
1204                                         default: // not a color code
1205                                                 ++len; // STRING_COLOR_TAG
1206                                                 ++len; // the character
1207                                                 break;
1208                                 }
1209                                 break;
1210                         default:
1211                                 ++len;
1212                                 break;
1213                 }
1214                 ++s;
1215         }
1216         // never get here
1217 }
1218
1219 /*
1220 ============
1221 COM_StringDecolorize
1222
1223 removes color codes from a string.
1224
1225 If escape_carets is true, the resulting string will be safe for printing. If
1226 escape_carets is false, the function will just strip color codes (for logging
1227 for example).
1228
1229 If the output buffer size did not suffice for converting, the function returns
1230 FALSE. Generally, if escape_carets is false, the output buffer needs
1231 strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)*1.5+2
1232 bytes. In any case, the function makes sure that the resulting string is
1233 zero terminated.
1234
1235 For size_in, specify the maximum number of characters from in to use, or 0 to use
1236 all characters until the zero terminator.
1237 ============
1238 */
1239 qbool
1240 COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qbool escape_carets)
1241 {
1242 #define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return false; } } while(0)
1243         const char *end = size_in ? (in + size_in) : NULL;
1244         if(size_out < 1)
1245                 return false;
1246         for(;;)
1247         {
1248                 switch((in == end) ? 0 : *in)
1249                 {
1250                         case 0:
1251                                 *out++ = 0;
1252                                 return true;
1253                         case STRING_COLOR_TAG:
1254                                 ++in;
1255                                 switch((in == end) ? 0 : *in)
1256                                 {
1257                                         case STRING_COLOR_RGB_TAG_CHAR:
1258                                                 if (in+1 != end && isxdigit(in[1]) &&
1259                                                         in+2 != end && isxdigit(in[2]) &&
1260                                                         in+3 != end && isxdigit(in[3]) )
1261                                                 {
1262                                                         in+=3;
1263                                                         break;
1264                                                 }
1265                                                 APPEND(STRING_COLOR_TAG);
1266                                                 if(escape_carets)
1267                                                         APPEND(STRING_COLOR_TAG);
1268                                                 APPEND(STRING_COLOR_RGB_TAG_CHAR);
1269                                                 break;
1270                                         case 0: // ends with unfinished color code!
1271                                                 APPEND(STRING_COLOR_TAG);
1272                                                 // finish the code by appending another caret when escaping
1273                                                 if(escape_carets)
1274                                                         APPEND(STRING_COLOR_TAG);
1275                                                 *out++ = 0;
1276                                                 return true;
1277                                         case STRING_COLOR_TAG: // escaped ^
1278                                                 APPEND(STRING_COLOR_TAG);
1279                                                 // append a ^ twice when escaping
1280                                                 if(escape_carets)
1281                                                         APPEND(STRING_COLOR_TAG);
1282                                                 break;
1283                                         case '0': case '1': case '2': case '3': case '4':
1284                                         case '5': case '6': case '7': case '8': case '9': // color code
1285                                                 break;
1286                                         default: // not a color code
1287                                                 APPEND(STRING_COLOR_TAG);
1288                                                 APPEND(*in);
1289                                                 break;
1290                                 }
1291                                 break;
1292                         default:
1293                                 APPEND(*in);
1294                                 break;
1295                 }
1296                 ++in;
1297         }
1298         // never get here
1299 #undef APPEND
1300 }
1301
1302 char *InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
1303 {
1304         int pos = 0, j;
1305         size_t keylength;
1306         if (!key)
1307                 key = "";
1308         keylength = strlen(key);
1309         if (valuelength < 1 || !value)
1310         {
1311                 Con_Printf("InfoString_GetValue: no room in value\n");
1312                 return NULL;
1313         }
1314         value[0] = 0;
1315         if (strchr(key, '\\'))
1316         {
1317                 Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
1318                 return NULL;
1319         }
1320         if (strchr(key, '\"'))
1321         {
1322                 Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
1323                 return NULL;
1324         }
1325         if (!key[0])
1326         {
1327                 Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
1328                 return NULL;
1329         }
1330         while (buffer[pos] == '\\')
1331         {
1332                 if (!memcmp(buffer + pos+1, key, keylength) &&
1333                                 (buffer[pos+1 + keylength] == 0 ||
1334                                  buffer[pos+1 + keylength] == '\\'))
1335                 {
1336                         pos += 1 + (int)keylength;           // Skip \key
1337                         if (buffer[pos] == '\\') pos++; // Skip \ before value.
1338                         for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
1339                                 value[j] = buffer[pos+j];
1340                         value[j] = 0;
1341                         return value;
1342                 }
1343                 if (buffer[pos] == '\\') pos++; // Skip \ before value.
1344                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1345                 if (buffer[pos] == '\\') pos++; // Skip \ before value.
1346                 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1347         }
1348         // if we reach this point the key was not found
1349         return NULL;
1350 }
1351
1352 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
1353 {
1354         int pos = 0, pos2;
1355         size_t keylength;
1356         if (!key)
1357                 key = "";
1358         if (!value)
1359                 value = "";
1360         keylength = strlen(key);
1361         if (strchr(key, '\\') || strchr(value, '\\'))
1362         {
1363                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
1364                 return;
1365         }
1366         if (strchr(key, '\"') || strchr(value, '\"'))
1367         {
1368                 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
1369                 return;
1370         }
1371         if (!key[0])
1372         {
1373                 Con_Printf("InfoString_SetValue: can not set a key with no name\n");
1374                 return;
1375         }
1376         while (buffer[pos] == '\\')
1377         {
1378                 if (!memcmp(buffer + pos+1, key, keylength) &&
1379                                 (buffer[pos+1 + keylength] == 0 ||
1380                                  buffer[pos+1 + keylength] == '\\'))
1381                         break;
1382                 if (buffer[pos] == '\\') pos++; // Skip \ before value.
1383                 for (;buffer[pos] && buffer[pos] != '\\';pos++);
1384                 if (buffer[pos] == '\\') pos++; // Skip \ before value.
1385                 for (;buffer[pos] && buffer[pos] != '\\';pos++);
1386         }
1387         // if we found the key, find the end of it because we will be replacing it
1388         pos2 = pos;
1389         if (buffer[pos] == '\\')
1390         {
1391                 pos2 += 1 + (int)keylength;  // Skip \key
1392                 if (buffer[pos2] == '\\') pos2++; // Skip \ before value.
1393                 for (;buffer[pos2] && buffer[pos2] != '\\';pos2++);
1394         }
1395         if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
1396         {
1397                 Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
1398                 return;
1399         }
1400         if (value[0])
1401         {
1402                 // set the key/value and append the remaining text
1403                 char tempbuffer[MAX_INPUTLINE];
1404                 strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
1405                 dpsnprintf(buffer + pos, bufferlength - pos, "\\%s\\%s%s", key, value, tempbuffer);
1406         }
1407         else
1408         {
1409                 // just remove the key from the text
1410                 strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
1411         }
1412 }
1413
1414 void InfoString_Print(char *buffer)
1415 {
1416         int i;
1417         char key[MAX_INPUTLINE];
1418         char value[MAX_INPUTLINE];
1419         while (*buffer)
1420         {
1421                 if (*buffer != '\\')
1422                 {
1423                         Con_Printf("InfoString_Print: corrupt string\n");
1424                         return;
1425                 }
1426                 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
1427                         if (i < (int)sizeof(key)-1)
1428                                 key[i++] = *buffer;
1429                 key[i] = 0;
1430                 if (*buffer != '\\')
1431                 {
1432                         Con_Printf("InfoString_Print: corrupt string\n");
1433                         return;
1434                 }
1435                 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
1436                         if (i < (int)sizeof(value)-1)
1437                                 value[i++] = *buffer;
1438                 value[i] = 0;
1439                 // empty value is an error case
1440                 Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
1441         }
1442 }
1443
1444 //========================================================
1445 // strlcat and strlcpy, from OpenBSD
1446
1447 /*
1448  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
1449  *
1450  * Permission to use, copy, modify, and distribute this software for any
1451  * purpose with or without fee is hereby granted, provided that the above
1452  * copyright notice and this permission notice appear in all copies.
1453  *
1454  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1455  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1456  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1457  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1458  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1459  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1460  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1461  */
1462
1463 /*      $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $    */
1464 /*      $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $     */
1465
1466
1467 #ifndef HAVE_STRLCAT
1468 size_t
1469 strlcat(char *dst, const char *src, size_t siz)
1470 {
1471         register char *d = dst;
1472         register const char *s = src;
1473         register size_t n = siz;
1474         size_t dlen;
1475
1476         /* Find the end of dst and adjust bytes left but don't go past end */
1477         while (n-- != 0 && *d != '\0')
1478                 d++;
1479         dlen = d - dst;
1480         n = siz - dlen;
1481
1482         if (n == 0)
1483                 return(dlen + strlen(s));
1484         while (*s != '\0') {
1485                 if (n != 1) {
1486                         *d++ = *s;
1487                         n--;
1488                 }
1489                 s++;
1490         }
1491         *d = '\0';
1492
1493         return(dlen + (s - src));       /* count does not include NUL */
1494 }
1495 #endif  // #ifndef HAVE_STRLCAT
1496
1497
1498 #ifndef HAVE_STRLCPY
1499 size_t
1500 strlcpy(char *dst, const char *src, size_t siz)
1501 {
1502         register char *d = dst;
1503         register const char *s = src;
1504         register size_t n = siz;
1505
1506         /* Copy as many bytes as will fit */
1507         if (n != 0 && --n != 0) {
1508                 do {
1509                         if ((*d++ = *s++) == 0)
1510                                 break;
1511                 } while (--n != 0);
1512         }
1513
1514         /* Not enough room in dst, add NUL and traverse rest of src */
1515         if (n == 0) {
1516                 if (siz != 0)
1517                         *d = '\0';              /* NUL-terminate dst */
1518                 while (*s++)
1519                         ;
1520         }
1521
1522         return(s - src - 1);    /* count does not include NUL */
1523 }
1524
1525 #endif  // #ifndef HAVE_STRLCPY
1526
1527 void FindFraction(double val, int *num, int *denom, int denomMax)
1528 {
1529         int i;
1530         double bestdiff;
1531         // initialize
1532         bestdiff = fabs(val);
1533         *num = 0;
1534         *denom = 1;
1535
1536         for(i = 1; i <= denomMax; ++i)
1537         {
1538                 int inum = (int) floor(0.5 + val * i);
1539                 double diff = fabs(val - inum / (double)i);
1540                 if(diff < bestdiff)
1541                 {
1542                         bestdiff = diff;
1543                         *num = inum;
1544                         *denom = i;
1545                 }
1546         }
1547 }
1548
1549 // decodes an XPM from C syntax
1550 char **XPM_DecodeString(const char *in)
1551 {
1552         static char *tokens[257];
1553         static char lines[257][512];
1554         size_t line = 0;
1555
1556         // skip until "{" token
1557         while(COM_ParseToken_QuakeC(&in, false) && strcmp(com_token, "{"));
1558
1559         // now, read in succession: string, comma-or-}
1560         while(COM_ParseToken_QuakeC(&in, false))
1561         {
1562                 tokens[line] = lines[line];
1563                 strlcpy(lines[line++], com_token, sizeof(lines[0]));
1564                 if(!COM_ParseToken_QuakeC(&in, false))
1565                         return NULL;
1566                 if(!strcmp(com_token, "}"))
1567                         break;
1568                 if(strcmp(com_token, ","))
1569                         return NULL;
1570                 if(line >= sizeof(tokens) / sizeof(tokens[0]))
1571                         return NULL;
1572         }
1573
1574         return tokens;
1575 }
1576
1577 static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1578 static void base64_3to4(const unsigned char *in, unsigned char *out, int bytes)
1579 {
1580         unsigned char i0 = (bytes > 0) ? in[0] : 0;
1581         unsigned char i1 = (bytes > 1) ? in[1] : 0;
1582         unsigned char i2 = (bytes > 2) ? in[2] : 0;
1583         unsigned char o0 = base64[i0 >> 2];
1584         unsigned char o1 = base64[((i0 << 4) | (i1 >> 4)) & 077];
1585         unsigned char o2 = base64[((i1 << 2) | (i2 >> 6)) & 077];
1586         unsigned char o3 = base64[i2 & 077];
1587         out[0] = (bytes > 0) ? o0 : '?';
1588         out[1] = (bytes > 0) ? o1 : '?';
1589         out[2] = (bytes > 1) ? o2 : '=';
1590         out[3] = (bytes > 2) ? o3 : '=';
1591 }
1592
1593 size_t base64_encode(unsigned char *buf, size_t buflen, size_t outbuflen)
1594 {
1595         size_t blocks, i;
1596         // expand the out-buffer
1597         blocks = (buflen + 2) / 3;
1598         if(blocks*4 > outbuflen)
1599                 return 0;
1600         for(i = blocks; i > 0; )
1601         {
1602                 --i;
1603                 base64_3to4(buf + 3*i, buf + 4*i, (int)(buflen - 3*i));
1604         }
1605         return blocks * 4;
1606 }