1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 
5 This file is part of Quake III Arena source code.
6 
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11 
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Foobar; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 ===========================================================================
21 */
22 //
23 // q_shared.c -- stateless support routines that are included in each code dll
24 #include "q_shared.h"
25 
26 /*
27 ============================================================================
28 
29 GROWLISTS
30 
31 ============================================================================
32 */
33 
Com_InitGrowList(growList_t * list,int maxElements)34 void Com_InitGrowList(growList_t * list, int maxElements)
35 {
36 	list->maxElements = maxElements;
37 	list->currentElements = 0;
38 	list->elements = (void **)malloc(list->maxElements * sizeof(void *));
39 }
40 
Com_DestroyGrowList(growList_t * list)41 void Com_DestroyGrowList(growList_t * list)
42 {
43 	free(list->elements);
44 	memset(list, 0, sizeof(*list));
45 }
46 
Com_AddToGrowList(growList_t * list,void * data)47 int Com_AddToGrowList(growList_t * list, void *data)
48 {
49 	void          **old;
50 
51 	if(list->currentElements != list->maxElements)
52 	{
53 		list->elements[list->currentElements] = data;
54 		return list->currentElements++;
55 	}
56 
57 	// grow, reallocate and move
58 	old = list->elements;
59 
60 	if(list->maxElements < 0)
61 	{
62 		Com_Error(ERR_FATAL, "Com_AddToGrowList: maxElements = %i", list->maxElements);
63 	}
64 
65 	if(list->maxElements == 0)
66 	{
67 		// initialize the list to hold 100 elements
68 		Com_InitGrowList(list, 100);
69 		return Com_AddToGrowList(list, data);
70 	}
71 
72 	list->maxElements *= 2;
73 
74 //  Com_DPrintf("Resizing growlist to %i maxElements\n", list->maxElements);
75 
76 	list->elements = (void **)malloc(list->maxElements * sizeof(void *));
77 
78 	if(!list->elements)
79 	{
80 		Com_Error(ERR_DROP, "Growlist alloc failed");
81 	}
82 
83 	memcpy(list->elements, old, list->currentElements * sizeof(void *));
84 
85 	free(old);
86 
87 	return Com_AddToGrowList(list, data);
88 }
89 
Com_GrowListElement(const growList_t * list,int index)90 void           *Com_GrowListElement(const growList_t * list, int index)
91 {
92 	if(index < 0 || index >= list->currentElements)
93 	{
94 		Com_Error(ERR_DROP, "Com_GrowListElement: %i out of range of %i", index, list->currentElements);
95 	}
96 	return list->elements[index];
97 }
98 
Com_IndexForGrowListElement(const growList_t * list,const void * element)99 int Com_IndexForGrowListElement(const growList_t * list, const void *element)
100 {
101 	int             i;
102 
103 	for(i = 0; i < list->currentElements; i++)
104 	{
105 		if(list->elements[i] == element)
106 		{
107 			return i;
108 		}
109 	}
110 	return -1;
111 }
112 
113 //============================================================================
114 
Com_Clamp(float min,float max,float value)115 float Com_Clamp(float min, float max, float value)
116 {
117 	if(value < min)
118 	{
119 		return min;
120 	}
121 	if(value > max)
122 	{
123 		return max;
124 	}
125 	return value;
126 }
127 
128 
129 /*
130 ============
131 Com_SkipPath
132 ============
133 */
Com_SkipPath(char * pathname)134 char           *Com_SkipPath(char *pathname)
135 {
136 	char           *last;
137 
138 	last = pathname;
139 	while(*pathname)
140 	{
141 		if(*pathname == '/')
142 			last = pathname + 1;
143 		pathname++;
144 	}
145 	return last;
146 }
147 
148 /*
149 ============
150 Com_GetExtension
151 ============
152 */
Com_GetExtension(const char * name)153 const char     *Com_GetExtension(const char *name)
154 {
155 	int             length, i;
156 
157 	length = strlen(name) - 1;
158 	i = length;
159 
160 	while(name[i] != '.')
161 	{
162 		i--;
163 		if(name[i] == '/' || i == 0)
164 			return "";			// no extension
165 	}
166 
167 	return &name[i + 1];
168 }
169 
170 
171 /*
172 ============
173 Com_StripExtension
174 ============
175 */
Com_StripExtension(const char * src,char * dest,int destsize)176 void Com_StripExtension(const char *src, char *dest, int destsize)
177 {
178 	int             length;
179 
180 	Q_strncpyz(dest, src, destsize);
181 
182 	length = strlen(dest) - 1;
183 
184 	while(length > 0 && dest[length] != '.')
185 	{
186 		length--;
187 
188 		if(dest[length] == '/')
189 			return;				// no extension
190 	}
191 
192 	if(length)
193 	{
194 		dest[length] = 0;
195 	}
196 }
197 
198 
199 /*
200 ==================
201 Com_DefaultExtension
202 ==================
203 */
Com_DefaultExtension(char * path,int maxSize,const char * extension)204 void Com_DefaultExtension(char *path, int maxSize, const char *extension)
205 {
206 	char            oldPath[MAX_QPATH];
207 	char           *src;
208 
209 //
210 // if path doesn't have a .EXT, append extension
211 // (extension should include the .)
212 //
213 	src = path + strlen(path) - 1;
214 
215 	while(*src != '/' && src != path)
216 	{
217 		if(*src == '.')
218 		{
219 			return;				// it has an extension
220 		}
221 		src--;
222 	}
223 
224 	Q_strncpyz(oldPath, path, sizeof(oldPath));
225 	Com_sprintf(path, maxSize, "%s%s", oldPath, extension);
226 }
227 
228 /*
229 ============================================================================
230 
231 					BYTE ORDER FUNCTIONS
232 
233 ============================================================================
234 */
235 /*
236 // can't just use function pointers, or dll linkage can
237 // mess up when qcommon is included in multiple places
238 static short	(*_BigShort) (short l);
239 static short	(*_LittleShort) (short l);
240 static int		(*_BigLong) (int l);
241 static int		(*_LittleLong) (int l);
242 static qint64	(*_BigLong64) (qint64 l);
243 static qint64	(*_LittleLong64) (qint64 l);
244 static float	(*_BigFloat) (const float *l);
245 static float	(*_LittleFloat) (const float *l);
246 
247 short	BigShort(short l){return _BigShort(l);}
248 short	LittleShort(short l) {return _LittleShort(l);}
249 int		BigLong (int l) {return _BigLong(l);}
250 int		LittleLong (int l) {return _LittleLong(l);}
251 qint64 	BigLong64 (qint64 l) {return _BigLong64(l);}
252 qint64 	LittleLong64 (qint64 l) {return _LittleLong64(l);}
253 float	BigFloat (const float *l) {return _BigFloat(l);}
254 float	LittleFloat (const float *l) {return _LittleFloat(l);}
255 */
256 
ShortSwap(short l)257 short ShortSwap(short l)
258 {
259 	byte            b1, b2;
260 
261 	b1 = l & 255;
262 	b2 = (l >> 8) & 255;
263 
264 	return (b1 << 8) + b2;
265 }
266 
ShortNoSwap(short l)267 short ShortNoSwap(short l)
268 {
269 	return l;
270 }
271 
LongSwap(int l)272 int LongSwap(int l)
273 {
274 	byte            b1, b2, b3, b4;
275 
276 	b1 = l & 255;
277 	b2 = (l >> 8) & 255;
278 	b3 = (l >> 16) & 255;
279 	b4 = (l >> 24) & 255;
280 
281 	return ((int)b1 << 24) + ((int)b2 << 16) + ((int)b3 << 8) + b4;
282 }
283 
LongNoSwap(int l)284 int LongNoSwap(int l)
285 {
286 	return l;
287 }
288 
Long64Swap(qint64 ll)289 qint64 Long64Swap(qint64 ll)
290 {
291 	qint64          result;
292 
293 	result.b0 = ll.b7;
294 	result.b1 = ll.b6;
295 	result.b2 = ll.b5;
296 	result.b3 = ll.b4;
297 	result.b4 = ll.b3;
298 	result.b5 = ll.b2;
299 	result.b6 = ll.b1;
300 	result.b7 = ll.b0;
301 
302 	return result;
303 }
304 
Long64NoSwap(qint64 ll)305 qint64 Long64NoSwap(qint64 ll)
306 {
307 	return ll;
308 }
309 
310 typedef union
311 {
312 	float           f;
313 	unsigned int    i;
314 } _FloatByteUnion;
315 
FloatSwap(const float * f)316 float FloatSwap(const float *f)
317 {
318 	_FloatByteUnion out;
319 
320 	out.f = *f;
321 	out.i = LongSwap(out.i);
322 
323 	return out.f;
324 }
325 
FloatNoSwap(const float * f)326 float FloatNoSwap(const float *f)
327 {
328 	return *f;
329 }
330 
331 /*
332 ================
333 Swap_Init
334 ================
335 */
336 /*
337 void Swap_Init (void)
338 {
339 	byte	swaptest[2] = {1,0};
340 
341 // set the byte swapping variables in a portable manner
342 	if ( *(short *)swaptest == 1)
343 	{
344 		_BigShort = ShortSwap;
345 		_LittleShort = ShortNoSwap;
346 		_BigLong = LongSwap;
347 		_LittleLong = LongNoSwap;
348 		_BigLong64 = Long64Swap;
349 		_LittleLong64 = Long64NoSwap;
350 		_BigFloat = FloatSwap;
351 		_LittleFloat = FloatNoSwap;
352 	}
353 	else
354 	{
355 		_BigShort = ShortNoSwap;
356 		_LittleShort = ShortSwap;
357 		_BigLong = LongNoSwap;
358 		_LittleLong = LongSwap;
359 		_BigLong64 = Long64NoSwap;
360 		_LittleLong64 = Long64Swap;
361 		_BigFloat = FloatNoSwap;
362 		_LittleFloat = FloatSwap;
363 	}
364 
365 }
366 */
367 
368 /*
369 ============================================================================
370 
371 PARSING
372 
373 ============================================================================
374 */
375 
376 // multiple character punctuation tokens
377 const char     *punctuation[] = {
378 	"+=", "-=", "*=", "/=", "&=", "|=", "++", "--",
379 	"&&", "||", "<=", ">=", "==", "!=",
380 	NULL
381 };
382 
383 static char     com_token[MAX_TOKEN_CHARS];
384 static char     Com_Parsename[MAX_TOKEN_CHARS];
385 static int      com_lines;
386 
Com_BeginParseSession(const char * name)387 void Com_BeginParseSession(const char *name)
388 {
389 	com_lines = 0;
390 	Com_sprintf(Com_Parsename, sizeof(Com_Parsename), "%s", name);
391 }
392 
Com_GetCurrentParseLine(void)393 int Com_GetCurrentParseLine(void)
394 {
395 	return com_lines;
396 }
397 
Com_Parse(char ** data_p)398 char           *Com_Parse(char **data_p)
399 {
400 	return Com_ParseExt(data_p, qtrue);
401 }
402 
Com_ParseError(char * format,...)403 void Com_ParseError(char *format, ...)
404 {
405 	va_list         argptr;
406 	static char     string[4096];
407 
408 	va_start(argptr, format);
409 	Q_vsnprintf(string, sizeof(string), format, argptr);
410 	va_end(argptr);
411 
412 	Com_Printf("ERROR: %s, line %d: %s\n", Com_Parsename, com_lines, string);
413 }
414 
Com_ParseWarning(char * format,...)415 void Com_ParseWarning(char *format, ...)
416 {
417 	va_list         argptr;
418 	static char     string[4096];
419 
420 	va_start(argptr, format);
421 	Q_vsnprintf(string, sizeof(string), format, argptr);
422 	va_end(argptr);
423 
424 	Com_Printf("WARNING: %s, line %d: %s\n", Com_Parsename, com_lines, string);
425 }
426 
427 /*
428 ==============
429 Com_Parse
430 
431 Parse a token out of a string
432 Will never return NULL, just empty strings
433 
434 If "allowLineBreaks" is qtrue then an empty
435 string will be returned if the next token is
436 a newline.
437 ==============
438 */
SkipWhitespace(char * data,qboolean * hasNewLines)439 static char    *SkipWhitespace(char *data, qboolean * hasNewLines)
440 {
441 	int             c;
442 
443 	while((c = *data) <= ' ')
444 	{
445 		if(!c)
446 		{
447 			return NULL;
448 		}
449 		if(c == '\n')
450 		{
451 			com_lines++;
452 			*hasNewLines = qtrue;
453 		}
454 		data++;
455 	}
456 
457 	return data;
458 }
459 
Com_Compress(char * data_p)460 int Com_Compress(char *data_p)
461 {
462 	char           *in, *out;
463 	int             c;
464 	qboolean        newline = qfalse, whitespace = qfalse;
465 
466 	in = out = data_p;
467 	if(in)
468 	{
469 		while((c = *in) != 0)
470 		{
471 			// skip double slash comments
472 			if(c == '/' && in[1] == '/')
473 			{
474 				while(*in && *in != '\n')
475 				{
476 					in++;
477 				}
478 				// skip /* */ comments
479 			}
480 			else if(c == '/' && in[1] == '*')
481 			{
482 				while(*in && (*in != '*' || in[1] != '/'))
483 					in++;
484 				if(*in)
485 					in += 2;
486 				// record when we hit a newline
487 			}
488 			else if(c == '\n' || c == '\r')
489 			{
490 				newline = qtrue;
491 				in++;
492 				// record when we hit whitespace
493 			}
494 			else if(c == ' ' || c == '\t')
495 			{
496 				whitespace = qtrue;
497 				in++;
498 				// an actual token
499 			}
500 			else
501 			{
502 				// if we have a pending newline, emit it (and it counts as whitespace)
503 				if(newline)
504 				{
505 					*out++ = '\n';
506 					newline = qfalse;
507 					whitespace = qfalse;
508 				}
509 				if(whitespace)
510 				{
511 					*out++ = ' ';
512 					whitespace = qfalse;
513 				}
514 
515 				// copy quoted strings unmolested
516 				if(c == '"')
517 				{
518 					*out++ = c;
519 					in++;
520 					while(1)
521 					{
522 						c = *in;
523 						if(c && c != '"')
524 						{
525 							*out++ = c;
526 							in++;
527 						}
528 						else
529 						{
530 							break;
531 						}
532 					}
533 					if(c == '"')
534 					{
535 						*out++ = c;
536 						in++;
537 					}
538 				}
539 				else
540 				{
541 					*out = c;
542 					out++;
543 					in++;
544 				}
545 			}
546 		}
547 	}
548 	*out = 0;
549 	return out - data_p;
550 }
551 
Com_ParseExt(char ** data_p,qboolean allowLineBreaks)552 char           *Com_ParseExt(char **data_p, qboolean allowLineBreaks)
553 {
554 	int             c = 0, len;
555 	qboolean        hasNewLines = qfalse;
556 	char           *data;
557 	const char    **punc;
558 
559 	if(!data_p)
560 	{
561 		Com_Error(ERR_FATAL, "Com_ParseExt: NULL data_p");
562 	}
563 
564 	data = *data_p;
565 	len = 0;
566 	com_token[0] = 0;
567 
568 	// make sure incoming data is valid
569 	if(!data)
570 	{
571 		*data_p = NULL;
572 		return com_token;
573 	}
574 
575 	// skip whitespace
576 	while(1)
577 	{
578 		data = SkipWhitespace(data, &hasNewLines);
579 		if(!data)
580 		{
581 			*data_p = NULL;
582 			return com_token;
583 		}
584 		if(hasNewLines && !allowLineBreaks)
585 		{
586 			*data_p = data;
587 			return com_token;
588 		}
589 
590 		c = *data;
591 
592 		// skip double slash comments
593 		if(c == '/' && data[1] == '/')
594 		{
595 			data += 2;
596 			while(*data && *data != '\n')
597 			{
598 				data++;
599 			}
600 		}
601 		// skip /* */ comments
602 		else if(c == '/' && data[1] == '*')
603 		{
604 			data += 2;
605 			while(*data && (*data != '*' || data[1] != '/'))
606 			{
607 				data++;
608 			}
609 			if(*data)
610 			{
611 				data += 2;
612 			}
613 		}
614 		else
615 		{
616 			// a real token to parse
617 			break;
618 		}
619 	}
620 
621 	// handle quoted strings
622 	if(c == '\"')
623 	{
624 		data++;
625 		while(1)
626 		{
627 			c = *data++;
628 
629 			if((c == '\\') && (*data == '\"'))
630 			{
631 				// allow quoted strings to use \" to indicate the " character
632 				data++;
633 			}
634 			else if(c == '\"' || !c)
635 			{
636 				com_token[len] = 0;
637 				*data_p = (char *)data;
638 				return com_token;
639 			}
640 			else if(*data == '\n')
641 			{
642 				com_lines++;
643 			}
644 
645 			if(len < MAX_TOKEN_CHARS - 1)
646 			{
647 				com_token[len] = c;
648 				len++;
649 			}
650 		}
651 	}
652 
653 	// check for a number
654 	// is this parsing of negative numbers going to cause expression problems
655 	if((c >= '0' && c <= '9') ||
656 	   (c == '-' && data[1] >= '0' && data[1] <= '9') ||
657 	   (c == '.' && data[1] >= '0' && data[1] <= '9') || (c == '-' && data[1] == '.' && data[2] >= '0' && data[2] <= '9'))
658 	{
659 		do
660 		{
661 			if(len < MAX_TOKEN_CHARS - 1)
662 			{
663 				com_token[len] = c;
664 				len++;
665 			}
666 			data++;
667 
668 			c = *data;
669 		} while((c >= '0' && c <= '9') || c == '.');
670 
671 		// parse the exponent
672 		if(c == 'e' || c == 'E')
673 		{
674 			if(len < MAX_TOKEN_CHARS - 1)
675 			{
676 				com_token[len] = c;
677 				len++;
678 			}
679 			data++;
680 			c = *data;
681 
682 			if(c == '-' || c == '+')
683 			{
684 				if(len < MAX_TOKEN_CHARS - 1)
685 				{
686 					com_token[len] = c;
687 					len++;
688 				}
689 				data++;
690 				c = *data;
691 			}
692 
693 			do
694 			{
695 				if(len < MAX_TOKEN_CHARS - 1)
696 				{
697 					com_token[len] = c;
698 					len++;
699 				}
700 				data++;
701 
702 				c = *data;
703 			} while(c >= '0' && c <= '9');
704 		}
705 
706 		if(len == MAX_TOKEN_CHARS)
707 		{
708 			len = 0;
709 		}
710 		com_token[len] = 0;
711 
712 		*data_p = (char *)data;
713 		return com_token;
714 	}
715 
716 	// check for a regular word
717 	// we still allow forward and back slashes in name tokens for pathnames
718 	// and also colons for drive letters
719 	if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '_') || (c == '/') || (c == '\\') || (c == '$') || (c == '*'))	// Tr3B - for bad shader strings
720 	{
721 		do
722 		{
723 			if(len < MAX_TOKEN_CHARS - 1)
724 			{
725 				com_token[len] = c;
726 				len++;
727 			}
728 			data++;
729 
730 			c = *data;
731 		}
732 		while
733 			((c >= 'a' && c <= 'z') ||
734 			 (c >= 'A' && c <= 'Z') ||
735 			 (c == '_') ||
736 			 (c == '-') ||
737 			 (c >= '0' && c <= '9') ||
738 			 (c == '/') || (c == '\\') || (c == ':') || (c == '.') || (c == '$') || (c == '*') || (c == '@'));
739 
740 		if(len == MAX_TOKEN_CHARS)
741 		{
742 			len = 0;
743 		}
744 		com_token[len] = 0;
745 
746 		*data_p = (char *)data;
747 		return com_token;
748 	}
749 
750 	// check for multi-character punctuation token
751 	for(punc = punctuation; *punc; punc++)
752 	{
753 		int             l;
754 		int             j;
755 
756 		l = strlen(*punc);
757 		for(j = 0; j < l; j++)
758 		{
759 			if(data[j] != (*punc)[j])
760 			{
761 				break;
762 			}
763 		}
764 		if(j == l)
765 		{
766 			// a valid multi-character punctuation
767 			memcpy(com_token, *punc, l);
768 			com_token[l] = 0;
769 			data += l;
770 			*data_p = (char *)data;
771 			return com_token;
772 		}
773 	}
774 
775 	// single character punctuation
776 	com_token[0] = *data;
777 	com_token[1] = 0;
778 	data++;
779 	*data_p = (char *)data;
780 
781 	return com_token;
782 }
783 
784 /*
785 ==================
786 COM_MatchToken
787 ==================
788 */
COM_MatchToken(char ** buf_p,char * match)789 void COM_MatchToken(char **buf_p, char *match)
790 {
791 	char           *token;
792 
793 	token = Com_Parse(buf_p);
794 	if(strcmp(token, match))
795 	{
796 		Com_Error(ERR_DROP, "MatchToken: %s != %s", token, match);
797 	}
798 }
799 
800 
801 /*
802 =================
803 Com_SkipBracedSection
804 
805 The next token should be an open brace.
806 Skips until a matching close brace is found.
807 Internal brace depths are properly skipped.
808 =================
809 */
Com_SkipBracedSection(char ** program)810 void Com_SkipBracedSection(char **program)
811 {
812 	char           *token;
813 	int             depth;
814 
815 	depth = 0;
816 	do
817 	{
818 		token = Com_ParseExt(program, qtrue);
819 		if(token[1] == 0)
820 		{
821 			if(token[0] == '{')
822 			{
823 				depth++;
824 			}
825 			else if(token[0] == '}')
826 			{
827 				depth--;
828 			}
829 		}
830 	} while(depth && *program);
831 }
832 
833 /*
834 =================
835 Com_SkipRestOfLine
836 =================
837 */
Com_SkipRestOfLine(char ** data)838 void Com_SkipRestOfLine(char **data)
839 {
840 	char           *p;
841 	int             c;
842 
843 	p = *data;
844 	while((c = *p++) != 0)
845 	{
846 		if(c == '\n')
847 		{
848 			com_lines++;
849 			break;
850 		}
851 	}
852 
853 	*data = p;
854 }
855 
856 
Parse1DMatrix(char ** buf_p,int x,float * m)857 void Parse1DMatrix(char **buf_p, int x, float *m)
858 {
859 	char           *token;
860 	int             i;
861 
862 	COM_MatchToken(buf_p, "(");
863 
864 	for(i = 0; i < x; i++)
865 	{
866 		token = Com_Parse(buf_p);
867 		m[i] = atof(token);
868 	}
869 
870 	COM_MatchToken(buf_p, ")");
871 }
872 
Parse2DMatrix(char ** buf_p,int y,int x,float * m)873 void Parse2DMatrix(char **buf_p, int y, int x, float *m)
874 {
875 	int             i;
876 
877 	COM_MatchToken(buf_p, "(");
878 
879 	for(i = 0; i < y; i++)
880 	{
881 		Parse1DMatrix(buf_p, x, m + i * x);
882 	}
883 
884 	COM_MatchToken(buf_p, ")");
885 }
886 
Parse3DMatrix(char ** buf_p,int z,int y,int x,float * m)887 void Parse3DMatrix(char **buf_p, int z, int y, int x, float *m)
888 {
889 	int             i;
890 
891 	COM_MatchToken(buf_p, "(");
892 
893 	for(i = 0; i < z; i++)
894 	{
895 		Parse2DMatrix(buf_p, y, x, m + i * x * y);
896 	}
897 
898 	COM_MatchToken(buf_p, ")");
899 }
900 
901 
902 /*
903 ============================================================================
904 
905 					LIBRARY REPLACEMENT FUNCTIONS
906 
907 ============================================================================
908 */
909 
Q_isprint(int c)910 int Q_isprint(int c)
911 {
912 	if(c >= 0x20 && c <= 0x7E)
913 		return (1);
914 	return (0);
915 }
916 
Q_islower(int c)917 int Q_islower(int c)
918 {
919 	if(c >= 'a' && c <= 'z')
920 		return (1);
921 	return (0);
922 }
923 
Q_isupper(int c)924 int Q_isupper(int c)
925 {
926 	if(c >= 'A' && c <= 'Z')
927 		return (1);
928 	return (0);
929 }
930 
Q_isalpha(int c)931 int Q_isalpha(int c)
932 {
933 	if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
934 		return (1);
935 	return (0);
936 }
937 
Q_strrchr(const char * string,int c)938 char           *Q_strrchr(const char *string, int c)
939 {
940 	char            cc = c;
941 	char           *s;
942 	char           *sp = (char *)0;
943 
944 	s = (char *)string;
945 
946 	while(*s)
947 	{
948 		if(*s == cc)
949 			sp = s;
950 		s++;
951 	}
952 	if(cc == 0)
953 		sp = s;
954 
955 	return sp;
956 }
957 
958 /*
959 * Find the first occurrence of find in s.
960 */
Q_stristr(const char * s,const char * find)961 const char     *Q_stristr(const char *s, const char *find)
962 {
963 	char            c, sc;
964 	size_t          len;
965 
966 	if((c = *find++) != 0)
967 	{
968 		if(c >= 'a' && c <= 'z')
969 		{
970 			c -= ('a' - 'A');
971 		}
972 		len = strlen(find);
973 		do
974 		{
975 			do
976 			{
977 				if((sc = *s++) == 0)
978 				{
979 					return NULL;
980 				}
981 
982 				if(sc >= 'a' && sc <= 'z')
983 				{
984 					sc -= ('a' - 'A');
985 				}
986 			} while(sc != c);
987 		} while(Q_stricmpn(s, find, len) != 0);
988 		s--;
989 	}
990 	return s;
991 }
992 
993 /*
994 =============
995 Q_strncpyz
996 
997 Safe strncpy that ensures a trailing zero
998 =============
999 */
Q_strncpyz(char * dest,const char * src,int destsize)1000 void Q_strncpyz(char *dest, const char *src, int destsize)
1001 {
1002 	if(!dest)
1003 	{
1004 		Com_Error(ERR_FATAL, "Q_strncpyz: NULL dest");
1005 	}
1006 	if(!src)
1007 	{
1008 		Com_Error(ERR_FATAL, "Q_strncpyz: NULL src");
1009 	}
1010 	if(destsize < 1)
1011 	{
1012 		Com_Error(ERR_FATAL, "Q_strncpyz: destsize < 1");
1013 	}
1014 
1015 	strncpy(dest, src, destsize - 1);
1016 	dest[destsize - 1] = 0;
1017 }
1018 
Q_stricmpn(const char * s1,const char * s2,int n)1019 int Q_stricmpn(const char *s1, const char *s2, int n)
1020 {
1021 	int             c1, c2;
1022 
1023 	if(s1 == NULL)
1024 	{
1025 		if(s2 == NULL)
1026 			return 0;
1027 		else
1028 			return -1;
1029 	}
1030 	else if(s2 == NULL)
1031 		return 1;
1032 
1033 
1034 
1035 	do
1036 	{
1037 		c1 = *s1++;
1038 		c2 = *s2++;
1039 
1040 		if(!n--)
1041 		{
1042 			return 0;			// strings are equal until end point
1043 		}
1044 
1045 		if(c1 != c2)
1046 		{
1047 			if(c1 >= 'a' && c1 <= 'z')
1048 			{
1049 				c1 -= ('a' - 'A');
1050 			}
1051 			if(c2 >= 'a' && c2 <= 'z')
1052 			{
1053 				c2 -= ('a' - 'A');
1054 			}
1055 			if(c1 != c2)
1056 			{
1057 				return c1 < c2 ? -1 : 1;
1058 			}
1059 		}
1060 	} while(c1);
1061 
1062 	return 0;					// strings are equal
1063 }
1064 
Q_strncmp(const char * s1,const char * s2,int n)1065 int Q_strncmp(const char *s1, const char *s2, int n)
1066 {
1067 	int             c1, c2;
1068 
1069 	do
1070 	{
1071 		c1 = *s1++;
1072 		c2 = *s2++;
1073 
1074 		if(!n--)
1075 		{
1076 			return 0;			// strings are equal until end point
1077 		}
1078 
1079 		if(c1 != c2)
1080 		{
1081 			return c1 < c2 ? -1 : 1;
1082 		}
1083 	} while(c1);
1084 
1085 	return 0;					// strings are equal
1086 }
1087 
Q_stricmp(const char * s1,const char * s2)1088 int Q_stricmp(const char *s1, const char *s2)
1089 {
1090 	return (s1 && s2) ? Q_stricmpn(s1, s2, 99999) : -1;
1091 }
1092 
1093 
Q_strlwr(char * s1)1094 char           *Q_strlwr(char *s1)
1095 {
1096 	char           *s;
1097 
1098 	s = s1;
1099 	while(*s)
1100 	{
1101 		*s = tolower(*s);
1102 		s++;
1103 	}
1104 	return s1;
1105 }
1106 
Q_strupr(char * s1)1107 char           *Q_strupr(char *s1)
1108 {
1109 	char           *s;
1110 
1111 	s = s1;
1112 	while(*s)
1113 	{
1114 		*s = toupper(*s);
1115 		s++;
1116 	}
1117 	return s1;
1118 }
1119 
1120 
1121 // never goes past bounds or leaves without a terminating 0
Q_strcat(char * dest,int size,const char * src)1122 void Q_strcat(char *dest, int size, const char *src)
1123 {
1124 	int             l1;
1125 
1126 	l1 = strlen(dest);
1127 	if(l1 >= size)
1128 	{
1129 		Com_Error(ERR_FATAL, "Q_strcat: already overflowed");
1130 	}
1131 	Q_strncpyz(dest + l1, src, size - l1);
1132 }
1133 
1134 /*
1135 =============
1136 Q_strreplace
1137 
1138 replaces content of find by replace in dest
1139 =============
1140 */
Q_strreplace(char * dest,int destsize,const char * find,const char * replace)1141 qboolean Q_strreplace(char *dest, int destsize, const char *find, const char *replace)
1142 {
1143 	int             lstart, lfind, lreplace, lend;
1144 	char           *s;
1145 	char            backup[32000];	// big, but small enough to fit in PPC stack
1146 
1147 	lend = strlen(dest);
1148 	if(lend >= destsize)
1149 	{
1150 		Com_Error(ERR_FATAL, "Q_strreplace: already overflowed");
1151 	}
1152 
1153 	s = strstr(dest, find);
1154 	if(!s)
1155 	{
1156 		return qfalse;
1157 	}
1158 	else
1159 	{
1160 		Q_strncpyz(backup, dest, lend + 1);
1161 		lstart = s - dest;
1162 		lfind = strlen(find);
1163 		lreplace = strlen(replace);
1164 
1165 		strncpy(s, replace, destsize - lstart - 1);
1166 		strncpy(s + lreplace, backup + lstart + lfind, destsize - lstart - lreplace - 1);
1167 
1168 		return qtrue;
1169 	}
1170 }
1171 
Q_PrintStrlen(const char * string)1172 int Q_PrintStrlen(const char *string)
1173 {
1174 	int             len;
1175 	const char     *p;
1176 
1177 	if(!string)
1178 	{
1179 		return 0;
1180 	}
1181 
1182 	len = 0;
1183 	p = string;
1184 	while(*p)
1185 	{
1186 		if(Q_IsColorString(p))
1187 		{
1188 			p += 2;
1189 			continue;
1190 		}
1191 		p++;
1192 		len++;
1193 	}
1194 
1195 	return len;
1196 }
1197 
1198 
Q_CleanStr(char * string)1199 char           *Q_CleanStr(char *string)
1200 {
1201 	char           *d;
1202 	char           *s;
1203 	int             c;
1204 
1205 	s = string;
1206 	d = string;
1207 	while((c = *s) != 0)
1208 	{
1209 		if(Q_IsColorString(s))
1210 		{
1211 			s++;
1212 		}
1213 		else if(c >= 0x20 && c <= 0x7E)
1214 		{
1215 			*d++ = c;
1216 		}
1217 		s++;
1218 	}
1219 	*d = '\0';
1220 
1221 	return string;
1222 }
1223 
Q_CountChar(const char * string,char tocount)1224 int Q_CountChar(const char *string, char tocount)
1225 {
1226 	int count;
1227 
1228 	for(count = 0; *string; string++)
1229 	{
1230 		if(*string == tocount)
1231 			count++;
1232 	}
1233 
1234 	return count;
1235 }
1236 
Com_sprintf(char * dest,int size,const char * fmt,...)1237 void QDECL Com_sprintf(char *dest, int size, const char *fmt, ...)
1238 {
1239 	int             len;
1240 	va_list         argptr;
1241 	char            bigbuffer[32000];	// big, but small enough to fit in PPC stack
1242 
1243 	va_start(argptr, fmt);
1244 	len = Q_vsnprintf(bigbuffer, sizeof(bigbuffer), fmt, argptr);
1245 	va_end(argptr);
1246 	if(len >= sizeof(bigbuffer))
1247 	{
1248 		Com_Error(ERR_FATAL, "Com_sprintf: overflowed bigbuffer");
1249 	}
1250 	if(len >= size)
1251 	{
1252 		Com_Printf("Com_sprintf: overflow of %i in %i\n", len, size);
1253 	}
1254 	Q_strncpyz(dest, bigbuffer, size);
1255 }
1256 
1257 
1258 /*
1259 ============
1260 va
1261 
1262 does a varargs printf into a temp buffer, so I don't need to have
1263 varargs versions of all text functions.
1264 ============
1265 */
va(char * format,...)1266 char           *QDECL va(char *format, ...)
1267 {
1268 	va_list         argptr;
1269 	static char     string[2][32000];	// in case va is called by nested functions
1270 	static int      index = 0;
1271 	char           *buf;
1272 
1273 	buf = string[index & 1];
1274 	index++;
1275 
1276 	va_start(argptr, format);
1277 	Q_vsnprintf(buf, sizeof(*string), format, argptr);
1278 	va_end(argptr);
1279 
1280 	return buf;
1281 }
1282 
1283 /*
1284 ============
1285 Com_TruncateLongString
1286 
1287 Assumes buffer is atleast TRUNCATE_LENGTH big
1288 ============
1289 */
Com_TruncateLongString(char * buffer,const char * s)1290 void Com_TruncateLongString(char *buffer, const char *s)
1291 {
1292 	int             length = strlen(s);
1293 
1294 	if(length <= TRUNCATE_LENGTH)
1295 		Q_strncpyz(buffer, s, TRUNCATE_LENGTH);
1296 	else
1297 	{
1298 		Q_strncpyz(buffer, s, (TRUNCATE_LENGTH / 2) - 3);
1299 		Q_strcat(buffer, TRUNCATE_LENGTH, " ... ");
1300 		Q_strcat(buffer, TRUNCATE_LENGTH, s + length - (TRUNCATE_LENGTH / 2) + 3);
1301 	}
1302 }
1303 
1304 /*
1305 =====================================================================
1306 
1307   INFO STRINGS
1308 
1309 =====================================================================
1310 */
1311 
1312 /*
1313 ===============
1314 Info_ValueForKey
1315 
1316 Searches the string for the given
1317 key and returns the associated value, or an empty string.
1318 FIXME: overflow check?
1319 ===============
1320 */
Info_ValueForKey(const char * s,const char * key)1321 char           *Info_ValueForKey(const char *s, const char *key)
1322 {
1323 	char            pkey[BIG_INFO_KEY];
1324 	static char     value[2][BIG_INFO_VALUE];	// use two buffers so compares
1325 
1326 	// work without stomping on each other
1327 	static int      valueindex = 0;
1328 	char           *o;
1329 
1330 	if(!s || !key)
1331 	{
1332 		return "";
1333 	}
1334 
1335 	if(strlen(s) >= BIG_INFO_STRING)
1336 	{
1337 		Com_Error(ERR_DROP, "Info_ValueForKey: oversize infostring");
1338 	}
1339 
1340 	valueindex ^= 1;
1341 	if(*s == '\\')
1342 		s++;
1343 	while(1)
1344 	{
1345 		o = pkey;
1346 		while(*s != '\\')
1347 		{
1348 			if(!*s)
1349 				return "";
1350 			*o++ = *s++;
1351 		}
1352 		*o = 0;
1353 		s++;
1354 
1355 		o = value[valueindex];
1356 
1357 		while(*s != '\\' && *s)
1358 		{
1359 			*o++ = *s++;
1360 		}
1361 		*o = 0;
1362 
1363 		if(!Q_stricmp(key, pkey))
1364 			return value[valueindex];
1365 
1366 		if(!*s)
1367 			break;
1368 		s++;
1369 	}
1370 
1371 	return "";
1372 }
1373 
1374 
1375 /*
1376 ===================
1377 Info_NextPair
1378 
1379 Used to itterate through all the key/value pairs in an info string
1380 ===================
1381 */
Info_NextPair(const char ** head,char * key,char * value)1382 void Info_NextPair(const char **head, char *key, char *value)
1383 {
1384 	char           *o;
1385 	const char     *s;
1386 
1387 	s = *head;
1388 
1389 	if(*s == '\\')
1390 	{
1391 		s++;
1392 	}
1393 	key[0] = 0;
1394 	value[0] = 0;
1395 
1396 	o = key;
1397 	while(*s != '\\')
1398 	{
1399 		if(!*s)
1400 		{
1401 			*o = 0;
1402 			*head = s;
1403 			return;
1404 		}
1405 		*o++ = *s++;
1406 	}
1407 	*o = 0;
1408 	s++;
1409 
1410 	o = value;
1411 	while(*s != '\\' && *s)
1412 	{
1413 		*o++ = *s++;
1414 	}
1415 	*o = 0;
1416 
1417 	*head = s;
1418 }
1419 
1420 
1421 /*
1422 ===================
1423 Info_RemoveKey
1424 ===================
1425 */
Info_RemoveKey(char * s,const char * key)1426 void Info_RemoveKey(char *s, const char *key)
1427 {
1428 	char           *start;
1429 	char            pkey[MAX_INFO_KEY];
1430 	char            value[MAX_INFO_VALUE];
1431 	char           *o;
1432 
1433 	if(strlen(s) >= MAX_INFO_STRING)
1434 	{
1435 		Com_Error(ERR_DROP, "Info_RemoveKey: oversize infostring");
1436 	}
1437 
1438 	if(strchr(key, '\\'))
1439 	{
1440 		return;
1441 	}
1442 
1443 	while(1)
1444 	{
1445 		start = s;
1446 		if(*s == '\\')
1447 			s++;
1448 		o = pkey;
1449 		while(*s != '\\')
1450 		{
1451 			if(!*s)
1452 				return;
1453 			*o++ = *s++;
1454 		}
1455 		*o = 0;
1456 		s++;
1457 
1458 		o = value;
1459 		while(*s != '\\' && *s)
1460 		{
1461 			if(!*s)
1462 				return;
1463 			*o++ = *s++;
1464 		}
1465 		*o = 0;
1466 
1467 		if(!strcmp(key, pkey))
1468 		{
1469 			memmove(start, s, strlen(s) + 1);	// remove this part
1470 			return;
1471 		}
1472 
1473 		if(!*s)
1474 			return;
1475 	}
1476 
1477 }
1478 
1479 /*
1480 ===================
1481 Info_RemoveKey_Big
1482 ===================
1483 */
Info_RemoveKey_Big(char * s,const char * key)1484 void Info_RemoveKey_Big(char *s, const char *key)
1485 {
1486 	char           *start;
1487 	char            pkey[BIG_INFO_KEY];
1488 	char            value[BIG_INFO_VALUE];
1489 	char           *o;
1490 
1491 	if(strlen(s) >= BIG_INFO_STRING)
1492 	{
1493 		Com_Error(ERR_DROP, "Info_RemoveKey_Big: oversize infostring");
1494 	}
1495 
1496 	if(strchr(key, '\\'))
1497 	{
1498 		return;
1499 	}
1500 
1501 	while(1)
1502 	{
1503 		start = s;
1504 		if(*s == '\\')
1505 			s++;
1506 		o = pkey;
1507 		while(*s != '\\')
1508 		{
1509 			if(!*s)
1510 				return;
1511 			*o++ = *s++;
1512 		}
1513 		*o = 0;
1514 		s++;
1515 
1516 		o = value;
1517 		while(*s != '\\' && *s)
1518 		{
1519 			if(!*s)
1520 				return;
1521 			*o++ = *s++;
1522 		}
1523 		*o = 0;
1524 
1525 		if(!strcmp(key, pkey))
1526 		{
1527 			strcpy(start, s);	// remove this part
1528 			return;
1529 		}
1530 
1531 		if(!*s)
1532 			return;
1533 	}
1534 
1535 }
1536 
1537 
1538 
1539 
1540 /*
1541 ==================
1542 Info_Validate
1543 
1544 Some characters are illegal in info strings because they
1545 can mess up the server's parsing
1546 ==================
1547 */
Info_Validate(const char * s)1548 qboolean Info_Validate(const char *s)
1549 {
1550 	if(strchr(s, '\"'))
1551 	{
1552 		return qfalse;
1553 	}
1554 	if(strchr(s, ';'))
1555 	{
1556 		return qfalse;
1557 	}
1558 	return qtrue;
1559 }
1560 
1561 /*
1562 ==================
1563 Info_SetValueForKey
1564 
1565 Changes or adds a key/value pair
1566 ==================
1567 */
Info_SetValueForKey(char * s,const char * key,const char * value)1568 void Info_SetValueForKey(char *s, const char *key, const char *value)
1569 {
1570 	char            newi[MAX_INFO_STRING];
1571 	const char     *blacklist = "\\;\"";
1572 
1573 	if(strlen(s) >= MAX_INFO_STRING)
1574 	{
1575 		Com_Error(ERR_DROP, "Info_SetValueForKey: oversize infostring");
1576 	}
1577 
1578 	for(; *blacklist; ++blacklist)
1579 	{
1580 		if(strchr(key, *blacklist) || strchr(value, *blacklist))
1581 		{
1582 			Com_Printf(S_COLOR_YELLOW "Can't use keys or values with a '%c': %s = %s\n", *blacklist, key, value);
1583 			return;
1584 		}
1585 	}
1586 
1587 	Info_RemoveKey(s, key);
1588 	if(!value || !strlen(value))
1589 		return;
1590 
1591 	Com_sprintf(newi, sizeof(newi), "\\%s\\%s", key, value);
1592 
1593 	if(strlen(newi) + strlen(s) >= MAX_INFO_STRING)
1594 	{
1595 		Com_Printf("Info string length exceeded\n");
1596 		return;
1597 	}
1598 
1599 	strcat(newi, s);
1600 	strcpy(s, newi);
1601 }
1602 
1603 /*
1604 ==================
1605 Info_SetValueForKey_Big
1606 
1607 Changes or adds a key/value pair
1608 ==================
1609 */
Info_SetValueForKey_Big(char * s,const char * key,const char * value)1610 void Info_SetValueForKey_Big(char *s, const char *key, const char *value)
1611 {
1612 	char            newi[BIG_INFO_STRING];
1613 	const char     *blacklist = "\\;\"";
1614 
1615 	if(strlen(s) >= BIG_INFO_STRING)
1616 	{
1617 		Com_Error(ERR_DROP, "Info_SetValueForKey: oversize infostring");
1618 	}
1619 
1620 	for(; *blacklist; ++blacklist)
1621 	{
1622 		if(strchr(key, *blacklist) || strchr(value, *blacklist))
1623 		{
1624 			Com_Printf(S_COLOR_YELLOW "Can't use keys or values with a '%c': %s = %s\n", *blacklist, key, value);
1625 			return;
1626 		}
1627 	}
1628 
1629 	Info_RemoveKey_Big(s, key);
1630 	if(!value || !strlen(value))
1631 		return;
1632 
1633 	Com_sprintf(newi, sizeof(newi), "\\%s\\%s", key, value);
1634 
1635 	if(strlen(newi) + strlen(s) >= BIG_INFO_STRING)
1636 	{
1637 		Com_Printf("BIG Info string length exceeded\n");
1638 		return;
1639 	}
1640 
1641 	strcat(s, newi);
1642 }
1643 
1644 //====================================================================
1645 
1646 /*
1647 ==================
1648 Com_CharIsOneOfCharset
1649 ==================
1650 */
Com_CharIsOneOfCharset(char c,char * set)1651 static qboolean Com_CharIsOneOfCharset( char c, char *set )
1652 {
1653 	int i;
1654 
1655 	for( i = 0; i < strlen( set ); i++ )
1656 	{
1657 		if( set[ i ] == c )
1658 			return qtrue;
1659 	}
1660 
1661 	return qfalse;
1662 }
1663 
1664 /*
1665 ==================
1666 Com_SkipCharset
1667 ==================
1668 */
Com_SkipCharset(char * s,char * sep)1669 char *Com_SkipCharset( char *s, char *sep )
1670 {
1671 	char	*p = s;
1672 
1673 	while( p )
1674 	{
1675 		if( Com_CharIsOneOfCharset( *p, sep ) )
1676 			p++;
1677 		else
1678 			break;
1679 	}
1680 
1681 	return p;
1682 }
1683 
1684 /*
1685 ==================
1686 Com_SkipTokens
1687 ==================
1688 */
Com_SkipTokens(char * s,int numTokens,char * sep)1689 char *Com_SkipTokens( char *s, int numTokens, char *sep )
1690 {
1691 	int		sepCount = 0;
1692 	char	*p = s;
1693 
1694 	while( sepCount < numTokens )
1695 	{
1696 		if( Com_CharIsOneOfCharset( *p++, sep ) )
1697 		{
1698 			sepCount++;
1699 			while( Com_CharIsOneOfCharset( *p, sep ) )
1700 				p++;
1701 		}
1702 		else if( *p == '\0' )
1703 			break;
1704 	}
1705 
1706 	if( sepCount == numTokens )
1707 		return p;
1708 	else
1709 		return s;
1710 }
1711