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