common: Move filematch headers to new filematch.h
[xonotic/darkplaces.git] / common.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3 Copyright (C) 2000-2020 DarkPlaces contributors
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
14 See the GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19
20 */
21 // common.c -- misc functions used in client and server
22
23 #include <stdlib.h>
24 #include <fcntl.h>
25 #ifndef WIN32
26 #include <unistd.h>
27 #endif
28
29 #include "quakedef.h"
30 #include "utf8lib.h"
31
32 cvar_t registered = {CF_CLIENT | CF_SERVER, "registered","0", "indicates if this is running registered quake (whether gfx/pop.lmp was found)"};
33 cvar_t cmdline = {CF_CLIENT | CF_SERVER, "cmdline","0", "contains commandline the engine was launched with"};
34
35 // FIXME: Find a better place for these.
36 cvar_t cl_playermodel = {CF_CLIENT | CF_SERVER | CF_USERINFO | CF_ARCHIVE, "playermodel", "", "current player model in Nexuiz/Xonotic"};
37 cvar_t cl_playerskin = {CF_CLIENT | CF_SERVER | CF_USERINFO | CF_ARCHIVE, "playerskin", "", "current player skin in Nexuiz/Xonotic"};
38
39 char com_token[MAX_INPUTLINE];
40
41 //===========================================================================
42
43 void SZ_Clear (sizebuf_t *buf)
44 {
45         buf->cursize = 0;
46 }
47
48 unsigned char *SZ_GetSpace (sizebuf_t *buf, int length)
49 {
50         unsigned char *data;
51
52         if (buf->cursize + length > buf->maxsize)
53         {
54                 if (!buf->allowoverflow)
55                         Host_Error ("SZ_GetSpace: overflow without allowoverflow set");
56
57                 if (length > buf->maxsize)
58                         Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
59
60                 buf->overflowed = true;
61                 Con_Print("SZ_GetSpace: overflow\n");
62                 SZ_Clear (buf);
63         }
64
65         data = buf->data + buf->cursize;
66         buf->cursize += length;
67
68         return data;
69 }
70
71 void SZ_Write (sizebuf_t *buf, const unsigned char *data, int length)
72 {
73         memcpy (SZ_GetSpace(buf,length),data,length);
74 }
75
76 // LadyHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my
77 // attention, it has been eradicated from here, its only (former) use in
78 // all of darkplaces.
79
80 static const char *hexchar = "0123456789ABCDEF";
81 void Com_HexDumpToConsole(const unsigned char *data, int size)
82 {
83         int i, j, n;
84         char text[1024];
85         char *cur, *flushpointer;
86         const unsigned char *d;
87         cur = text;
88         flushpointer = text + 512;
89         for (i = 0;i < size;)
90         {
91                 n = 16;
92                 if (n > size - i)
93                         n = size - i;
94                 d = data + i;
95                 // print offset
96                 *cur++ = hexchar[(i >> 12) & 15];
97                 *cur++ = hexchar[(i >>  8) & 15];
98                 *cur++ = hexchar[(i >>  4) & 15];
99                 *cur++ = hexchar[(i >>  0) & 15];
100                 *cur++ = ':';
101                 // print hex
102                 for (j = 0;j < 16;j++)
103                 {
104                         if (j < n)
105                         {
106                                 *cur++ = hexchar[(d[j] >> 4) & 15];
107                                 *cur++ = hexchar[(d[j] >> 0) & 15];
108                         }
109                         else
110                         {
111                                 *cur++ = ' ';
112                                 *cur++ = ' ';
113                         }
114                         if ((j & 3) == 3)
115                                 *cur++ = ' ';
116                 }
117                 // print text
118                 for (j = 0;j < 16;j++)
119                 {
120                         if (j < n)
121                         {
122                                 // color change prefix character has to be treated specially
123                                 if (d[j] == STRING_COLOR_TAG)
124                                 {
125                                         *cur++ = STRING_COLOR_TAG;
126                                         *cur++ = STRING_COLOR_TAG;
127                                 }
128                                 else if (d[j] >= (unsigned char) ' ')
129                                         *cur++ = d[j];
130                                 else
131                                         *cur++ = '.';
132                         }
133                         else
134                                 *cur++ = ' ';
135                 }
136                 *cur++ = '\n';
137                 i += n;
138                 if (cur >= flushpointer || i >= size)
139                 {
140                         *cur++ = 0;
141                         Con_Print(text);
142                         cur = text;
143                 }
144         }
145 }
146
147 void SZ_HexDumpToConsole(const sizebuf_t *buf)
148 {
149         Com_HexDumpToConsole(buf->data, buf->cursize);
150 }
151
152
153 //============================================================================
154
155 /*
156 ==============
157 COM_Wordwrap
158
159 Word wraps a string. The wordWidth function is guaranteed to be called exactly
160 once for each word in the string, so it may be stateful, no idea what that
161 would be good for any more. At the beginning of the string, it will be called
162 for the char 0 to initialize a clean state, and then once with the string " "
163 (a space) so the routine knows how long a space is.
164
165 In case no single character fits into the given width, the wordWidth function
166 must return the width of exactly one character.
167
168 Wrapped lines get the isContinuation flag set and are continuationWidth less wide.
169
170 The sum of the return values of the processLine function will be returned.
171 ==============
172 */
173 int COM_Wordwrap(const char *string, size_t length, float continuationWidth, float maxWidth, COM_WordWidthFunc_t wordWidth, void *passthroughCW, COM_LineProcessorFunc processLine, void *passthroughPL)
174 {
175         // Logic is as follows:
176         //
177         // For each word or whitespace:
178         //   Newline found? Output current line, advance to next line. This is not a continuation. Continue.
179         //   Space found? Always add it to the current line, no matter if it fits.
180         //   Word found? Check if current line + current word fits.
181         //     If it fits, append it. Continue.
182         //     If it doesn't fit, output current line, advance to next line. Append the word. This is a continuation. Continue.
183
184         qbool isContinuation = false;
185         float spaceWidth;
186         const char *startOfLine = string;
187         const char *cursor = string;
188         const char *end = string + length;
189         float spaceUsedInLine = 0;
190         float spaceUsedForWord;
191         int result = 0;
192         size_t wordLen;
193         size_t dummy;
194
195         dummy = 0;
196         wordWidth(passthroughCW, NULL, &dummy, -1);
197         dummy = 1;
198         spaceWidth = wordWidth(passthroughCW, " ", &dummy, -1);
199
200         for(;;)
201         {
202                 char ch = (cursor < end) ? *cursor : 0;
203                 switch(ch)
204                 {
205                         case 0: // end of string
206                                 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
207                                 goto out;
208                         case '\n': // end of line
209                                 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
210                                 isContinuation = false;
211                                 ++cursor;
212                                 startOfLine = cursor;
213                                 break;
214                         case ' ': // space
215                                 ++cursor;
216                                 spaceUsedInLine += spaceWidth;
217                                 break;
218                         default: // word
219                                 wordLen = 1;
220                                 while(cursor + wordLen < end)
221                                 {
222                                         switch(cursor[wordLen])
223                                         {
224                                                 case 0:
225                                                 case '\n':
226                                                 case ' ':
227                                                         goto out_inner;
228                                                 default:
229                                                         ++wordLen;
230                                                         break;
231                                         }
232                                 }
233                                 out_inner:
234                                 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
235                                 if(wordLen < 1) // cannot happen according to current spec of wordWidth
236                                 {
237                                         wordLen = 1;
238                                         spaceUsedForWord = maxWidth + 1; // too high, forces it in a line of itself
239                                 }
240                                 if(spaceUsedInLine + spaceUsedForWord <= maxWidth || cursor == startOfLine)
241                                 {
242                                         // we can simply append it
243                                         cursor += wordLen;
244                                         spaceUsedInLine += spaceUsedForWord;
245                                 }
246                                 else
247                                 {
248                                         // output current line
249                                         result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
250                                         isContinuation = true;
251                                         startOfLine = cursor;
252                                         cursor += wordLen;
253                                         spaceUsedInLine = continuationWidth + spaceUsedForWord;
254                                 }
255                 }
256         }
257         out:
258
259         return result;
260
261 /*
262         qbool isContinuation = false;
263         float currentWordSpace = 0;
264         const char *currentWord = 0;
265         float minReserve = 0;
266
267         float spaceUsedInLine = 0;
268         const char *currentLine = 0;
269         const char *currentLineEnd = 0;
270         float currentLineFinalWhitespace = 0;
271         const char *p;
272
273         int result = 0;
274         minReserve = charWidth(passthroughCW, 0);
275         minReserve += charWidth(passthroughCW, ' ');
276
277         if(maxWidth < continuationWidth + minReserve)
278                 maxWidth = continuationWidth + minReserve;
279
280         charWidth(passthroughCW, 0);
281
282         for(p = string; p < string + length; ++p)
283         {
284                 char c = *p;
285                 float w = charWidth(passthroughCW, c);
286
287                 if(!currentWord)
288                 {
289                         currentWord = p;
290                         currentWordSpace = 0;
291                 }
292
293                 if(!currentLine)
294                 {
295                         currentLine = p;
296                         spaceUsedInLine = isContinuation ? continuationWidth : 0;
297                         currentLineEnd = 0;
298                 }
299
300                 if(c == ' ')
301                 {
302                         // 1. I can add the word AND a space - then just append it.
303                         if(spaceUsedInLine + currentWordSpace + w <= maxWidth)
304                         {
305                                 currentLineEnd = p; // note: space not included here
306                                 currentLineFinalWhitespace = w;
307                                 spaceUsedInLine += currentWordSpace + w;
308                         }
309                         // 2. I can just add the word - then append it, output current line and go to next one.
310                         else if(spaceUsedInLine + currentWordSpace <= maxWidth)
311                         {
312                                 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
313                                 currentLine = 0;
314                                 isContinuation = true;
315                         }
316                         // 3. Otherwise, output current line and go to next one, where I can add the word.
317                         else if(continuationWidth + currentWordSpace + w <= maxWidth)
318                         {
319                                 if(currentLineEnd)
320                                         result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
321                                 currentLine = currentWord;
322                                 spaceUsedInLine = continuationWidth + currentWordSpace + w;
323                                 currentLineEnd = p;
324                                 currentLineFinalWhitespace = w;
325                                 isContinuation = true;
326                         }
327                         // 4. We can't even do that? Then output both current and next word as new lines.
328                         else
329                         {
330                                 if(currentLineEnd)
331                                 {
332                                         result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
333                                         isContinuation = true;
334                                 }
335                                 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
336                                 currentLine = 0;
337                                 isContinuation = true;
338                         }
339                         currentWord = 0;
340                 }
341                 else if(c == '\n')
342                 {
343                         // 1. I can add the word - then do it.
344                         if(spaceUsedInLine + currentWordSpace <= maxWidth)
345                         {
346                                 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
347                         }
348                         // 2. Otherwise, output current line, next one and make tabula rasa.
349                         else
350                         {
351                                 if(currentLineEnd)
352                                 {
353                                         processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
354                                         isContinuation = true;
355                                 }
356                                 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
357                         }
358                         currentWord = 0;
359                         currentLine = 0;
360                         isContinuation = false;
361                 }
362                 else
363                 {
364                         currentWordSpace += w;
365                         if(
366                                 spaceUsedInLine + currentWordSpace > maxWidth // can't join this line...
367                                 &&
368                                 continuationWidth + currentWordSpace > maxWidth // can't join any other line...
369                         )
370                         {
371                                 // this word cannot join ANY line...
372                                 // so output the current line...
373                                 if(currentLineEnd)
374                                 {
375                                         result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
376                                         isContinuation = true;
377                                 }
378
379                                 // then this word's beginning...
380                                 if(isContinuation)
381                                 {
382                                         // it may not fit, but we know we have to split it into maxWidth - continuationWidth pieces
383                                         float pieceWidth = maxWidth - continuationWidth;
384                                         const char *pos = currentWord;
385                                         currentWordSpace = 0;
386
387                                         // reset the char width function to a state where no kerning occurs (start of word)
388                                         charWidth(passthroughCW, ' ');
389                                         while(pos <= p)
390                                         {
391                                                 float w = charWidth(passthroughCW, *pos);
392                                                 if(currentWordSpace + w > pieceWidth) // this piece won't fit any more
393                                                 {
394                                                         // print everything until it
395                                                         result += processLine(passthroughPL, currentWord, pos - currentWord, currentWordSpace, true);
396                                                         // go to here
397                                                         currentWord = pos;
398                                                         currentWordSpace = 0;
399                                                 }
400                                                 currentWordSpace += w;
401                                                 ++pos;
402                                         }
403                                         // now we have a currentWord that fits... set up its next line
404                                         // currentWordSpace has been set
405                                         // currentWord has been set
406                                         spaceUsedInLine = continuationWidth;
407                                         currentLine = currentWord;
408                                         currentLineEnd = 0;
409                                         isContinuation = true;
410                                 }
411                                 else
412                                 {
413                                         // we have a guarantee that it will fix (see if clause)
414                                         result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace - w, isContinuation);
415
416                                         // and use the rest of this word as new start of a line
417                                         currentWordSpace = w;
418                                         currentWord = p;
419                                         spaceUsedInLine = continuationWidth;
420                                         currentLine = p;
421                                         currentLineEnd = 0;
422                                         isContinuation = true;
423                                 }
424                         }
425                 }
426         }
427
428         if(!currentWord)
429         {
430                 currentWord = p;
431                 currentWordSpace = 0;
432         }
433
434         if(currentLine) // Same procedure as \n
435         {
436                 // Can I append the current word?
437                 if(spaceUsedInLine + currentWordSpace <= maxWidth)
438                         result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
439                 else
440                 {
441                         if(currentLineEnd)
442                         {
443                                 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
444                                 isContinuation = true;
445                         }
446                         result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
447                 }
448         }
449
450         return result;
451 */
452 }
453
454 /*
455 ==============
456 COM_ParseToken_Simple
457
458 Parse a token out of a string
459 ==============
460 */
461 int COM_ParseToken_Simple(const char **datapointer, qbool returnnewline, qbool parsebackslash, qbool parsecomments)
462 {
463         int len;
464         int c;
465         const char *data = *datapointer;
466
467         len = 0;
468         com_token[0] = 0;
469
470         if (!data)
471         {
472                 *datapointer = NULL;
473                 return false;
474         }
475
476 // skip whitespace
477 skipwhite:
478         // line endings:
479         // UNIX: \n
480         // Mac: \r
481         // Windows: \r\n
482         for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
483         {
484                 if (*data == 0)
485                 {
486                         // end of file
487                         *datapointer = NULL;
488                         return false;
489                 }
490         }
491
492         // handle Windows line ending
493         if (data[0] == '\r' && data[1] == '\n')
494                 data++;
495
496         if (parsecomments && data[0] == '/' && data[1] == '/')
497         {
498                 // comment
499                 while (*data && *data != '\n' && *data != '\r')
500                         data++;
501                 goto skipwhite;
502         }
503         else if (parsecomments && data[0] == '/' && data[1] == '*')
504         {
505                 // comment
506                 data++;
507                 while (*data && (data[0] != '*' || data[1] != '/'))
508                         data++;
509                 if (*data)
510                         data++;
511                 if (*data)
512                         data++;
513                 goto skipwhite;
514         }
515         else if (*data == '\"')
516         {
517                 // quoted string
518                 for (data++;*data && *data != '\"';data++)
519                 {
520                         c = *data;
521                         if (*data == '\\' && parsebackslash)
522                         {
523                                 data++;
524                                 c = *data;
525                                 if (c == 'n')
526                                         c = '\n';
527                                 else if (c == 't')
528                                         c = '\t';
529                         }
530                         if (len < (int)sizeof(com_token) - 1)
531                                 com_token[len++] = c;
532                 }
533                 com_token[len] = 0;
534                 if (*data == '\"')
535                         data++;
536                 *datapointer = data;
537                 return true;
538         }
539         else if (*data == '\r')
540         {
541                 // translate Mac line ending to UNIX
542                 com_token[len++] = '\n';data++;
543                 com_token[len] = 0;
544                 *datapointer = data;
545                 return true;
546         }
547         else if (*data == '\n')
548         {
549                 // single character
550                 com_token[len++] = *data++;
551                 com_token[len] = 0;
552                 *datapointer = data;
553                 return true;
554         }
555         else
556         {
557                 // regular word
558                 for (;!ISWHITESPACE(*data);data++)
559                         if (len < (int)sizeof(com_token) - 1)
560                                 com_token[len++] = *data;
561                 com_token[len] = 0;
562                 *datapointer = data;
563                 return true;
564         }
565 }
566
567 /*
568 ==============
569 COM_ParseToken_QuakeC
570
571 Parse a token out of a string
572 ==============
573 */
574 int COM_ParseToken_QuakeC(const char **datapointer, qbool returnnewline)
575 {
576         int len;
577         int c;
578         const char *data = *datapointer;
579
580         len = 0;
581         com_token[0] = 0;
582
583         if (!data)
584         {
585                 *datapointer = NULL;
586                 return false;
587         }
588
589 // skip whitespace
590 skipwhite:
591         // line endings:
592         // UNIX: \n
593         // Mac: \r
594         // Windows: \r\n
595         for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
596         {
597                 if (*data == 0)
598                 {
599                         // end of file
600                         *datapointer = NULL;
601                         return false;
602                 }
603         }
604
605         // handle Windows line ending
606         if (data[0] == '\r' && data[1] == '\n')
607                 data++;
608
609         if (data[0] == '/' && data[1] == '/')
610         {
611                 // comment
612                 while (*data && *data != '\n' && *data != '\r')
613                         data++;
614                 goto skipwhite;
615         }
616         else if (data[0] == '/' && data[1] == '*')
617         {
618                 // comment
619                 data++;
620                 while (*data && (data[0] != '*' || data[1] != '/'))
621                         data++;
622                 if (*data)
623                         data++;
624                 if (*data)
625                         data++;
626                 goto skipwhite;
627         }
628         else if (*data == '\"' || *data == '\'')
629         {
630                 // quoted string
631                 char quote = *data;
632                 for (data++;*data && *data != quote;data++)
633                 {
634                         c = *data;
635                         if (*data == '\\')
636                         {
637                                 data++;
638                                 c = *data;
639                                 if (c == 'n')
640                                         c = '\n';
641                                 else if (c == 't')
642                                         c = '\t';
643                         }
644                         if (len < (int)sizeof(com_token) - 1)
645                                 com_token[len++] = c;
646                 }
647                 com_token[len] = 0;
648                 if (*data == quote)
649                         data++;
650                 *datapointer = data;
651                 return true;
652         }
653         else if (*data == '\r')
654         {
655                 // translate Mac line ending to UNIX
656                 com_token[len++] = '\n';data++;
657                 com_token[len] = 0;
658                 *datapointer = data;
659                 return true;
660         }
661         else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
662         {
663                 // single character
664                 com_token[len++] = *data++;
665                 com_token[len] = 0;
666                 *datapointer = data;
667                 return true;
668         }
669         else
670         {
671                 // regular word
672                 for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
673                         if (len < (int)sizeof(com_token) - 1)
674                                 com_token[len++] = *data;
675                 com_token[len] = 0;
676                 *datapointer = data;
677                 return true;
678         }
679 }
680
681 /*
682 ==============
683 COM_ParseToken_VM_Tokenize
684
685 Parse a token out of a string
686 ==============
687 */
688 int COM_ParseToken_VM_Tokenize(const char **datapointer, qbool returnnewline)
689 {
690         int len;
691         int c;
692         const char *data = *datapointer;
693
694         len = 0;
695         com_token[0] = 0;
696
697         if (!data)
698         {
699                 *datapointer = NULL;
700                 return false;
701         }
702
703 // skip whitespace
704 skipwhite:
705         // line endings:
706         // UNIX: \n
707         // Mac: \r
708         // Windows: \r\n
709         for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
710         {
711                 if (*data == 0)
712                 {
713                         // end of file
714                         *datapointer = NULL;
715                         return false;
716                 }
717         }
718
719         // handle Windows line ending
720         if (data[0] == '\r' && data[1] == '\n')
721                 data++;
722
723         if (data[0] == '/' && data[1] == '/')
724         {
725                 // comment
726                 while (*data && *data != '\n' && *data != '\r')
727                         data++;
728                 goto skipwhite;
729         }
730         else if (data[0] == '/' && data[1] == '*')
731         {
732                 // comment
733                 data++;
734                 while (*data && (data[0] != '*' || data[1] != '/'))
735                         data++;
736                 if (*data)
737                         data++;
738                 if (*data)
739                         data++;
740                 goto skipwhite;
741         }
742         else if (*data == '\"' || *data == '\'')
743         {
744                 char quote = *data;
745                 // quoted string
746                 for (data++;*data && *data != quote;data++)
747                 {
748                         c = *data;
749                         if (*data == '\\')
750                         {
751                                 data++;
752                                 c = *data;
753                                 if (c == 'n')
754                                         c = '\n';
755                                 else if (c == 't')
756                                         c = '\t';
757                         }
758                         if (len < (int)sizeof(com_token) - 1)
759                                 com_token[len++] = c;
760                 }
761                 com_token[len] = 0;
762                 if (*data == quote)
763                         data++;
764                 *datapointer = data;
765                 return true;
766         }
767         else if (*data == '\r')
768         {
769                 // translate Mac line ending to UNIX
770                 com_token[len++] = '\n';data++;
771                 com_token[len] = 0;
772                 *datapointer = data;
773                 return true;
774         }
775         else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
776         {
777                 // single character
778                 com_token[len++] = *data++;
779                 com_token[len] = 0;
780                 *datapointer = data;
781                 return true;
782         }
783         else
784         {
785                 // regular word
786                 for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
787                         if (len < (int)sizeof(com_token) - 1)
788                                 com_token[len++] = *data;
789                 com_token[len] = 0;
790                 *datapointer = data;
791                 return true;
792         }
793 }
794
795 /*
796 ==============
797 COM_ParseToken_Console
798
799 Parse a token out of a string, behaving like the qwcl console
800 ==============
801 */
802 int COM_ParseToken_Console(const char **datapointer)
803 {
804         int len;
805         const char *data = *datapointer;
806
807         len = 0;
808         com_token[0] = 0;
809
810         if (!data)
811         {
812                 *datapointer = NULL;
813                 return false;
814         }
815
816 // skip whitespace
817 skipwhite:
818         for (;ISWHITESPACE(*data);data++)
819         {
820                 if (*data == 0)
821                 {
822                         // end of file
823                         *datapointer = NULL;
824                         return false;
825                 }
826         }
827
828         if (*data == '/' && data[1] == '/')
829         {
830                 // comment
831                 while (*data && *data != '\n' && *data != '\r')
832                         data++;
833                 goto skipwhite;
834         }
835         else if (*data == '\"')
836         {
837                 // quoted string
838                 for (data++;*data && *data != '\"';data++)
839                 {
840                         // allow escaped " and \ case
841                         if (*data == '\\' && (data[1] == '\"' || data[1] == '\\'))
842                                 data++;
843                         if (len < (int)sizeof(com_token) - 1)
844                                 com_token[len++] = *data;
845                 }
846                 com_token[len] = 0;
847                 if (*data == '\"')
848                         data++;
849                 *datapointer = data;
850         }
851         else
852         {
853                 // regular word
854                 for (;!ISWHITESPACE(*data);data++)
855                         if (len < (int)sizeof(com_token) - 1)
856                                 com_token[len++] = *data;
857                 com_token[len] = 0;
858                 *datapointer = data;
859         }
860
861         return true;
862 }
863
864 /*
865 ===============
866 Com_CalcRoll
867
868 Used by view and sv_user
869 ===============
870 */
871 float Com_CalcRoll (const vec3_t angles, const vec3_t velocity, const vec_t angleval, const vec_t velocityval)
872 {
873         vec3_t  forward, right, up;
874         float   sign;
875         float   side;
876
877         AngleVectors (angles, forward, right, up);
878         side = DotProduct (velocity, right);
879         sign = side < 0 ? -1 : 1;
880         side = fabs(side);
881
882         if (side < velocityval)
883                 side = side * angleval / velocityval;
884         else
885                 side = angleval;
886
887         return side*sign;
888
889 }
890
891 //===========================================================================
892
893 /*
894 ================
895 COM_Init
896 ================
897 */
898 void COM_Init_Commands (void)
899 {
900         int i, j, n;
901         char com_cmdline[MAX_INPUTLINE];
902
903         Cvar_RegisterVariable (&registered);
904         Cvar_RegisterVariable (&cmdline);
905         Cvar_RegisterVariable(&cl_playermodel);
906         Cvar_RegisterVirtual(&cl_playermodel, "_cl_playermodel");
907         Cvar_RegisterVariable(&cl_playerskin);
908         Cvar_RegisterVirtual(&cl_playerskin, "_cl_playerskin");
909
910         // reconstitute the command line for the cmdline externally visible cvar
911         n = 0;
912         for (j = 0;(j < MAX_NUM_ARGVS) && (j < sys.argc);j++)
913         {
914                 i = 0;
915                 if (strstr(sys.argv[j], " "))
916                 {
917                         // arg contains whitespace, store quotes around it
918                         // This condition checks whether we can allow to put
919                         // in two quote characters.
920                         if (n >= ((int)sizeof(com_cmdline) - 2))
921                                 break;
922                         com_cmdline[n++] = '\"';
923                         // This condition checks whether we can allow one
924                         // more character and a quote character.
925                         while ((n < ((int)sizeof(com_cmdline) - 2)) && sys.argv[j][i])
926                                 // FIXME: Doesn't quote special characters.
927                                 com_cmdline[n++] = sys.argv[j][i++];
928                         com_cmdline[n++] = '\"';
929                 }
930                 else
931                 {
932                         // This condition checks whether we can allow one
933                         // more character.
934                         while ((n < ((int)sizeof(com_cmdline) - 1)) && sys.argv[j][i])
935                                 com_cmdline[n++] = sys.argv[j][i++];
936                 }
937                 if (n < ((int)sizeof(com_cmdline) - 1))
938                         com_cmdline[n++] = ' ';
939                 else
940                         break;
941         }
942         com_cmdline[n] = 0;
943         Cvar_SetQuick(&cmdline, com_cmdline);
944 }
945
946 /*
947 ============
948 va
949
950 varargs print into provided buffer, returns buffer (so that it can be called in-line, unlike dpsnprintf)
951 ============
952 */
953 char *va(char *buf, size_t buflen, const char *format, ...)
954 {
955         va_list argptr;
956
957         va_start (argptr, format);
958         dpvsnprintf (buf, buflen, format,argptr);
959         va_end (argptr);
960
961         return buf;
962 }
963
964
965 //======================================
966
967 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
968
969 #undef snprintf
970 #undef vsnprintf
971
972 #ifdef WIN32
973 # define snprintf _snprintf
974 # define vsnprintf _vsnprintf
975 #endif
976
977
978 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
979 {
980         va_list args;
981         int result;
982
983         va_start (args, format);
984         result = dpvsnprintf (buffer, buffersize, format, args);
985         va_end (args);
986
987         return result;
988 }
989
990
991 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
992 {
993         int result;
994
995 #if _MSC_VER >= 1400
996         result = _vsnprintf_s (buffer, buffersize, _TRUNCATE, format, args);
997 #else
998         result = vsnprintf (buffer, buffersize, format, args);
999 #endif
1000         if (result < 0 || (size_t)result >= buffersize)
1001         {
1002                 buffer[buffersize - 1] = '\0';
1003                 return -1;
1004         }
1005
1006         return result;
1007 }
1008
1009
1010 //======================================
1011
1012 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1013 {
1014         if (size_out == 0)
1015                 return;
1016
1017         if(utf8_enable.integer)
1018         {
1019                 *out = 0;
1020                 while(*in && size_out > 1)
1021                 {
1022                         int n;
1023                         Uchar ch = u8_getchar_utf8_enabled(in, &in);
1024                         ch = u8_tolower(ch);
1025                         n = u8_fromchar(ch, out, size_out);
1026                         if(n <= 0)
1027                                 break;
1028                         out += n;
1029                         size_out -= n;
1030                 }
1031                 return;
1032         }
1033
1034         while (*in && size_out > 1)
1035         {
1036                 if (*in >= 'A' && *in <= 'Z')
1037                         *out++ = *in++ + 'a' - 'A';
1038                 else
1039                         *out++ = *in++;
1040                 size_out--;
1041         }
1042         *out = '\0';
1043 }
1044
1045 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1046 {
1047         if (size_out == 0)
1048                 return;
1049
1050         if(utf8_enable.integer)
1051         {
1052                 *out = 0;
1053                 while(*in && size_out > 1)
1054                 {
1055                         int n;
1056                         Uchar ch = u8_getchar_utf8_enabled(in, &in);
1057                         ch = u8_toupper(ch);
1058                         n = u8_fromchar(ch, out, size_out);
1059                         if(n <= 0)
1060                                 break;
1061                         out += n;
1062                         size_out -= n;
1063                 }
1064                 return;
1065         }
1066
1067         while (*in && size_out > 1)
1068         {
1069                 if (*in >= 'a' && *in <= 'z')
1070                         *out++ = *in++ + 'A' - 'a';
1071                 else
1072                         *out++ = *in++;
1073                 size_out--;
1074         }
1075         *out = '\0';
1076 }
1077
1078 int COM_StringBeginsWith(const char *s, const char *match)
1079 {
1080         for (;*s && *match;s++, match++)
1081                 if (*s != *match)
1082                         return false;
1083         return true;
1084 }
1085
1086 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1087 {
1088         int argc, commentprefixlength;
1089         char *tokenbufend;
1090         const char *l;
1091         argc = 0;
1092         tokenbufend = tokenbuf + tokenbufsize;
1093         l = *text;
1094         commentprefixlength = 0;
1095         if (commentprefix)
1096                 commentprefixlength = (int)strlen(commentprefix);
1097         while (*l && *l != '\n' && *l != '\r')
1098         {
1099                 if (!ISWHITESPACE(*l))
1100                 {
1101                         if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1102                         {
1103                                 while (*l && *l != '\n' && *l != '\r')
1104                                         l++;
1105                                 break;
1106                         }
1107                         if (argc >= maxargc)
1108                                 return -1;
1109                         argv[argc++] = tokenbuf;
1110                         if (*l == '"')
1111                         {
1112                                 l++;
1113                                 while (*l && *l != '"')
1114                                 {
1115                                         if (tokenbuf >= tokenbufend)
1116                                                 return -1;
1117                                         *tokenbuf++ = *l++;
1118                                 }
1119                                 if (*l == '"')
1120                                         l++;
1121                         }
1122                         else
1123                         {
1124                                 while (!ISWHITESPACE(*l))
1125                                 {
1126                                         if (tokenbuf >= tokenbufend)
1127                                                 return -1;
1128                                         *tokenbuf++ = *l++;
1129                                 }
1130                         }
1131                         if (tokenbuf >= tokenbufend)
1132                                 return -1;
1133                         *tokenbuf++ = 0;
1134                 }
1135                 else
1136                         l++;
1137         }
1138         // line endings:
1139         // UNIX: \n
1140         // Mac: \r
1141         // Windows: \r\n
1142         if (*l == '\r')
1143                 l++;
1144         if (*l == '\n')
1145                 l++;
1146         *text = l;
1147         return argc;
1148 }
1149
1150 /*
1151 ============
1152 COM_StringLengthNoColors
1153
1154 calculates the visible width of a color coded string.
1155
1156 *valid is filled with TRUE if the string is a valid colored string (that is, if
1157 it does not end with an unfinished color code). If it gets filled with FALSE, a
1158 fix would be adding a STRING_COLOR_TAG at the end of the string.
1159
1160 valid can be set to NULL if the caller doesn't care.
1161
1162 For size_s, specify the maximum number of characters from s to use, or 0 to use
1163 all characters until the zero terminator.
1164 ============
1165 */
1166 size_t
1167 COM_StringLengthNoColors(const char *s, size_t size_s, qbool *valid)
1168 {
1169         const char *end = size_s ? (s + size_s) : NULL;
1170         size_t len = 0;
1171         for(;;)
1172         {
1173                 switch((s == end) ? 0 : *s)
1174                 {
1175                         case 0:
1176                                 if(valid)
1177                                         *valid = true;
1178                                 return len;
1179                         case STRING_COLOR_TAG:
1180                                 ++s;
1181                                 switch((s == end) ? 0 : *s)
1182                                 {
1183                                         case STRING_COLOR_RGB_TAG_CHAR:
1184                                                 if (s+1 != end && isxdigit(s[1]) &&
1185                                                         s+2 != end && isxdigit(s[2]) &&
1186                                                         s+3 != end && isxdigit(s[3]) )
1187                                                 {
1188                                                         s+=3;
1189                                                         break;
1190                                                 }
1191                                                 ++len; // STRING_COLOR_TAG
1192                                                 ++len; // STRING_COLOR_RGB_TAG_CHAR
1193                                                 break;
1194                                         case 0: // ends with unfinished color code!
1195                                                 ++len;
1196                                                 if(valid)
1197                                                         *valid = false;
1198                                                 return len;
1199                                         case STRING_COLOR_TAG: // escaped ^
1200                                                 ++len;
1201                                                 break;
1202                                         case '0': case '1': case '2': case '3': case '4':
1203                                         case '5': case '6': case '7': case '8': case '9': // color code
1204                                                 break;
1205                                         default: // not a color code
1206                                                 ++len; // STRING_COLOR_TAG
1207                                                 ++len; // the character
1208                                                 break;
1209                                 }
1210                                 break;
1211                         default:
1212                                 ++len;
1213                                 break;
1214                 }
1215                 ++s;
1216         }
1217         // never get here
1218 }
1219
1220 /*
1221 ============
1222 COM_StringDecolorize
1223
1224 removes color codes from a string.
1225
1226 If escape_carets is true, the resulting string will be safe for printing. If
1227 escape_carets is false, the function will just strip color codes (for logging
1228 for example).
1229
1230 If the output buffer size did not suffice for converting, the function returns
1231 FALSE. Generally, if escape_carets is false, the output buffer needs
1232 strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)*1.5+2
1233 bytes. In any case, the function makes sure that the resulting string is
1234 zero terminated.
1235
1236 For size_in, specify the maximum number of characters from in to use, or 0 to use
1237 all characters until the zero terminator.
1238 ============
1239 */
1240 qbool
1241 COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qbool escape_carets)
1242 {
1243 #define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return false; } } while(0)
1244         const char *end = size_in ? (in + size_in) : NULL;
1245         if(size_out < 1)
1246                 return false;
1247         for(;;)
1248         {
1249                 switch((in == end) ? 0 : *in)
1250                 {
1251                         case 0:
1252                                 *out++ = 0;
1253                                 return true;
1254                         case STRING_COLOR_TAG:
1255                                 ++in;
1256                                 switch((in == end) ? 0 : *in)
1257                                 {
1258                                         case STRING_COLOR_RGB_TAG_CHAR:
1259                                                 if (in+1 != end && isxdigit(in[1]) &&
1260                                                         in+2 != end && isxdigit(in[2]) &&
1261                                                         in+3 != end && isxdigit(in[3]) )
1262                                                 {
1263                                                         in+=3;
1264                                                         break;
1265                                                 }
1266                                                 APPEND(STRING_COLOR_TAG);
1267                                                 if(escape_carets)
1268                                                         APPEND(STRING_COLOR_TAG);
1269                                                 APPEND(STRING_COLOR_RGB_TAG_CHAR);
1270                                                 break;
1271                                         case 0: // ends with unfinished color code!
1272                                                 APPEND(STRING_COLOR_TAG);
1273                                                 // finish the code by appending another caret when escaping
1274                                                 if(escape_carets)
1275                                                         APPEND(STRING_COLOR_TAG);
1276                                                 *out++ = 0;
1277                                                 return true;
1278                                         case STRING_COLOR_TAG: // escaped ^
1279                                                 APPEND(STRING_COLOR_TAG);
1280                                                 // append a ^ twice when escaping
1281                                                 if(escape_carets)
1282                                                         APPEND(STRING_COLOR_TAG);
1283                                                 break;
1284                                         case '0': case '1': case '2': case '3': case '4':
1285                                         case '5': case '6': case '7': case '8': case '9': // color code
1286                                                 break;
1287                                         default: // not a color code
1288                                                 APPEND(STRING_COLOR_TAG);
1289                                                 APPEND(*in);
1290                                                 break;
1291                                 }
1292                                 break;
1293                         default:
1294                                 APPEND(*in);
1295                                 break;
1296                 }
1297                 ++in;
1298         }
1299         // never get here
1300 #undef APPEND
1301 }
1302
1303 //========================================================
1304 // strlcat and strlcpy, from OpenBSD
1305
1306 /*
1307  * Copyright (c) 1998, 2015 Todd C. Miller <millert@openbsd.org>
1308  *
1309  * Permission to use, copy, modify, and distribute this software for any
1310  * purpose with or without fee is hereby granted, provided that the above
1311  * copyright notice and this permission notice appear in all copies.
1312  *
1313  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1314  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1315  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1316  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1317  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1318  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1319  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1320  */
1321
1322 /*      $OpenBSD: strlcat.c,v 1.19 2019/01/25 00:19:25 millert Exp $    */
1323 /*      $OpenBSD: strlcpy.c,v 1.16 2019/01/25 00:19:25 millert Exp $    */
1324
1325
1326 #ifndef HAVE_STRLCAT
1327 size_t
1328 strlcat(char *dst, const char *src, size_t dsize)
1329 {
1330         const char *odst = dst;
1331         const char *osrc = src;
1332         size_t n = dsize;
1333         size_t dlen;
1334
1335         /* Find the end of dst and adjust bytes left but don't go past end. */
1336         while (n-- != 0 && *dst != '\0')
1337                 dst++;
1338         dlen = dst - odst;
1339         n = dsize - dlen;
1340
1341         if (n-- == 0)
1342                 return(dlen + strlen(src));
1343         while (*src != '\0') {
1344                 if (n != 0) {
1345                         *dst++ = *src;
1346                         n--;
1347                 }
1348                 src++;
1349         }
1350         *dst = '\0';
1351
1352         return(dlen + (src - osrc));    /* count does not include NUL */
1353 }
1354 #endif  // #ifndef HAVE_STRLCAT
1355
1356
1357 #ifndef HAVE_STRLCPY
1358 size_t
1359 strlcpy(char *dst, const char *src, size_t dsize)
1360 {
1361         const char *osrc = src;
1362         size_t nleft = dsize;
1363
1364         /* Copy as many bytes as will fit. */
1365         if (nleft != 0) {
1366                 while (--nleft != 0) {
1367                         if ((*dst++ = *src++) == '\0')
1368                                 break;
1369                 }
1370         }
1371
1372         /* Not enough room in dst, add NUL and traverse rest of src. */
1373         if (nleft == 0) {
1374                 if (dsize != 0)
1375                         *dst = '\0';            /* NUL-terminate dst */
1376                 while (*src++)
1377                         ;
1378         }
1379
1380         return(src - osrc - 1); /* count does not include NUL */
1381 }
1382
1383 #endif  // #ifndef HAVE_STRLCPY
1384
1385 void FindFraction(double val, int *num, int *denom, int denomMax)
1386 {
1387         int i;
1388         double bestdiff;
1389         // initialize
1390         bestdiff = fabs(val);
1391         *num = 0;
1392         *denom = 1;
1393
1394         for(i = 1; i <= denomMax; ++i)
1395         {
1396                 int inum = (int) floor(0.5 + val * i);
1397                 double diff = fabs(val - inum / (double)i);
1398                 if(diff < bestdiff)
1399                 {
1400                         bestdiff = diff;
1401                         *num = inum;
1402                         *denom = i;
1403                 }
1404         }
1405 }
1406
1407 // decodes an XPM from C syntax
1408 char **XPM_DecodeString(const char *in)
1409 {
1410         static char *tokens[257];
1411         static char lines[257][512];
1412         size_t line = 0;
1413
1414         // skip until "{" token
1415         while(COM_ParseToken_QuakeC(&in, false) && strcmp(com_token, "{"));
1416
1417         // now, read in succession: string, comma-or-}
1418         while(COM_ParseToken_QuakeC(&in, false))
1419         {
1420                 tokens[line] = lines[line];
1421                 strlcpy(lines[line++], com_token, sizeof(lines[0]));
1422                 if(!COM_ParseToken_QuakeC(&in, false))
1423                         return NULL;
1424                 if(!strcmp(com_token, "}"))
1425                         break;
1426                 if(strcmp(com_token, ","))
1427                         return NULL;
1428                 if(line >= sizeof(tokens) / sizeof(tokens[0]))
1429                         return NULL;
1430         }
1431
1432         return tokens;
1433 }
1434
1435 static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1436 static void base64_3to4(const unsigned char *in, unsigned char *out, int bytes)
1437 {
1438         unsigned char i0 = (bytes > 0) ? in[0] : 0;
1439         unsigned char i1 = (bytes > 1) ? in[1] : 0;
1440         unsigned char i2 = (bytes > 2) ? in[2] : 0;
1441         unsigned char o0 = base64[i0 >> 2];
1442         unsigned char o1 = base64[((i0 << 4) | (i1 >> 4)) & 077];
1443         unsigned char o2 = base64[((i1 << 2) | (i2 >> 6)) & 077];
1444         unsigned char o3 = base64[i2 & 077];
1445         out[0] = (bytes > 0) ? o0 : '?';
1446         out[1] = (bytes > 0) ? o1 : '?';
1447         out[2] = (bytes > 1) ? o2 : '=';
1448         out[3] = (bytes > 2) ? o3 : '=';
1449 }
1450
1451 size_t base64_encode(unsigned char *buf, size_t buflen, size_t outbuflen)
1452 {
1453         size_t blocks, i;
1454         // expand the out-buffer
1455         blocks = (buflen + 2) / 3;
1456         if(blocks*4 > outbuflen)
1457                 return 0;
1458         for(i = blocks; i > 0; )
1459         {
1460                 --i;
1461                 base64_3to4(buf + 3*i, buf + 4*i, (int)(buflen - 3*i));
1462         }
1463         return blocks * 4;
1464 }