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 Quake III Arena source code; 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
Com_Clamp(float min,float max,float value)26 float Com_Clamp( float min, float max, float value ) {
27 if ( value < min ) {
28 return min;
29 }
30 if ( value > max ) {
31 return max;
32 }
33 return value;
34 }
35
36
37 /*
38 ============
39 COM_SkipPath
40 ============
41 */
COM_SkipPath(char * pathname)42 char *COM_SkipPath (char *pathname)
43 {
44 char *last;
45
46 last = pathname;
47 while (*pathname)
48 {
49 if (*pathname=='/')
50 last = pathname+1;
51 pathname++;
52 }
53 return last;
54 }
55
56 /*
57 ============
58 COM_GetExtension
59 ============
60 */
COM_GetExtension(const char * name)61 const char *COM_GetExtension( const char *name ) {
62 int length, i;
63
64 length = strlen(name)-1;
65 i = length;
66
67 while (name[i] != '.')
68 {
69 i--;
70 if (name[i] == '/' || i == 0)
71 return ""; // no extension
72 }
73
74 return &name[i+1];
75 }
76
77
78 /*
79 ============
80 COM_StripExtension
81 ============
82 */
COM_StripExtension(const char * in,char * out,int destsize)83 void COM_StripExtension( const char *in, char *out, int destsize ) {
84 int length;
85
86 Q_strncpyz(out, in, destsize);
87
88 length = strlen(out)-1;
89 while (length > 0 && out[length] != '.')
90 {
91 length--;
92 if (out[length] == '/')
93 return; // no extension
94 }
95 if (length)
96 out[length] = 0;
97 }
98
99
100 /*
101 ==================
102 COM_DefaultExtension
103 ==================
104 */
COM_DefaultExtension(char * path,int maxSize,const char * extension)105 void COM_DefaultExtension (char *path, int maxSize, const char *extension ) {
106 char oldPath[MAX_QPATH];
107 char *src;
108
109 //
110 // if path doesn't have a .EXT, append extension
111 // (extension should include the .)
112 //
113 src = path + strlen(path) - 1;
114
115 while (*src != '/' && src != path) {
116 if ( *src == '.' ) {
117 return; // it has an extension
118 }
119 src--;
120 }
121
122 Q_strncpyz( oldPath, path, sizeof( oldPath ) );
123 Com_sprintf( path, maxSize, "%s%s", oldPath, extension );
124 }
125
126 /*
127 ============================================================================
128
129 BYTE ORDER FUNCTIONS
130
131 ============================================================================
132 */
133 /*
134 // can't just use function pointers, or dll linkage can
135 // mess up when qcommon is included in multiple places
136 static short (*_BigShort) (short l);
137 static short (*_LittleShort) (short l);
138 static int (*_BigLong) (int l);
139 static int (*_LittleLong) (int l);
140 static qint64 (*_BigLong64) (qint64 l);
141 static qint64 (*_LittleLong64) (qint64 l);
142 static float (*_BigFloat) (const float *l);
143 static float (*_LittleFloat) (const float *l);
144
145 short BigShort(short l){return _BigShort(l);}
146 short LittleShort(short l) {return _LittleShort(l);}
147 int BigLong (int l) {return _BigLong(l);}
148 int LittleLong (int l) {return _LittleLong(l);}
149 qint64 BigLong64 (qint64 l) {return _BigLong64(l);}
150 qint64 LittleLong64 (qint64 l) {return _LittleLong64(l);}
151 float BigFloat (const float *l) {return _BigFloat(l);}
152 float LittleFloat (const float *l) {return _LittleFloat(l);}
153 */
154
ShortSwap(short l)155 short ShortSwap (short l)
156 {
157 byte b1,b2;
158
159 b1 = l&255;
160 b2 = (l>>8)&255;
161
162 return (b1<<8) + b2;
163 }
164
ShortNoSwap(short l)165 short ShortNoSwap (short l)
166 {
167 return l;
168 }
169
LongSwap(int l)170 int LongSwap (int l)
171 {
172 byte b1,b2,b3,b4;
173
174 b1 = l&255;
175 b2 = (l>>8)&255;
176 b3 = (l>>16)&255;
177 b4 = (l>>24)&255;
178
179 return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
180 }
181
LongNoSwap(int l)182 int LongNoSwap (int l)
183 {
184 return l;
185 }
186
Long64Swap(qint64 ll)187 qint64 Long64Swap (qint64 ll)
188 {
189 qint64 result;
190
191 result.b0 = ll.b7;
192 result.b1 = ll.b6;
193 result.b2 = ll.b5;
194 result.b3 = ll.b4;
195 result.b4 = ll.b3;
196 result.b5 = ll.b2;
197 result.b6 = ll.b1;
198 result.b7 = ll.b0;
199
200 return result;
201 }
202
Long64NoSwap(qint64 ll)203 qint64 Long64NoSwap (qint64 ll)
204 {
205 return ll;
206 }
207
208 typedef union {
209 float f;
210 unsigned int i;
211 } _FloatByteUnion;
212
FloatSwap(const float * f)213 float FloatSwap (const float *f) {
214 _FloatByteUnion out;
215
216 out.f = *f;
217 out.i = LongSwap(out.i);
218
219 return out.f;
220 }
221
FloatNoSwap(const float * f)222 float FloatNoSwap (const float *f)
223 {
224 return *f;
225 }
226
227 /*
228 ================
229 Swap_Init
230 ================
231 */
232 /*
233 void Swap_Init (void)
234 {
235 byte swaptest[2] = {1,0};
236
237 // set the byte swapping variables in a portable manner
238 if ( *(short *)swaptest == 1)
239 {
240 _BigShort = ShortSwap;
241 _LittleShort = ShortNoSwap;
242 _BigLong = LongSwap;
243 _LittleLong = LongNoSwap;
244 _BigLong64 = Long64Swap;
245 _LittleLong64 = Long64NoSwap;
246 _BigFloat = FloatSwap;
247 _LittleFloat = FloatNoSwap;
248 }
249 else
250 {
251 _BigShort = ShortNoSwap;
252 _LittleShort = ShortSwap;
253 _BigLong = LongNoSwap;
254 _LittleLong = LongSwap;
255 _BigLong64 = Long64NoSwap;
256 _LittleLong64 = Long64Swap;
257 _BigFloat = FloatNoSwap;
258 _LittleFloat = FloatSwap;
259 }
260
261 }
262 */
263
264 /*
265 ============================================================================
266
267 PARSING
268
269 ============================================================================
270 */
271
272 static char com_token[MAX_TOKEN_CHARS];
273 static char com_parsename[MAX_TOKEN_CHARS];
274 static int com_lines;
275
COM_BeginParseSession(const char * name)276 void COM_BeginParseSession( const char *name )
277 {
278 com_lines = 0;
279 Com_sprintf(com_parsename, sizeof(com_parsename), "%s", name);
280 }
281
COM_GetCurrentParseLine(void)282 int COM_GetCurrentParseLine( void )
283 {
284 return com_lines;
285 }
286
COM_Parse(char ** data_p)287 char *COM_Parse( char **data_p )
288 {
289 return COM_ParseExt( data_p, qtrue );
290 }
291
COM_ParseError(char * format,...)292 void COM_ParseError( char *format, ... )
293 {
294 va_list argptr;
295 static char string[4096];
296
297 va_start (argptr, format);
298 Q_vsnprintf (string, sizeof(string), format, argptr);
299 va_end (argptr);
300
301 Com_Printf("ERROR: %s, line %d: %s\n", com_parsename, com_lines, string);
302 }
303
COM_ParseWarning(char * format,...)304 void COM_ParseWarning( char *format, ... )
305 {
306 va_list argptr;
307 static char string[4096];
308
309 va_start (argptr, format);
310 Q_vsnprintf (string, sizeof(string), format, argptr);
311 va_end (argptr);
312
313 Com_Printf("WARNING: %s, line %d: %s\n", com_parsename, com_lines, string);
314 }
315
316 /*
317 ==============
318 COM_Parse
319
320 Parse a token out of a string
321 Will never return NULL, just empty strings
322
323 If "allowLineBreaks" is qtrue then an empty
324 string will be returned if the next token is
325 a newline.
326 ==============
327 */
SkipWhitespace(char * data,qboolean * hasNewLines)328 static char *SkipWhitespace( char *data, qboolean *hasNewLines ) {
329 int c;
330
331 while( (c = *data) <= ' ') {
332 if( !c ) {
333 return NULL;
334 }
335 if( c == '\n' ) {
336 com_lines++;
337 *hasNewLines = qtrue;
338 }
339 data++;
340 }
341
342 return data;
343 }
344
COM_Compress(char * data_p)345 int COM_Compress( char *data_p ) {
346 char *in, *out;
347 int c;
348 qboolean newline = qfalse, whitespace = qfalse;
349
350 in = out = data_p;
351 if (in) {
352 while ((c = *in) != 0) {
353 // skip double slash comments
354 if ( c == '/' && in[1] == '/' ) {
355 while (*in && *in != '\n') {
356 in++;
357 }
358 // skip /* */ comments
359 } else if ( c == '/' && in[1] == '*' ) {
360 while ( *in && ( *in != '*' || in[1] != '/' ) )
361 in++;
362 if ( *in )
363 in += 2;
364 // record when we hit a newline
365 } else if ( c == '\n' || c == '\r' ) {
366 newline = qtrue;
367 in++;
368 // record when we hit whitespace
369 } else if ( c == ' ' || c == '\t') {
370 whitespace = qtrue;
371 in++;
372 // an actual token
373 } else {
374 // if we have a pending newline, emit it (and it counts as whitespace)
375 if (newline) {
376 *out++ = '\n';
377 newline = qfalse;
378 whitespace = qfalse;
379 } if (whitespace) {
380 *out++ = ' ';
381 whitespace = qfalse;
382 }
383
384 // copy quoted strings unmolested
385 if (c == '"') {
386 *out++ = c;
387 in++;
388 while (1) {
389 c = *in;
390 if (c && c != '"') {
391 *out++ = c;
392 in++;
393 } else {
394 break;
395 }
396 }
397 if (c == '"') {
398 *out++ = c;
399 in++;
400 }
401 } else {
402 *out = c;
403 out++;
404 in++;
405 }
406 }
407 }
408 }
409 *out = 0;
410 return out - data_p;
411 }
412
COM_ParseExt(char ** data_p,qboolean allowLineBreaks)413 char *COM_ParseExt( char **data_p, qboolean allowLineBreaks )
414 {
415 int c = 0, len;
416 qboolean hasNewLines = qfalse;
417 char *data;
418
419 data = *data_p;
420 len = 0;
421 com_token[0] = 0;
422
423 // make sure incoming data is valid
424 if ( !data )
425 {
426 *data_p = NULL;
427 return com_token;
428 }
429
430 while ( 1 )
431 {
432 // skip whitespace
433 data = SkipWhitespace( data, &hasNewLines );
434 if ( !data )
435 {
436 *data_p = NULL;
437 return com_token;
438 }
439 if ( hasNewLines && !allowLineBreaks )
440 {
441 *data_p = data;
442 return com_token;
443 }
444
445 c = *data;
446
447 // skip double slash comments
448 if ( c == '/' && data[1] == '/' )
449 {
450 data += 2;
451 while (*data && *data != '\n') {
452 data++;
453 }
454 }
455 // skip /* */ comments
456 else if ( c=='/' && data[1] == '*' )
457 {
458 data += 2;
459 while ( *data && ( *data != '*' || data[1] != '/' ) )
460 {
461 data++;
462 }
463 if ( *data )
464 {
465 data += 2;
466 }
467 }
468 else
469 {
470 break;
471 }
472 }
473
474 // handle quoted strings
475 if (c == '\"')
476 {
477 data++;
478 while (1)
479 {
480 c = *data++;
481 if (c=='\"' || !c)
482 {
483 com_token[len] = 0;
484 *data_p = ( char * ) data;
485 return com_token;
486 }
487 if (len < MAX_TOKEN_CHARS - 1)
488 {
489 com_token[len] = c;
490 len++;
491 }
492 }
493 }
494
495 // parse a regular word
496 do
497 {
498 if (len < MAX_TOKEN_CHARS - 1)
499 {
500 com_token[len] = c;
501 len++;
502 }
503 data++;
504 c = *data;
505 if ( c == '\n' )
506 com_lines++;
507 } while (c>32);
508
509 com_token[len] = 0;
510
511 *data_p = ( char * ) data;
512 return com_token;
513 }
514
515
516 #if 0
517 // no longer used
518 /*
519 ===============
520 COM_ParseInfos
521 ===============
522 */
523 int COM_ParseInfos( char *buf, int max, char infos[][MAX_INFO_STRING] ) {
524 char *token;
525 int count;
526 char key[MAX_TOKEN_CHARS];
527
528 count = 0;
529
530 while ( 1 ) {
531 token = COM_Parse( &buf );
532 if ( !token[0] ) {
533 break;
534 }
535 if ( strcmp( token, "{" ) ) {
536 Com_Printf( "Missing { in info file\n" );
537 break;
538 }
539
540 if ( count == max ) {
541 Com_Printf( "Max infos exceeded\n" );
542 break;
543 }
544
545 infos[count][0] = 0;
546 while ( 1 ) {
547 token = COM_ParseExt( &buf, qtrue );
548 if ( !token[0] ) {
549 Com_Printf( "Unexpected end of info file\n" );
550 break;
551 }
552 if ( !strcmp( token, "}" ) ) {
553 break;
554 }
555 Q_strncpyz( key, token, sizeof( key ) );
556
557 token = COM_ParseExt( &buf, qfalse );
558 if ( !token[0] ) {
559 strcpy( token, "<NULL>" );
560 }
561 Info_SetValueForKey( infos[count], key, token );
562 }
563 count++;
564 }
565
566 return count;
567 }
568 #endif
569
570
571 /*
572 ==================
573 COM_MatchToken
574 ==================
575 */
COM_MatchToken(char ** buf_p,char * match)576 void COM_MatchToken( char **buf_p, char *match ) {
577 char *token;
578
579 token = COM_Parse( buf_p );
580 if ( strcmp( token, match ) ) {
581 Com_Error( ERR_DROP, "MatchToken: %s != %s", token, match );
582 }
583 }
584
585
586 /*
587 =================
588 SkipBracedSection
589
590 The next token should be an open brace.
591 Skips until a matching close brace is found.
592 Internal brace depths are properly skipped.
593 =================
594 */
SkipBracedSection(char ** program)595 void SkipBracedSection (char **program) {
596 char *token;
597 int depth;
598
599 depth = 0;
600 do {
601 token = COM_ParseExt( program, qtrue );
602 if( token[1] == 0 ) {
603 if( token[0] == '{' ) {
604 depth++;
605 }
606 else if( token[0] == '}' ) {
607 depth--;
608 }
609 }
610 } while( depth && *program );
611 }
612
613 /*
614 =================
615 SkipRestOfLine
616 =================
617 */
SkipRestOfLine(char ** data)618 void SkipRestOfLine ( char **data ) {
619 char *p;
620 int c;
621
622 p = *data;
623 while ( (c = *p++) != 0 ) {
624 if ( c == '\n' ) {
625 com_lines++;
626 break;
627 }
628 }
629
630 *data = p;
631 }
632
633
Parse1DMatrix(char ** buf_p,int x,float * m)634 void Parse1DMatrix (char **buf_p, int x, float *m) {
635 char *token;
636 int i;
637
638 COM_MatchToken( buf_p, "(" );
639
640 for (i = 0 ; i < x ; i++) {
641 token = COM_Parse(buf_p);
642 m[i] = atof(token);
643 }
644
645 COM_MatchToken( buf_p, ")" );
646 }
647
Parse2DMatrix(char ** buf_p,int y,int x,float * m)648 void Parse2DMatrix (char **buf_p, int y, int x, float *m) {
649 int i;
650
651 COM_MatchToken( buf_p, "(" );
652
653 for (i = 0 ; i < y ; i++) {
654 Parse1DMatrix (buf_p, x, m + i * x);
655 }
656
657 COM_MatchToken( buf_p, ")" );
658 }
659
Parse3DMatrix(char ** buf_p,int z,int y,int x,float * m)660 void Parse3DMatrix (char **buf_p, int z, int y, int x, float *m) {
661 int i;
662
663 COM_MatchToken( buf_p, "(" );
664
665 for (i = 0 ; i < z ; i++) {
666 Parse2DMatrix (buf_p, y, x, m + i * x*y);
667 }
668
669 COM_MatchToken( buf_p, ")" );
670 }
671
672
673 /*
674 ============================================================================
675
676 LIBRARY REPLACEMENT FUNCTIONS
677
678 ============================================================================
679 */
680
Q_isprint(int c)681 int Q_isprint( int c )
682 {
683 if ( c >= 0x20 && c <= 0x7E )
684 return ( 1 );
685 return ( 0 );
686 }
687
Q_islower(int c)688 int Q_islower( int c )
689 {
690 if (c >= 'a' && c <= 'z')
691 return ( 1 );
692 return ( 0 );
693 }
694
Q_isupper(int c)695 int Q_isupper( int c )
696 {
697 if (c >= 'A' && c <= 'Z')
698 return ( 1 );
699 return ( 0 );
700 }
701
Q_isalpha(int c)702 int Q_isalpha( int c )
703 {
704 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
705 return ( 1 );
706 return ( 0 );
707 }
708
Q_strrchr(const char * string,int c)709 char* Q_strrchr( const char* string, int c )
710 {
711 char cc = c;
712 char *s;
713 char *sp=(char *)0;
714
715 s = (char*)string;
716
717 while (*s)
718 {
719 if (*s == cc)
720 sp = s;
721 s++;
722 }
723 if (cc == 0)
724 sp = s;
725
726 return sp;
727 }
728
729 /*
730 =============
731 Q_strncpyz
732
733 Safe strncpy that ensures a trailing zero
734 =============
735 */
Q_strncpyz(char * dest,const char * src,int destsize)736 void Q_strncpyz( char *dest, const char *src, int destsize ) {
737 if ( !dest ) {
738 Com_Error( ERR_FATAL, "Q_strncpyz: NULL dest" );
739 }
740 if ( !src ) {
741 Com_Error( ERR_FATAL, "Q_strncpyz: NULL src" );
742 }
743 if ( destsize < 1 ) {
744 Com_Error(ERR_FATAL,"Q_strncpyz: destsize < 1" );
745 }
746
747 strncpy( dest, src, destsize-1 );
748 dest[destsize-1] = 0;
749 }
750
Q_stricmpn(const char * s1,const char * s2,int n)751 int Q_stricmpn (const char *s1, const char *s2, int n) {
752 int c1, c2;
753
754 if ( s1 == NULL ) {
755 if ( s2 == NULL )
756 return 0;
757 else
758 return -1;
759 }
760 else if ( s2==NULL )
761 return 1;
762
763
764
765 do {
766 c1 = *s1++;
767 c2 = *s2++;
768
769 if (!n--) {
770 return 0; // strings are equal until end point
771 }
772
773 if (c1 != c2) {
774 if (c1 >= 'a' && c1 <= 'z') {
775 c1 -= ('a' - 'A');
776 }
777 if (c2 >= 'a' && c2 <= 'z') {
778 c2 -= ('a' - 'A');
779 }
780 if (c1 != c2) {
781 return c1 < c2 ? -1 : 1;
782 }
783 }
784 } while (c1);
785
786 return 0; // strings are equal
787 }
788
Q_strncmp(const char * s1,const char * s2,int n)789 int Q_strncmp (const char *s1, const char *s2, int n) {
790 int c1, c2;
791
792 do {
793 c1 = *s1++;
794 c2 = *s2++;
795
796 if (!n--) {
797 return 0; // strings are equal until end point
798 }
799
800 if (c1 != c2) {
801 return c1 < c2 ? -1 : 1;
802 }
803 } while (c1);
804
805 return 0; // strings are equal
806 }
807
Q_stricmp(const char * s1,const char * s2)808 int Q_stricmp (const char *s1, const char *s2) {
809 return (s1 && s2) ? Q_stricmpn (s1, s2, 99999) : -1;
810 }
811
812
Q_strlwr(char * s1)813 char *Q_strlwr( char *s1 ) {
814 char *s;
815
816 s = s1;
817 while ( *s ) {
818 *s = tolower(*s);
819 s++;
820 }
821 return s1;
822 }
823
Q_strupr(char * s1)824 char *Q_strupr( char *s1 ) {
825 char *s;
826
827 s = s1;
828 while ( *s ) {
829 *s = toupper(*s);
830 s++;
831 }
832 return s1;
833 }
834
835
836 // never goes past bounds or leaves without a terminating 0
Q_strcat(char * dest,int size,const char * src)837 void Q_strcat( char *dest, int size, const char *src ) {
838 int l1;
839
840 l1 = strlen( dest );
841 if ( l1 >= size ) {
842 Com_Error( ERR_FATAL, "Q_strcat: already overflowed" );
843 }
844 Q_strncpyz( dest + l1, src, size - l1 );
845 }
846
847 /*
848 * Find the first occurrence of find in s.
849 */
Q_stristr(const char * s,const char * find)850 const char *Q_stristr( const char *s, const char *find)
851 {
852 char c, sc;
853 size_t len;
854
855 if ((c = *find++) != 0)
856 {
857 if (c >= 'a' && c <= 'z')
858 {
859 c -= ('a' - 'A');
860 }
861 len = strlen(find);
862 do
863 {
864 do
865 {
866 if ((sc = *s++) == 0)
867 return NULL;
868 if (sc >= 'a' && sc <= 'z')
869 {
870 sc -= ('a' - 'A');
871 }
872 } while (sc != c);
873 } while (Q_stricmpn(s, find, len) != 0);
874 s--;
875 }
876 return s;
877 }
878
879
Q_PrintStrlen(const char * string)880 int Q_PrintStrlen( const char *string ) {
881 int len;
882 const char *p;
883
884 if( !string ) {
885 return 0;
886 }
887
888 len = 0;
889 p = string;
890 while( *p ) {
891 if( Q_IsColorString( p ) ) {
892 p += 2;
893 continue;
894 }
895 p++;
896 len++;
897 }
898
899 return len;
900 }
901
902
Q_CleanStr(char * string)903 char *Q_CleanStr( char *string ) {
904 char* d;
905 char* s;
906 int c;
907
908 s = string;
909 d = string;
910 while ((c = *s) != 0 ) {
911 if ( Q_IsColorString( s ) ) {
912 s++;
913 }
914 else if ( c >= 0x20 && c <= 0x7E ) {
915 *d++ = c;
916 }
917 s++;
918 }
919 *d = '\0';
920
921 return string;
922 }
923
Q_CountChar(const char * string,char tocount)924 int Q_CountChar(const char *string, char tocount)
925 {
926 int count;
927
928 for(count = 0; *string; string++)
929 {
930 if(*string == tocount)
931 count++;
932 }
933
934 return count;
935 }
936
Com_sprintf(char * dest,int size,const char * fmt,...)937 void QDECL Com_sprintf( char *dest, int size, const char *fmt, ...) {
938 int len;
939 va_list argptr;
940 char bigbuffer[32000]; // big, but small enough to fit in PPC stack
941
942 va_start (argptr,fmt);
943 len = Q_vsnprintf (bigbuffer, sizeof(bigbuffer), fmt,argptr);
944 va_end (argptr);
945 if ( len >= sizeof( bigbuffer ) ) {
946 Com_Error( ERR_FATAL, "Com_sprintf: overflowed bigbuffer" );
947 }
948 if (len >= size) {
949 Com_Printf ("Com_sprintf: overflow of %i in %i\n", len, size);
950 #ifdef _DEBUG
951 __asm {
952 int 3;
953 }
954 #endif
955 }
956 Q_strncpyz (dest, bigbuffer, size );
957 }
958
959
960 /*
961 ============
962 va
963
964 does a varargs printf into a temp buffer, so I don't need to have
965 varargs versions of all text functions.
966 ============
967 */
va(char * format,...)968 char * QDECL va( char *format, ... ) {
969 va_list argptr;
970 static char string[2][32000]; // in case va is called by nested functions
971 static int index = 0;
972 char *buf;
973
974 buf = string[index & 1];
975 index++;
976
977 va_start (argptr, format);
978 Q_vsnprintf (buf, sizeof(*string), format, argptr);
979 va_end (argptr);
980
981 return buf;
982 }
983
984 /*
985 ============
986 Com_TruncateLongString
987
988 Assumes buffer is atleast TRUNCATE_LENGTH big
989 ============
990 */
Com_TruncateLongString(char * buffer,const char * s)991 void Com_TruncateLongString( char *buffer, const char *s )
992 {
993 int length = strlen( s );
994
995 if( length <= TRUNCATE_LENGTH )
996 Q_strncpyz( buffer, s, TRUNCATE_LENGTH );
997 else
998 {
999 Q_strncpyz( buffer, s, ( TRUNCATE_LENGTH / 2 ) - 3 );
1000 Q_strcat( buffer, TRUNCATE_LENGTH, " ... " );
1001 Q_strcat( buffer, TRUNCATE_LENGTH, s + length - ( TRUNCATE_LENGTH / 2 ) + 3 );
1002 }
1003 }
1004
1005 /*
1006 =====================================================================
1007
1008 INFO STRINGS
1009
1010 =====================================================================
1011 */
1012
1013 /*
1014 ===============
1015 Info_ValueForKey
1016
1017 Searches the string for the given
1018 key and returns the associated value, or an empty string.
1019 FIXME: overflow check?
1020 ===============
1021 */
Info_ValueForKey(const char * s,const char * key)1022 char *Info_ValueForKey( const char *s, const char *key ) {
1023 char pkey[BIG_INFO_KEY];
1024 static char value[2][BIG_INFO_VALUE]; // use two buffers so compares
1025 // work without stomping on each other
1026 static int valueindex = 0;
1027 char *o;
1028
1029 if ( !s || !key ) {
1030 return "";
1031 }
1032
1033 if ( strlen( s ) >= BIG_INFO_STRING ) {
1034 Com_Error( ERR_DROP, "Info_ValueForKey: oversize infostring" );
1035 }
1036
1037 valueindex ^= 1;
1038 if (*s == '\\')
1039 s++;
1040 while (1)
1041 {
1042 o = pkey;
1043 while (*s != '\\')
1044 {
1045 if (!*s)
1046 return "";
1047 *o++ = *s++;
1048 }
1049 *o = 0;
1050 s++;
1051
1052 o = value[valueindex];
1053
1054 while (*s != '\\' && *s)
1055 {
1056 *o++ = *s++;
1057 }
1058 *o = 0;
1059
1060 if (!Q_stricmp (key, pkey) )
1061 return value[valueindex];
1062
1063 if (!*s)
1064 break;
1065 s++;
1066 }
1067
1068 return "";
1069 }
1070
1071
1072 /*
1073 ===================
1074 Info_NextPair
1075
1076 Used to itterate through all the key/value pairs in an info string
1077 ===================
1078 */
Info_NextPair(const char ** head,char * key,char * value)1079 void Info_NextPair( const char **head, char *key, char *value ) {
1080 char *o;
1081 const char *s;
1082
1083 s = *head;
1084
1085 if ( *s == '\\' ) {
1086 s++;
1087 }
1088 key[0] = 0;
1089 value[0] = 0;
1090
1091 o = key;
1092 while ( *s != '\\' ) {
1093 if ( !*s ) {
1094 *o = 0;
1095 *head = s;
1096 return;
1097 }
1098 *o++ = *s++;
1099 }
1100 *o = 0;
1101 s++;
1102
1103 o = value;
1104 while ( *s != '\\' && *s ) {
1105 *o++ = *s++;
1106 }
1107 *o = 0;
1108
1109 *head = s;
1110 }
1111
1112
1113 /*
1114 ===================
1115 Info_RemoveKey
1116 ===================
1117 */
Info_RemoveKey(char * s,const char * key)1118 void Info_RemoveKey( char *s, const char *key ) {
1119 char *start;
1120 char pkey[MAX_INFO_KEY];
1121 char value[MAX_INFO_VALUE];
1122 char *o;
1123
1124 if ( strlen( s ) >= MAX_INFO_STRING ) {
1125 Com_Error( ERR_DROP, "Info_RemoveKey: oversize infostring" );
1126 }
1127
1128 if (strchr (key, '\\')) {
1129 return;
1130 }
1131
1132 while (1)
1133 {
1134 start = s;
1135 if (*s == '\\')
1136 s++;
1137 o = pkey;
1138 while (*s != '\\')
1139 {
1140 if (!*s)
1141 return;
1142 *o++ = *s++;
1143 }
1144 *o = 0;
1145 s++;
1146
1147 o = value;
1148 while (*s != '\\' && *s)
1149 {
1150 if (!*s)
1151 return;
1152 *o++ = *s++;
1153 }
1154 *o = 0;
1155
1156 if (!strcmp (key, pkey) )
1157 {
1158 memmove(start, s, strlen(s) + 1); // remove this part
1159
1160 return;
1161 }
1162
1163 if (!*s)
1164 return;
1165 }
1166
1167 }
1168
1169 /*
1170 ===================
1171 Info_RemoveKey_Big
1172 ===================
1173 */
Info_RemoveKey_Big(char * s,const char * key)1174 void Info_RemoveKey_Big( char *s, const char *key ) {
1175 char *start;
1176 char pkey[BIG_INFO_KEY];
1177 char value[BIG_INFO_VALUE];
1178 char *o;
1179
1180 if ( strlen( s ) >= BIG_INFO_STRING ) {
1181 Com_Error( ERR_DROP, "Info_RemoveKey_Big: oversize infostring" );
1182 }
1183
1184 if (strchr (key, '\\')) {
1185 return;
1186 }
1187
1188 while (1)
1189 {
1190 start = s;
1191 if (*s == '\\')
1192 s++;
1193 o = pkey;
1194 while (*s != '\\')
1195 {
1196 if (!*s)
1197 return;
1198 *o++ = *s++;
1199 }
1200 *o = 0;
1201 s++;
1202
1203 o = value;
1204 while (*s != '\\' && *s)
1205 {
1206 if (!*s)
1207 return;
1208 *o++ = *s++;
1209 }
1210 *o = 0;
1211
1212 if (!strcmp (key, pkey) )
1213 {
1214 strcpy (start, s); // remove this part
1215 return;
1216 }
1217
1218 if (!*s)
1219 return;
1220 }
1221
1222 }
1223
1224
1225
1226
1227 /*
1228 ==================
1229 Info_Validate
1230
1231 Some characters are illegal in info strings because they
1232 can mess up the server's parsing
1233 ==================
1234 */
Info_Validate(const char * s)1235 qboolean Info_Validate( const char *s ) {
1236 if ( strchr( s, '\"' ) ) {
1237 return qfalse;
1238 }
1239 if ( strchr( s, ';' ) ) {
1240 return qfalse;
1241 }
1242 return qtrue;
1243 }
1244
1245 /*
1246 ==================
1247 Info_SetValueForKey
1248
1249 Changes or adds a key/value pair
1250 ==================
1251 */
Info_SetValueForKey(char * s,const char * key,const char * value)1252 void Info_SetValueForKey( char *s, const char *key, const char *value ) {
1253 char newi[MAX_INFO_STRING];
1254 const char* blacklist = "\\;\"";
1255
1256 if ( strlen( s ) >= MAX_INFO_STRING ) {
1257 Com_Error( ERR_DROP, "Info_SetValueForKey: oversize infostring" );
1258 }
1259
1260 for(; *blacklist; ++blacklist)
1261 {
1262 if (strchr (key, *blacklist) || strchr (value, *blacklist))
1263 {
1264 Com_Printf (S_COLOR_YELLOW "Can't use keys or values with a '%c': %s = %s\n", *blacklist, key, value);
1265 return;
1266 }
1267 }
1268
1269 Info_RemoveKey (s, key);
1270 if (!value || !strlen(value))
1271 return;
1272
1273 Com_sprintf (newi, sizeof(newi), "\\%s\\%s", key, value);
1274
1275 if (strlen(newi) + strlen(s) >= MAX_INFO_STRING)
1276 {
1277 Com_Printf ("Info string length exceeded\n");
1278 return;
1279 }
1280
1281 strcat (newi, s);
1282 strcpy (s, newi);
1283 }
1284
1285 /*
1286 ==================
1287 Info_SetValueForKey_Big
1288
1289 Changes or adds a key/value pair
1290 ==================
1291 */
Info_SetValueForKey_Big(char * s,const char * key,const char * value)1292 void Info_SetValueForKey_Big( char *s, const char *key, const char *value ) {
1293 char newi[BIG_INFO_STRING];
1294 const char* blacklist = "\\;\"";
1295
1296 if ( strlen( s ) >= BIG_INFO_STRING ) {
1297 Com_Error( ERR_DROP, "Info_SetValueForKey: oversize infostring" );
1298 }
1299
1300 for(; *blacklist; ++blacklist)
1301 {
1302 if (strchr (key, *blacklist) || strchr (value, *blacklist))
1303 {
1304 Com_Printf (S_COLOR_YELLOW "Can't use keys or values with a '%c': %s = %s\n", *blacklist, key, value);
1305 return;
1306 }
1307 }
1308
1309 Info_RemoveKey_Big (s, key);
1310 if (!value || !strlen(value))
1311 return;
1312
1313 Com_sprintf (newi, sizeof(newi), "\\%s\\%s", key, value);
1314
1315 if (strlen(newi) + strlen(s) >= BIG_INFO_STRING)
1316 {
1317 Com_Printf ("BIG Info string length exceeded\n");
1318 return;
1319 }
1320
1321 strcat (s, newi);
1322 }
1323
1324
1325
1326
1327 //====================================================================
1328
1329 /*
1330 ==================
1331 Com_CharIsOneOfCharset
1332 ==================
1333 */
Com_CharIsOneOfCharset(char c,char * set)1334 static qboolean Com_CharIsOneOfCharset( char c, char *set )
1335 {
1336 int i;
1337
1338 for( i = 0; i < strlen( set ); i++ )
1339 {
1340 if( set[ i ] == c )
1341 return qtrue;
1342 }
1343
1344 return qfalse;
1345 }
1346
1347 /*
1348 ==================
1349 Com_SkipCharset
1350 ==================
1351 */
Com_SkipCharset(char * s,char * sep)1352 char *Com_SkipCharset( char *s, char *sep )
1353 {
1354 char *p = s;
1355
1356 while( p )
1357 {
1358 if( Com_CharIsOneOfCharset( *p, sep ) )
1359 p++;
1360 else
1361 break;
1362 }
1363
1364 return p;
1365 }
1366
1367 /*
1368 ==================
1369 Com_SkipTokens
1370 ==================
1371 */
Com_SkipTokens(char * s,int numTokens,char * sep)1372 char *Com_SkipTokens( char *s, int numTokens, char *sep )
1373 {
1374 int sepCount = 0;
1375 char *p = s;
1376
1377 while( sepCount < numTokens )
1378 {
1379 if( Com_CharIsOneOfCharset( *p++, sep ) )
1380 {
1381 sepCount++;
1382 while( Com_CharIsOneOfCharset( *p, sep ) )
1383 p++;
1384 }
1385 else if( *p == '\0' )
1386 break;
1387 }
1388
1389 if( sepCount == numTokens )
1390 return p;
1391 else
1392 return s;
1393 }
1394