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