1 /*
2 ===========================================================================
3 Copyright (C) 1999 - 2005, Id Software, Inc.
4 Copyright (C) 2000 - 2013, Raven Software, Inc.
5 Copyright (C) 2001 - 2013, Activision, Inc.
6 Copyright (C) 2005 - 2015, ioquake3 contributors
7 Copyright (C) 2013 - 2015, OpenJK contributors
8 
9 This file is part of the OpenJK source code.
10 
11 OpenJK is free software; you can redistribute it and/or modify it
12 under the terms of the GNU General Public License version 2 as
13 published by the Free Software Foundation.
14 
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, see <http://www.gnu.org/licenses/>.
22 ===========================================================================
23 */
24 
25 // q_shared.c -- stateless support routines that are included in each code dll
26 #include "q_shared.h"
27 
28 /*
29 -------------------------
30 GetIDForString
31 -------------------------
32 */
33 
34 
GetIDForString(stringID_table_t * table,const char * string)35 int GetIDForString ( stringID_table_t *table, const char *string )
36 {
37 	int	index = 0;
38 
39 	while ( ( table[index].name != NULL ) &&
40 			( table[index].name[0] != 0 ) )
41 	{
42 		if ( !Q_stricmp( table[index].name, string ) )
43 			return table[index].id;
44 
45 		index++;
46 	}
47 
48 	return -1;
49 }
50 
51 /*
52 -------------------------
53 GetStringForID
54 -------------------------
55 */
56 
GetStringForID(stringID_table_t * table,int id)57 const char *GetStringForID( stringID_table_t *table, int id )
58 {
59 	int	index = 0;
60 
61 	while ( ( table[index].name != NULL ) &&
62 			( table[index].name[0] != 0 ) )
63 	{
64 		if ( table[index].id == id )
65 			return table[index].name;
66 
67 		index++;
68 	}
69 
70 	return NULL;
71 }
72 
73 /*
74 ============
75 COM_SkipPath
76 ============
77 */
COM_SkipPath(char * pathname)78 char *COM_SkipPath (char *pathname)
79 {
80 	char	*last;
81 
82 	last = pathname;
83 	while (*pathname)
84 	{
85 		if (*pathname=='/')
86 			last = pathname+1;
87 		pathname++;
88 	}
89 	return last;
90 }
91 
92 /*
93 ============
94 COM_GetExtension
95 ============
96 */
COM_GetExtension(const char * name)97 const char *COM_GetExtension( const char *name )
98 {
99 	const char *dot = strrchr(name, '.'), *slash;
100 	if (dot && (!(slash = strrchr(name, '/')) || slash < dot))
101 		return dot + 1;
102 	else
103 		return "";
104 }
105 
106 /*
107 ============
108 COM_StripExtension
109 ============
110 */
COM_StripExtension(const char * in,char * out,int destsize)111 void COM_StripExtension( const char *in, char *out, int destsize )
112 {
113 	const char *dot = strrchr(in, '.'), *slash;
114 	if (dot && (!(slash = strrchr(in, '/')) || slash < dot))
115 		destsize = (destsize < dot-in+1 ? destsize : dot-in+1);
116 
117 	if ( in == out && destsize > 1 )
118 		out[destsize-1] = '\0';
119 	else
120 		Q_strncpyz(out, in, destsize);
121 }
122 
123 /*
124 ============
125 COM_CompareExtension
126 
127 string compare the end of the strings and return qtrue if strings match
128 ============
129 */
COM_CompareExtension(const char * in,const char * ext)130 qboolean COM_CompareExtension(const char *in, const char *ext)
131 {
132 	int inlen, extlen;
133 
134 	inlen = strlen(in);
135 	extlen = strlen(ext);
136 
137 	if(extlen <= inlen)
138 	{
139 		in += inlen - extlen;
140 
141 		if(!Q_stricmp(in, ext))
142 			return qtrue;
143 	}
144 
145 	return qfalse;
146 }
147 
148 /*
149 ==================
150 COM_DefaultExtension
151 ==================
152 */
COM_DefaultExtension(char * path,int maxSize,const char * extension)153 void COM_DefaultExtension( char *path, int maxSize, const char *extension )
154 {
155 	const char *dot = strrchr(path, '.'), *slash;
156 	if (dot && (!(slash = strrchr(path, '/')) || slash < dot))
157 		return;
158 	else
159 		Q_strcat(path, maxSize, extension);
160 }
161 
162 /*
163 ============================================================================
164 
165 PARSING
166 
167 ============================================================================
168 */
169 
170 static	char	com_token[MAX_TOKEN_CHARS];
171 static	char	com_parsename[MAX_TOKEN_CHARS];
172 static	int		com_lines;
173 static	int		com_tokenline;
174 
COM_BeginParseSession(const char * name)175 void COM_BeginParseSession( const char *name )
176 {
177 	com_lines = 1;
178 	com_tokenline = 0;
179 	Com_sprintf(com_parsename, sizeof(com_parsename), "%s", name);
180 }
181 
COM_GetCurrentParseLine(void)182 int COM_GetCurrentParseLine( void )
183 {
184 	if ( com_tokenline )
185 	{
186 		return com_tokenline;
187 	}
188 
189 	return com_lines;
190 }
191 
COM_Parse(const char ** data_p)192 char *COM_Parse( const char **data_p )
193 {
194 	return COM_ParseExt( data_p, qtrue );
195 }
196 
COM_ParseError(char * format,...)197 void COM_ParseError( char *format, ... )
198 {
199 	va_list argptr;
200 	static char string[4096];
201 
202 	va_start (argptr, format);
203 	Q_vsnprintf (string, sizeof( string ), format, argptr);
204 	va_end (argptr);
205 
206 	Com_Printf("ERROR: %s, line %d: %s\n", com_parsename, COM_GetCurrentParseLine(), string);
207 }
208 
COM_ParseWarning(char * format,...)209 void COM_ParseWarning( char *format, ... )
210 {
211 	va_list argptr;
212 	static char string[4096];
213 
214 	va_start (argptr, format);
215 	Q_vsnprintf (string, sizeof(string), format, argptr);
216 	va_end (argptr);
217 
218 	Com_Printf("WARNING: %s, line %d: %s\n", com_parsename, COM_GetCurrentParseLine(), string);
219 }
220 
221 /*
222 ==============
223 COM_Parse
224 
225 Parse a token out of a string
226 Will never return NULL, just empty strings
227 
228 If "allowLineBreaks" is qtrue then an empty
229 string will be returned if the next token is
230 a newline.
231 ==============
232 */
SkipWhitespace(const char * data,qboolean * hasNewLines)233 const char *SkipWhitespace( const char *data, qboolean *hasNewLines ) {
234 	int c;
235 
236 	while( (c = *(const unsigned char* /*eurofix*/)data) <= ' ') {
237 		if( !c ) {
238 			return NULL;
239 		}
240 		if( c == '\n' ) {
241 			com_lines++;
242 			*hasNewLines = qtrue;
243 		}
244 		data++;
245 	}
246 
247 	return data;
248 }
249 
COM_Compress(char * data_p)250 int COM_Compress( char *data_p ) {
251 	char *in, *out;
252 	int c;
253 	qboolean newline = qfalse, whitespace = qfalse;
254 
255 	in = out = data_p;
256 	if (in) {
257 		while ((c = *in) != 0) {
258 			// skip double slash comments
259 			if ( c == '/' && in[1] == '/' ) {
260 				while (*in && *in != '\n') {
261 					in++;
262 				}
263 			// skip /* */ comments
264 			} else if ( c == '/' && in[1] == '*' ) {
265 				while ( *in && ( *in != '*' || in[1] != '/' ) )
266 					in++;
267 				if ( *in )
268 					in += 2;
269 				// record when we hit a newline
270 			} else if ( c == '\n' || c == '\r' ) {
271 				newline = qtrue;
272 				in++;
273 				// record when we hit whitespace
274 			} else if ( c == ' ' || c == '\t') {
275 				whitespace = qtrue;
276 				in++;
277 				// an actual token
278 			} else {
279 				// if we have a pending newline, emit it (and it counts as whitespace)
280 				if (newline) {
281 					*out++ = '\n';
282 					newline = qfalse;
283 					whitespace = qfalse;
284 				} if (whitespace) {
285 					*out++ = ' ';
286 					whitespace = qfalse;
287 				}
288 
289 				// copy quoted strings unmolested
290 				if (c == '"') {
291 					*out++ = c;
292 					in++;
293 					while (1) {
294 						c = *in;
295 						if (c && c != '"') {
296 							*out++ = c;
297 							in++;
298 						} else {
299 							break;
300 						}
301 					}
302 					if (c == '"') {
303 						*out++ = c;
304 						in++;
305 					}
306 				} else {
307 					*out = c;
308 					out++;
309 					in++;
310 				}
311 			}
312 		}
313 
314 		*out = 0;
315 	}
316 	return out - data_p;
317 }
318 
COM_ParseExt(const char ** data_p,qboolean allowLineBreaks)319 char *COM_ParseExt( const char **data_p, qboolean allowLineBreaks )
320 {
321 	int c = 0, len;
322 	qboolean hasNewLines = qfalse;
323 	const char *data;
324 
325 	data = *data_p;
326 	len = 0;
327 	com_token[0] = 0;
328 	com_tokenline = 0;
329 
330 	// make sure incoming data is valid
331 	if ( !data )
332 	{
333 		*data_p = NULL;
334 		return com_token;
335 	}
336 
337 	while ( 1 )
338 	{
339 		// skip whitespace
340 		data = SkipWhitespace( data, &hasNewLines );
341 		if ( !data )
342 		{
343 			*data_p = NULL;
344 			return com_token;
345 		}
346 		if ( hasNewLines && !allowLineBreaks )
347 		{
348 			*data_p = data;
349 			return com_token;
350 		}
351 
352 		c = *data;
353 
354 		// skip double slash comments
355 		if ( c == '/' && data[1] == '/' )
356 		{
357 			data += 2;
358 			while (*data && *data != '\n') {
359 				data++;
360 			}
361 		}
362 		// skip /* */ comments
363 		else if ( c=='/' && data[1] == '*' )
364 		{
365 			data += 2;
366 			while ( *data && ( *data != '*' || data[1] != '/' ) )
367 			{
368 				if ( *data == '\n' )
369 				{
370 					com_lines++;
371 				}
372 				data++;
373 			}
374 			if ( *data )
375 			{
376 				data += 2;
377 			}
378 		}
379 		else
380 		{
381 			break;
382 		}
383 	}
384 
385 	// token starts on this line
386 	com_tokenline = com_lines;
387 
388 	// handle quoted strings
389 	if (c == '\"')
390 	{
391 		data++;
392 		while (1)
393 		{
394 			c = *data++;
395 			if (c=='\"' || !c)
396 			{
397 				com_token[len] = 0;
398 				*data_p = ( char * ) data;
399 				return com_token;
400 			}
401 			if ( c == '\n' )
402 			{
403 				com_lines++;
404 			}
405 			if (len < MAX_TOKEN_CHARS - 1)
406 			{
407 				com_token[len] = c;
408 				len++;
409 			}
410 		}
411 	}
412 
413 	// parse a regular word
414 	do
415 	{
416 		if (len < MAX_TOKEN_CHARS - 1)
417 		{
418 			com_token[len] = c;
419 			len++;
420 		}
421 		data++;
422 		c = *data;
423 	} while (c>32);
424 
425 	com_token[len] = 0;
426 
427 	*data_p = ( char * ) data;
428 	return com_token;
429 }
430 
431 /*
432 ===============
433 COM_ParseString
434 ===============
435 */
COM_ParseString(const char ** data,const char ** s)436 qboolean COM_ParseString( const char **data, const char **s )
437 {
438 //	*s = COM_ParseExt( data, qtrue );
439 	*s = COM_ParseExt( data, qfalse );
440 	if ( s[0] == 0 )
441 	{
442 		COM_ParseWarning( "COM_ParseString: unexpected EOF" );
443 		return qtrue;
444 	}
445 	return qfalse;
446 }
447 
448 /*
449 ===============
450 COM_ParseInt
451 ===============
452 */
COM_ParseInt(const char ** data,int * i)453 qboolean COM_ParseInt( const char **data, int *i )
454 {
455 	const char	*token;
456 
457 	token = COM_ParseExt( data, qfalse );
458 	if ( token[0] == 0 )
459 	{
460 		COM_ParseWarning( "COM_ParseInt: unexpected EOF" );
461 		return qtrue;
462 	}
463 
464 	*i = atoi( token );
465 	return qfalse;
466 }
467 
468 /*
469 ===============
470 COM_ParseFloat
471 ===============
472 */
COM_ParseFloat(const char ** data,float * f)473 qboolean COM_ParseFloat( const char **data, float *f )
474 {
475 	const char	*token;
476 
477 	token = COM_ParseExt( data, qfalse );
478 	if ( token[0] == 0 )
479 	{
480 		COM_ParseWarning( "COM_ParseFloat: unexpected EOF" );
481 		return qtrue;
482 	}
483 
484 	*f = atof( token );
485 	return qfalse;
486 }
487 
488 /*
489 ===============
490 COM_ParseVec4
491 ===============
492 */
COM_ParseVec4(const char ** buffer,vec4_t * c)493 qboolean COM_ParseVec4( const char **buffer, vec4_t *c)
494 {
495 	int i;
496 	float f;
497 
498 	for (i = 0; i < 4; i++)
499 	{
500 		if (COM_ParseFloat(buffer, &f))
501 		{
502 			return qtrue;
503 		}
504 		(*c)[i] = f;
505 	}
506 	return qfalse;
507 }
508 
509 /*
510 ==================
511 COM_MatchToken
512 ==================
513 */
COM_MatchToken(const char ** buf_p,char * match)514 void COM_MatchToken( const char **buf_p, char *match ) {
515 	char	*token;
516 
517 	token = COM_Parse( buf_p );
518 	if ( strcmp( token, match ) ) {
519 		Com_Error( ERR_DROP, "MatchToken: %s != %s", token, match );
520 	}
521 }
522 
523 
524 /*
525 =================
526 SkipBracedSection
527 
528 The next token should be an open brace or set depth to 1 if already parsed it.
529 Skips until a matching close brace is found.
530 Internal brace depths are properly skipped.
531 =================
532 */
SkipBracedSection(const char ** program,int depth)533 qboolean SkipBracedSection (const char **program, int depth) {
534 	char			*token;
535 
536 	do {
537 		token = COM_ParseExt( program, qtrue );
538 		if( token[1] == 0 ) {
539 			if( token[0] == '{' ) {
540 				depth++;
541 			}
542 			else if( token[0] == '}' ) {
543 				depth--;
544 			}
545 		}
546 	} while( depth && *program );
547 
548 	return (qboolean)( depth == 0 );
549 }
550 
551 /*
552 =================
553 SkipRestOfLine
554 =================
555 */
SkipRestOfLine(const char ** data)556 void SkipRestOfLine ( const char **data ) {
557 	const char	*p;
558 	int		c;
559 
560 	p = *data;
561 
562 	if ( !*p )
563 		return;
564 
565 	while ( (c = *p++) != 0 ) {
566 		if ( c == '\n' ) {
567 			com_lines++;
568 			break;
569 		}
570 	}
571 
572 	*data = p;
573 }
574 
575 
Parse1DMatrix(const char ** buf_p,int x,float * m)576 void Parse1DMatrix (const char **buf_p, int x, float *m) {
577 	char	*token;
578 	int		i;
579 
580 	COM_MatchToken( buf_p, "(" );
581 
582 	for (i = 0 ; i < x ; i++) {
583 		token = COM_Parse(buf_p);
584 		m[i] = atof(token);
585 	}
586 
587 	COM_MatchToken( buf_p, ")" );
588 }
589 
Parse2DMatrix(const char ** buf_p,int y,int x,float * m)590 void Parse2DMatrix (const char **buf_p, int y, int x, float *m) {
591 	int		i;
592 
593 	COM_MatchToken( buf_p, "(" );
594 
595 	for (i = 0 ; i < y ; i++) {
596 		Parse1DMatrix (buf_p, x, m + i * x);
597 	}
598 
599 	COM_MatchToken( buf_p, ")" );
600 }
601 
Parse3DMatrix(const char ** buf_p,int z,int y,int x,float * m)602 void Parse3DMatrix (const char **buf_p, int z, int y, int x, float *m) {
603 	int		i;
604 
605 	COM_MatchToken( buf_p, "(" );
606 
607 	for (i = 0 ; i < z ; i++) {
608 		Parse2DMatrix (buf_p, y, x, m + i * x*y);
609 	}
610 
611 	COM_MatchToken( buf_p, ")" );
612 }
613 
614 /*
615 ===================
616 Com_HexStrToInt
617 ===================
618 */
Com_HexStrToInt(const char * str)619 int Com_HexStrToInt( const char *str )
620 {
621 	if ( !str || !str[ 0 ] )
622 		return -1;
623 
624 	// check for hex code
625 	if( str[ 0 ] == '0' && str[ 1 ] == 'x' )
626 	{
627 		int  n = 0;
628 		size_t i;
629 
630 		for( i = 2; i < strlen( str ); i++ )
631 		{
632 			char digit;
633 
634 			n *= 16;
635 
636 			digit = tolower( str[ i ] );
637 
638 			if( digit >= '0' && digit <= '9' )
639 				digit -= '0';
640 			else if( digit >= 'a' && digit <= 'f' )
641 				digit = digit - 'a' + 10;
642 			else
643 				return -1;
644 
645 			n += digit;
646 		}
647 
648 		return n;
649 	}
650 
651 	return -1;
652 }
653 
654 /*
655 ============================================================================
656 
657 					LIBRARY REPLACEMENT FUNCTIONS
658 
659 ============================================================================
660 */
661 
Com_sprintf(char * dest,int size,const char * fmt,...)662 int QDECL Com_sprintf( char *dest, int size, const char *fmt, ...) {
663 	int		len;
664 	va_list		argptr;
665 
666 	va_start (argptr,fmt);
667 	len = Q_vsnprintf(dest, size, fmt, argptr);
668 	va_end (argptr);
669 
670 	if(len >= size)
671 		Com_Printf("Com_sprintf: Output length %d too short, require %d bytes.\n", size, len + 1);
672 
673 	return len;
674 }
675 
FloatAsInt(float f)676 int FloatAsInt( float f ) {
677 	byteAlias_t fi;
678 	fi.f = f;
679 	return fi.i;
680 }
681 
682 /*
683 ============
684 va
685 
686 does a varargs printf into a temp buffer, so I don't need to have
687 varargs versions of all text functions.
688 FIXME: make this buffer size safe someday
689 ============
690 */
691 #define	MAX_VA_STRING	32000
692 #define MAX_VA_BUFFERS 4
693 
va(const char * format,...)694 char * QDECL va( const char *format, ... )
695 {
696 	va_list		argptr;
697 	static char	string[MAX_VA_BUFFERS][MAX_VA_STRING];	// in case va is called by nested functions
698 	static int	index = 0;
699 	char		*buf;
700 
701 	va_start( argptr, format );
702 	buf = (char *)&string[index++ & 3];
703 	Q_vsnprintf( buf, sizeof(*string), format, argptr );
704 	va_end( argptr );
705 
706 	return buf;
707 }
708 
709 /*
710 ============
711 Com_TruncateLongString
712 
713 Assumes buffer is atleast TRUNCATE_LENGTH big
714 ============
715 */
Com_TruncateLongString(char * buffer,const char * s)716 void Com_TruncateLongString( char *buffer, const char *s ) {
717 	int length = strlen( s );
718 
719 	if ( length <= TRUNCATE_LENGTH )
720 		Q_strncpyz( buffer, s, TRUNCATE_LENGTH );
721 	else {
722 		Q_strncpyz( buffer, s, (TRUNCATE_LENGTH/2) - 3 );
723 		Q_strcat( buffer, TRUNCATE_LENGTH, " ... " );
724 		Q_strcat( buffer, TRUNCATE_LENGTH, s + length - (TRUNCATE_LENGTH/2) + 3 );
725 	}
726 }
727 
728 /*
729 =====================================================================
730 
731   INFO STRINGS
732 
733 =====================================================================
734 */
735 
736 /*
737 ===============
738 Info_ValueForKey
739 
740 Searches the string for the given
741 key and returns the associated value, or an empty string.
742 FIXME: overflow check?
743 ===============
744 */
Info_ValueForKey(const char * s,const char * key)745 char *Info_ValueForKey( const char *s, const char *key ) {
746 	char	pkey[BIG_INFO_KEY];
747 	static	char value[2][BIG_INFO_VALUE];	// use two buffers so compares
748 											// work without stomping on each other
749 	static	int	valueindex = 0;
750 	char	*o;
751 
752 	if ( !s || !key ) {
753 		return "";
754 	}
755 
756 	if ( strlen( s ) >= BIG_INFO_STRING ) {
757 		Com_Error( ERR_DROP, "Info_ValueForKey: oversize infostring" );
758 	}
759 
760 	valueindex ^= 1;
761 	if (*s == '\\')
762 		s++;
763 	while (1)
764 	{
765 		o = pkey;
766 		while (*s != '\\')
767 		{
768 			if (!*s)
769 				return "";
770 			*o++ = *s++;
771 		}
772 		*o = 0;
773 		s++;
774 
775 		o = value[valueindex];
776 
777 		while (*s != '\\' && *s)
778 		{
779 			*o++ = *s++;
780 		}
781 		*o = 0;
782 
783 		if (!Q_stricmp (key, pkey) )
784 			return value[valueindex];
785 
786 		if (!*s)
787 			break;
788 		s++;
789 	}
790 
791 	return "";
792 }
793 
794 /*
795 ===================
796 Info_NextPair
797 
798 Used to itterate through all the key/value pairs in an info string
799 Return qfalse if we discover the infostring is invalid
800 ===================
801 */
Info_NextPair(const char ** head,char * key,char * value)802 qboolean Info_NextPair( const char **head, char *key, char *value ) {
803 	char *o;
804 	const char *s = *head;
805 
806 	if ( *s == '\\' )
807 		s++;
808 	key[0] = 0;
809 	value[0] = 0;
810 
811 	o = key;
812 	while ( *s != '\\' ) {
813 		if ( !*s ) {
814 			key[0] = 0;
815 			*head  = s;
816 			return qtrue;
817 		}
818 		*o++ = *s++;
819 	}
820 	*o = 0;
821 	s++;
822 
823 	// If they key is empty at this point with a slash after it
824 	// then this is considered invalid, possibly an attempt at hacked userinfo strings
825 	if ( !key[0] )
826 		return qfalse;
827 
828 	o = value;
829 	while ( *s != '\\' && *s ) {
830 		*o++ = *s++;
831 	}
832 	*o = 0;
833 
834 	*head = s;
835 
836 	return qtrue;
837 }
838 
839 /*
840 ===================
841 Info_RemoveKey
842 ===================
843 */
Info_RemoveKey(char * s,const char * key)844 void Info_RemoveKey( char *s, const char *key ) {
845 	char	*start = NULL, *o = NULL;
846 	char	pkey[MAX_INFO_KEY] = {0};
847 	char	value[MAX_INFO_VALUE] = {0};
848 
849 	if ( strlen( s ) >= MAX_INFO_STRING ) {
850 		Com_Error( ERR_DROP, "Info_RemoveKey: oversize infostring" );
851 		return;
852 	}
853 
854 	if (strchr (key, '\\')) {
855 		return;
856 	}
857 
858 	while (1)
859 	{
860 		start = s;
861 		if (*s == '\\')
862 			s++;
863 		o = pkey;
864 		while (*s != '\\')
865 		{
866 			if (!*s)
867 				return;
868 			*o++ = *s++;
869 		}
870 		*o = 0;
871 		s++;
872 
873 		o = value;
874 		while (*s != '\\' && *s)
875 		{
876 			if (!*s)
877 				return;
878 			*o++ = *s++;
879 		}
880 		*o = 0;
881 
882 		//OJKNOTE: static analysis pointed out pkey may not be null-terminated
883 		if (!strcmp (key, pkey) )
884 		{
885 			memmove(start, s, strlen(s) + 1);	// remove this part
886 			return;
887 		}
888 
889 		if (!*s)
890 			return;
891 	}
892 }
893 
894 /*
895 ===================
896 Info_RemoveKey_Big
897 ===================
898 */
Info_RemoveKey_Big(char * s,const char * key)899 void Info_RemoveKey_Big( char *s, const char *key ) {
900 	char	*start;
901 	static char	pkey[BIG_INFO_KEY], value[BIG_INFO_VALUE];
902 	char	*o;
903 
904 	pkey[0] = value[0] = '\0';
905 
906 	if ( strlen( s ) >= BIG_INFO_STRING ) {
907 		Com_Error( ERR_DROP, "Info_RemoveKey_Big: oversize infostring" );
908 		return;
909 	}
910 
911 	if (strchr (key, '\\')) {
912 		return;
913 	}
914 
915 	while (1)
916 	{
917 		start = s;
918 		if (*s == '\\')
919 			s++;
920 		o = pkey;
921 		while (*s != '\\')
922 		{
923 			if (!*s)
924 				return;
925 			*o++ = *s++;
926 		}
927 		*o = 0;
928 		s++;
929 
930 		o = value;
931 		while (*s != '\\' && *s)
932 		{
933 			if (!*s)
934 				return;
935 			*o++ = *s++;
936 		}
937 		*o = 0;
938 
939 		//OJKNOTE: static analysis pointed out pkey may not be null-terminated
940 		if (!strcmp (key, pkey) )
941 		{
942 			memmove(start, s, strlen(s) + 1);	// remove this part
943 			return;
944 		}
945 
946 		if (!*s)
947 			return;
948 	}
949 
950 }
951 
952 /*
953 ==================
954 Info_Validate
955 
956 Some characters are illegal in info strings because they
957 can mess up the server's parsing
958 ==================
959 */
Info_Validate(const char * s)960 qboolean Info_Validate( const char *s ) {
961 	const char *c = s;
962 
963 	while ( *c != '\0' )
964 	{
965 		if( !Q_isprint( *c ) )
966 			return qfalse;
967 
968 		if( *c == '\"' )
969 			return qfalse;
970 
971 		if( *c == ';' )
972 			return qfalse;
973 
974 		++c;
975 	}
976 
977 	return qtrue;
978 }
979 
980 /*
981 ==================
982 Info_SetValueForKey
983 
984 Changes or adds a key/value pair
985 ==================
986 */
Info_SetValueForKey(char * s,const char * key,const char * value)987 void Info_SetValueForKey( char *s, const char *key, const char *value ) {
988 	char	newi[MAX_INFO_STRING];
989 	const char* blacklist = "\\;\"";
990 
991 	if ( strlen( s ) >= MAX_INFO_STRING ) {
992 		Com_Error( ERR_DROP, "Info_SetValueForKey: oversize infostring" );
993 	}
994 
995 	for(; *blacklist; ++blacklist)
996 	{
997 		if (strchr (key, *blacklist) || strchr (value, *blacklist))
998 		{
999 			Com_Printf (S_COLOR_YELLOW "Can't use keys or values with a '%c': %s = %s\n", *blacklist, key, value);
1000 			return;
1001 		}
1002 	}
1003 
1004 	Info_RemoveKey (s, key);
1005 	if (!value || !strlen(value))
1006 		return;
1007 
1008 	Com_sprintf (newi, sizeof(newi), "\\%s\\%s", key, value);
1009 
1010 	if (strlen(newi) + strlen(s) >= MAX_INFO_STRING)
1011 	{
1012 		Com_Printf ("Info string length exceeded: %s\n", s);
1013 		return;
1014 	}
1015 
1016 	strcat (newi, s);
1017 	strcpy (s, newi);
1018 }
1019 
1020 /*
1021 ==================
1022 Info_SetValueForKey_Big
1023 
1024 Changes or adds a key/value pair
1025 Includes and retains zero-length values
1026 ==================
1027 */
Info_SetValueForKey_Big(char * s,const char * key,const char * value)1028 void Info_SetValueForKey_Big( char *s, const char *key, const char *value ) {
1029 	char	newi[BIG_INFO_STRING];
1030 	const char* blacklist = "\\;\"";
1031 
1032 	if ( strlen( s ) >= BIG_INFO_STRING ) {
1033 		Com_Error( ERR_DROP, "Info_SetValueForKey_Big: oversize infostring" );
1034 	}
1035 
1036 	for(; *blacklist; ++blacklist)
1037 	{
1038 		if (strchr (key, *blacklist) || strchr (value, *blacklist))
1039 		{
1040 			Com_Printf (S_COLOR_YELLOW "Can't use keys or values with a '%c': %s = %s\n", *blacklist, key, value);
1041 			return;
1042 		}
1043 	}
1044 
1045 	Info_RemoveKey_Big (s, key);
1046 	if (!value)
1047 		return;
1048 
1049 	Com_sprintf (newi, sizeof(newi), "\\%s\\%s", key, value);
1050 
1051 	if (strlen(newi) + strlen(s) >= BIG_INFO_STRING)
1052 	{
1053 		Com_Printf ("BIG Info string length exceeded\n");
1054 		return;
1055 	}
1056 
1057 	strcat (s, newi);
1058 }
1059 
1060 /*
1061 ==================
1062 Com_CharIsOneOfCharset
1063 ==================
1064 */
Com_CharIsOneOfCharset(char c,char * set)1065 static qboolean Com_CharIsOneOfCharset( char c, char *set ) {
1066 	size_t i;
1067 
1068 	for ( i=0; i<strlen( set ); i++ ) {
1069 		if ( set[i] == c )
1070 			return qtrue;
1071 	}
1072 
1073 	return qfalse;
1074 }
1075 
1076 /*
1077 ==================
1078 Com_SkipCharset
1079 ==================
1080 */
Com_SkipCharset(char * s,char * sep)1081 char *Com_SkipCharset( char *s, char *sep ) {
1082 	char *p = s;
1083 
1084 	while ( p ) {
1085 		if ( Com_CharIsOneOfCharset( *p, sep ) )
1086 			p++;
1087 		else
1088 			break;
1089 	}
1090 
1091 	return p;
1092 }
1093 
1094 /*
1095 ==================
1096 Com_SkipTokens
1097 ==================
1098 */
Com_SkipTokens(char * s,int numTokens,char * sep)1099 char *Com_SkipTokens( char *s, int numTokens, char *sep ) {
1100 	int sepCount = 0;
1101 	char *p = s;
1102 
1103 	while ( sepCount < numTokens ) {
1104 		if ( Com_CharIsOneOfCharset( *p++, sep ) ) {
1105 			sepCount++;
1106 			while ( Com_CharIsOneOfCharset( *p, sep ) )
1107 				p++;
1108 		}
1109 		else if ( *p == '\0' )
1110 			break;
1111 	}
1112 
1113 	if ( sepCount == numTokens )
1114 		return p;
1115 	else
1116 		return s;
1117 }
1118 
Q_InBitflags(const uint32_t * bits,int index,uint32_t bitsPerByte)1119 qboolean Q_InBitflags( const uint32_t *bits, int index, uint32_t bitsPerByte ) {
1120 	return ( bits[index / bitsPerByte] & (1 << (index % bitsPerByte)) ) ? qtrue : qfalse;
1121 }
1122 
Q_AddToBitflags(uint32_t * bits,int index,uint32_t bitsPerByte)1123 void Q_AddToBitflags( uint32_t *bits, int index, uint32_t bitsPerByte ) {
1124 	bits[index / bitsPerByte] |= (1 << (index % bitsPerByte));
1125 }
1126 
Q_RemoveFromBitflags(uint32_t * bits,int index,uint32_t bitsPerByte)1127 void Q_RemoveFromBitflags( uint32_t *bits, int index, uint32_t bitsPerByte ) {
1128 	bits[index / bitsPerByte] &= ~(1 << (index % bitsPerByte));
1129 }
1130 
Q_LinearSearch(const void * key,const void * ptr,size_t count,size_t size,cmpFunc_t cmp)1131 void *Q_LinearSearch( const void *key, const void *ptr, size_t count,
1132 	size_t size, cmpFunc_t cmp )
1133 {
1134 	size_t i;
1135 	for ( i = 0; i < count; i++ )
1136 	{
1137 		if ( cmp( key, ptr ) == 0 ) return (void *)ptr;
1138 		ptr = (const char *)ptr + size;
1139 	}
1140 	return NULL;
1141 }
1142