]> icculus.org git repositories - divverent/darkplaces.git/blob - common.c
Mostly [v]sprintf -> [v]snprintf replacements, and a couple of other fixes regarding...
[divverent/darkplaces.git] / common.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // common.c -- misc functions used in client and server
21
22 #include <stdlib.h>
23 #include <fcntl.h>
24 #ifndef WIN32
25 #include <unistd.h>
26 #endif
27
28 #include "quakedef.h"
29
30 cvar_t registered = {0, "registered","0"};
31 cvar_t cmdline = {0, "cmdline","0"};
32
33 extern qboolean fs_modified;   // set true if using non-id files
34
35 char com_token[1024];
36 int com_argc;
37 const char **com_argv;
38
39 // LordHavoc: made commandline 1024 characters instead of 256
40 #define CMDLINE_LENGTH  1024
41 char com_cmdline[CMDLINE_LENGTH];
42
43 int gamemode;
44 char *gamename;
45 char *gamedirname;
46 char com_modname[MAX_OSPATH];
47
48
49 /*
50 ============================================================================
51
52                                         BYTE ORDER FUNCTIONS
53
54 ============================================================================
55 */
56
57 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
58 short   (*BigShort) (short l);
59 short   (*LittleShort) (short l);
60 int     (*BigLong) (int l);
61 int     (*LittleLong) (int l);
62 float   (*BigFloat) (float l);
63 float   (*LittleFloat) (float l);
64 #endif
65
66 short   ShortSwap (short l)
67 {
68         qbyte    b1,b2;
69
70         b1 = l&255;
71         b2 = (l>>8)&255;
72
73         return (b1<<8) + b2;
74 }
75
76 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
77 short   ShortNoSwap (short l)
78 {
79         return l;
80 }
81 #endif
82
83 int    LongSwap (int l)
84 {
85         qbyte    b1,b2,b3,b4;
86
87         b1 = l&255;
88         b2 = (l>>8)&255;
89         b3 = (l>>16)&255;
90         b4 = (l>>24)&255;
91
92         return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
93 }
94
95 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
96 int     LongNoSwap (int l)
97 {
98         return l;
99 }
100 #endif
101
102 float FloatSwap (float f)
103 {
104         union
105         {
106                 float   f;
107                 qbyte    b[4];
108         } dat1, dat2;
109
110
111         dat1.f = f;
112         dat2.b[0] = dat1.b[3];
113         dat2.b[1] = dat1.b[2];
114         dat2.b[2] = dat1.b[1];
115         dat2.b[3] = dat1.b[0];
116         return dat2.f;
117 }
118
119 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
120 float FloatNoSwap (float f)
121 {
122         return f;
123 }
124 #endif
125
126
127 // Extract integers from buffers
128
129 unsigned int BuffBigLong (const qbyte *buffer)
130 {
131         return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
132 }
133
134 unsigned short BuffBigShort (const qbyte *buffer)
135 {
136         return (buffer[0] << 8) | buffer[1];
137 }
138
139 unsigned int BuffLittleLong (const qbyte *buffer)
140 {
141         return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
142 }
143
144 unsigned short BuffLittleShort (const qbyte *buffer)
145 {
146         return (buffer[1] << 8) | buffer[0];
147 }
148
149
150 /*
151 ==============================================================================
152
153                         MESSAGE IO FUNCTIONS
154
155 Handles byte ordering and avoids alignment errors
156 ==============================================================================
157 */
158
159 //
160 // writing functions
161 //
162
163 void MSG_WriteChar (sizebuf_t *sb, int c)
164 {
165         qbyte    *buf;
166
167         buf = SZ_GetSpace (sb, 1);
168         buf[0] = c;
169 }
170
171 void MSG_WriteByte (sizebuf_t *sb, int c)
172 {
173         qbyte    *buf;
174
175         buf = SZ_GetSpace (sb, 1);
176         buf[0] = c;
177 }
178
179 void MSG_WriteShort (sizebuf_t *sb, int c)
180 {
181         qbyte    *buf;
182
183         buf = SZ_GetSpace (sb, 2);
184         buf[0] = c&0xff;
185         buf[1] = c>>8;
186 }
187
188 void MSG_WriteLong (sizebuf_t *sb, int c)
189 {
190         qbyte    *buf;
191
192         buf = SZ_GetSpace (sb, 4);
193         buf[0] = c&0xff;
194         buf[1] = (c>>8)&0xff;
195         buf[2] = (c>>16)&0xff;
196         buf[3] = c>>24;
197 }
198
199 void MSG_WriteFloat (sizebuf_t *sb, float f)
200 {
201         union
202         {
203                 float   f;
204                 int     l;
205         } dat;
206
207
208         dat.f = f;
209         dat.l = LittleLong (dat.l);
210
211         SZ_Write (sb, &dat.l, 4);
212 }
213
214 void MSG_WriteString (sizebuf_t *sb, const char *s)
215 {
216         if (!s)
217                 SZ_Write (sb, "", 1);
218         else
219                 SZ_Write (sb, s, strlen(s)+1);
220 }
221
222 // used by server (always latest PROTOCOL_DARKPLACES)
223 void MSG_WriteDPCoord (sizebuf_t *sb, float f)
224 {
225         if (f >= 0)
226                 MSG_WriteShort (sb, (int)(f + 0.5f));
227         else
228                 MSG_WriteShort (sb, (int)(f - 0.5f));
229 }
230
231 void MSG_WritePreciseAngle (sizebuf_t *sb, float f)
232 {
233         if (f >= 0)
234                 MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) + 0.5f) & 65535);
235         else
236                 MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) - 0.5f) & 65535);
237 }
238
239 // LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
240 void MSG_WriteAngle (sizebuf_t *sb, float f)
241 {
242         if (f >= 0)
243                 MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) + 0.5f) & 255);
244         else
245                 MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) - 0.5f) & 255);
246 }
247
248 //
249 // reading functions
250 //
251 int msg_readcount;
252 qboolean msg_badread;
253
254 void MSG_BeginReading (void)
255 {
256         msg_readcount = 0;
257         msg_badread = false;
258 }
259
260 int MSG_ReadLittleShort (void)
261 {
262         if (msg_readcount+2 > net_message.cursize)
263         {
264                 msg_badread = true;
265                 return -1;
266         }
267         msg_readcount += 2;
268         return (short)(net_message.data[msg_readcount-2] | (net_message.data[msg_readcount-1]<<8));
269 }
270
271 int MSG_ReadBigShort (void)
272 {
273         if (msg_readcount+2 > net_message.cursize)
274         {
275                 msg_badread = true;
276                 return -1;
277         }
278         msg_readcount += 2;
279         return (short)((net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1]);
280 }
281
282 int MSG_ReadLittleLong (void)
283 {
284         if (msg_readcount+4 > net_message.cursize)
285         {
286                 msg_badread = true;
287                 return -1;
288         }
289         msg_readcount += 4;
290         return net_message.data[msg_readcount-4] | (net_message.data[msg_readcount-3]<<8) | (net_message.data[msg_readcount-2]<<16) | (net_message.data[msg_readcount-1]<<24);
291 }
292
293 int MSG_ReadBigLong (void)
294 {
295         if (msg_readcount+4 > net_message.cursize)
296         {
297                 msg_badread = true;
298                 return -1;
299         }
300         msg_readcount += 4;
301         return (net_message.data[msg_readcount-4]<<24) + (net_message.data[msg_readcount-3]<<16) + (net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1];
302 }
303
304 float MSG_ReadLittleFloat (void)
305 {
306         union
307         {
308                 float f;
309                 int l;
310         } dat;
311         if (msg_readcount+4 > net_message.cursize)
312         {
313                 msg_badread = true;
314                 return -1;
315         }
316         msg_readcount += 4;
317         dat.l = net_message.data[msg_readcount-4] | (net_message.data[msg_readcount-3]<<8) | (net_message.data[msg_readcount-2]<<16) | (net_message.data[msg_readcount-1]<<24);
318         return dat.f;
319 }
320
321 float MSG_ReadBigFloat (void)
322 {
323         union
324         {
325                 float f;
326                 int l;
327         } dat;
328         if (msg_readcount+4 > net_message.cursize)
329         {
330                 msg_badread = true;
331                 return -1;
332         }
333         msg_readcount += 4;
334         dat.l = (net_message.data[msg_readcount-4]<<24) | (net_message.data[msg_readcount-3]<<16) | (net_message.data[msg_readcount-2]<<8) | net_message.data[msg_readcount-1];
335         return dat.f;
336 }
337
338 char *MSG_ReadString (void)
339 {
340         static char string[2048];
341         int l,c;
342         for (l = 0;l < (int) sizeof(string) - 1 && (c = MSG_ReadChar()) != -1 && c != 0;l++)
343                 string[l] = c;
344         string[l] = 0;
345         return string;
346 }
347
348 int MSG_ReadBytes (int numbytes, unsigned char *out)
349 {
350         int l, c;
351         for (l = 0;l < numbytes && (c = MSG_ReadChar()) != -1;l++)
352                 out[l] = c;
353         return l;
354 }
355
356 // used by server (always latest PROTOCOL_DARKPLACES)
357 float MSG_ReadDPCoord (void)
358 {
359         return (signed short) MSG_ReadLittleShort();
360 }
361
362 // used by client
363 float MSG_ReadCoord (void)
364 {
365         if (cl.protocol == PROTOCOL_DARKPLACES2 || cl.protocol == PROTOCOL_DARKPLACES3 || cl.protocol == PROTOCOL_DARKPLACES4)
366                 return (signed short) MSG_ReadLittleShort();
367         else if (cl.protocol == PROTOCOL_DARKPLACES1)
368                 return MSG_ReadLittleFloat();
369         else
370                 return MSG_ReadLittleShort() * (1.0f/8.0f);
371 }
372
373
374 //===========================================================================
375
376 void SZ_Alloc (sizebuf_t *buf, int startsize, const char *name)
377 {
378         if (startsize < 256)
379                 startsize = 256;
380         buf->mempool = Mem_AllocPool(name);
381         buf->data = Mem_Alloc(buf->mempool, startsize);
382         buf->maxsize = startsize;
383         buf->cursize = 0;
384 }
385
386
387 void SZ_Free (sizebuf_t *buf)
388 {
389         Mem_FreePool(&buf->mempool);
390         buf->data = NULL;
391         buf->maxsize = 0;
392         buf->cursize = 0;
393 }
394
395 void SZ_Clear (sizebuf_t *buf)
396 {
397         buf->cursize = 0;
398 }
399
400 void *SZ_GetSpace (sizebuf_t *buf, int length)
401 {
402         void *data;
403
404         if (buf->cursize + length > buf->maxsize)
405         {
406                 if (!buf->allowoverflow)
407                         Host_Error ("SZ_GetSpace: overflow without allowoverflow set\n");
408
409                 if (length > buf->maxsize)
410                         Host_Error ("SZ_GetSpace: %i is > full buffer size\n", length);
411
412                 buf->overflowed = true;
413                 Con_Printf ("SZ_GetSpace: overflow\n");
414                 SZ_Clear (buf);
415         }
416
417         data = buf->data + buf->cursize;
418         buf->cursize += length;
419
420         return data;
421 }
422
423 void SZ_Write (sizebuf_t *buf, const void *data, int length)
424 {
425         memcpy (SZ_GetSpace(buf,length),data,length);
426 }
427
428 void SZ_Print (sizebuf_t *buf, const char *data)
429 {
430         int len;
431         len = strlen(data)+1;
432
433 // byte * cast to keep VC++ happy
434         if (buf->data[buf->cursize-1])
435                 memcpy ((qbyte *)SZ_GetSpace(buf, len),data,len); // no trailing 0
436         else
437                 memcpy ((qbyte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0
438 }
439
440 static char *hexchar = "0123456789ABCDEF";
441 void Com_HexDumpToConsole(const qbyte *data, int size)
442 {
443         int i, j, n;
444         char text[1024];
445         char *cur, *flushpointer;
446         const qbyte *d;
447         cur = text;
448         flushpointer = text + 512;
449         for (i = 0;i < size;)
450         {
451                 n = 16;
452                 if (n > size - i)
453                         n = size - i;
454                 d = data + i;
455                 *cur++ = hexchar[(i >> 12) & 15];
456                 *cur++ = hexchar[(i >>  8) & 15];
457                 *cur++ = hexchar[(i >>  4) & 15];
458                 *cur++ = hexchar[(i >>  0) & 15];
459                 *cur++ = ':';
460                 for (j = 0;j < n;j++)
461                 {
462                         if (j & 1)
463                         {
464                                 *cur++ = hexchar[(d[j] >> 4) & 15] | 0x80;
465                                 *cur++ = hexchar[(d[j] >> 0) & 15] | 0x80;
466                         }
467                         else
468                         {
469                                 *cur++ = hexchar[(d[j] >> 4) & 15];
470                                 *cur++ = hexchar[(d[j] >> 0) & 15];
471                         }
472                 }
473                 for (;j < 16;j++)
474                 {
475                         *cur++ = ' ';
476                         *cur++ = ' ';
477                 }
478                 for (j = 0;j < n;j++)
479                         *cur++ = (d[j] >= ' ' && d[j] <= 0x7E) ? d[j] : '.';
480                 for (;j < 16;j++)
481                         *cur++ = ' ';
482                 *cur++ = '\n';
483                 i += n;
484                 if (cur >= flushpointer || i >= size)
485                 {
486                         *cur++ = 0;
487                         Con_Printf("%s", text);
488                         cur = text;
489                 }
490         }
491 }
492
493 void SZ_HexDumpToConsole(const sizebuf_t *buf)
494 {
495         Com_HexDumpToConsole(buf->data, buf->cursize);
496 }
497
498
499 //============================================================================
500
501
502 /*
503 ==============
504 COM_ParseToken
505
506 Parse a token out of a string
507 ==============
508 */
509 int COM_ParseToken(const char **datapointer, int returnnewline)
510 {
511         int c;
512         int len;
513         const char *data = *datapointer;
514
515         len = 0;
516         com_token[0] = 0;
517
518         if (!data)
519         {
520                 *datapointer = NULL;
521                 return false;
522         }
523
524 // skip whitespace
525 skipwhite:
526         while ((c = *data) <= ' ' && (c != '\n' || !returnnewline))
527         {
528                 if (c == 0)
529                 {
530                         // end of file
531                         *datapointer = NULL;
532                         return false;
533                 }
534                 data++;
535         }
536
537         // check if it's a comment
538         if (c == '/')
539         {
540                 // skip // comments
541                 if (data[1] == '/')
542                 {
543                         while (*data && *data != '\n')
544                                 data++;
545                         goto skipwhite;
546                 }
547                 // skip /* comments
548                 if (data[1] == '*')
549                 {
550                         while (*data && *data != '*' && data[1] != '/')
551                                 data++;
552                         goto skipwhite;
553                 }
554         }
555
556 // handle quoted strings specially
557         if (c == '\"')
558         {
559                 data++;
560                 while (1)
561                 {
562                         c = *data++;
563                         if (c == '\"' || !c)
564                         {
565                                 com_token[len] = 0;
566                                 *datapointer = data;
567                                 return true;
568                         }
569                         com_token[len] = c;
570                         len++;
571                 }
572         }
573
574 // parse single characters
575         if (c == '{' || c == '}' || c == ')' || c == '(' || c == ']' || c == '[' || c == '\'' || c == ':' || c == ',' || c == ';' || c == '\n')
576         {
577                 com_token[len] = c;
578                 len++;
579                 com_token[len] = 0;
580                 *datapointer = data+1;
581                 return true;
582         }
583
584 // parse a regular word
585         do
586         {
587                 com_token[len] = c;
588                 data++;
589                 len++;
590                 c = *data;
591                 if (c == '{' || c == '}' || c == ')' || c == '(' || c == ']' || c == '[' || c == '\'' || c == ':' || c == ',' || c == ';')
592                         break;
593         } while (c>32);
594
595         com_token[len] = 0;
596         *datapointer = data;
597         return true;
598 }
599
600
601 /*
602 ================
603 COM_CheckParm
604
605 Returns the position (1 to argc-1) in the program's argument list
606 where the given parameter apears, or 0 if not present
607 ================
608 */
609 int COM_CheckParm (const char *parm)
610 {
611         int i;
612
613         for (i=1 ; i<com_argc ; i++)
614         {
615                 if (!com_argv[i])
616                         continue;               // NEXTSTEP sometimes clears appkit vars.
617                 if (!strcmp (parm,com_argv[i]))
618                         return i;
619         }
620
621         return 0;
622 }
623
624 /*
625 ================
626 COM_CheckRegistered
627
628 Looks for the pop.txt file and verifies it.
629 Sets the "registered" cvar.
630 Immediately exits out if an alternate game was attempted to be started without
631 being registered.
632 ================
633 */
634 void COM_CheckRegistered (void)
635 {
636         Cvar_Set ("cmdline", com_cmdline);
637
638         if (!FS_FileExists("gfx/pop.lmp"))
639         {
640                 if (fs_modified)
641                         Con_Printf ("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n");
642                 else
643                         Con_Printf ("Playing shareware version.\n");
644                 return;
645         }
646
647         Cvar_Set ("registered", "1");
648         Con_Printf ("Playing registered version.\n");
649 }
650
651
652 /*
653 ================
654 COM_InitArgv
655 ================
656 */
657 void COM_InitArgv (void)
658 {
659         int i, j, n;
660         // reconstitute the command line for the cmdline externally visible cvar
661         n = 0;
662         for (j = 0;(j < MAX_NUM_ARGVS) && (j < com_argc);j++)
663         {
664                 i = 0;
665                 while ((n < (CMDLINE_LENGTH - 1)) && com_argv[j][i])
666                         com_cmdline[n++] = com_argv[j][i++];
667                 if (n < (CMDLINE_LENGTH - 1))
668                         com_cmdline[n++] = ' ';
669                 else
670                         break;
671         }
672         com_cmdline[n] = 0;
673 }
674
675 void COM_InitGameType (void)
676 {
677         char name[MAX_OSPATH];
678         FS_StripExtension(com_argv[0], name);
679         COM_ToLowerString(name, name);
680
681         if (strstr(name, "transfusion"))
682                 gamemode = GAME_TRANSFUSION;
683         else if (strstr(name, "nexuiz"))
684                 gamemode = GAME_NEXUIZ;
685         else if (strstr(name, "nehahra"))
686                 gamemode = GAME_NEHAHRA;
687         else if (strstr(name, "hipnotic"))
688                 gamemode = GAME_HIPNOTIC;
689         else if (strstr(name, "rogue"))
690                 gamemode = GAME_ROGUE;
691         else if (strstr(name, "gvb2"))
692                 gamemode = GAME_GOODVSBAD2;
693         else if (strstr(name, "teu"))
694                 gamemode = GAME_TEU;
695         else if (strstr(name, "battlemech"))
696                 gamemode = GAME_BATTLEMECH;
697         else if (strstr(name, "zymotic"))
698                 gamemode = GAME_ZYMOTIC;
699         else
700                 gamemode = GAME_NORMAL;
701
702         if (COM_CheckParm ("-transfusion"))
703                 gamemode = GAME_TRANSFUSION;
704         else if (COM_CheckParm ("-nexuiz"))
705                 gamemode = GAME_NEXUIZ;
706         else if (COM_CheckParm ("-nehahra"))
707                 gamemode = GAME_NEHAHRA;
708         else if (COM_CheckParm ("-hipnotic"))
709                 gamemode = GAME_HIPNOTIC;
710         else if (COM_CheckParm ("-rogue"))
711                 gamemode = GAME_ROGUE;
712         else if (COM_CheckParm ("-quake"))
713                 gamemode = GAME_NORMAL;
714         else if (COM_CheckParm ("-goodvsbad2"))
715                 gamemode = GAME_GOODVSBAD2;
716         else if (COM_CheckParm ("-teu"))
717                 gamemode = GAME_TEU;
718         else if (COM_CheckParm ("-battlemech"))
719                 gamemode = GAME_BATTLEMECH;
720         else if (COM_CheckParm ("-zymotic"))
721                 gamemode = GAME_ZYMOTIC;
722
723         switch(gamemode)
724         {
725         case GAME_NORMAL:
726                 gamename = "DarkPlaces-Quake";
727                 gamedirname = "";
728                 break;
729         case GAME_HIPNOTIC:
730                 gamename = "Darkplaces-Hipnotic";
731                 gamedirname = "hipnotic";
732                 break;
733         case GAME_ROGUE:
734                 gamename = "Darkplaces-Rogue";
735                 gamedirname = "rogue";
736                 break;
737         case GAME_NEHAHRA:
738                 gamename = "DarkPlaces-Nehahra";
739                 gamedirname = "nehahra";
740                 break;
741         case GAME_NEXUIZ:
742                 gamename = "Nexuiz";
743                 gamedirname = "data";
744                 break;
745         case GAME_TRANSFUSION:
746                 gamename = "Transfusion";
747                 gamedirname = "transfusion";
748                 break;
749         case GAME_GOODVSBAD2:
750                 gamename = "GoodVs.Bad2";
751                 gamedirname = "rts";
752                 break;
753         case GAME_TEU:
754                 gamename = "TheEvilUnleashed";
755                 gamedirname = "baseteu";
756                 break;
757         case GAME_BATTLEMECH:
758                 gamename = "Battlemech";
759                 gamedirname = "base";
760                 break;
761         case GAME_ZYMOTIC:
762                 gamename = "Zymotic";
763                 gamedirname = "data";
764                 break;
765         default:
766                 Sys_Error("COM_InitGameType: unknown gamemode %i\n", gamemode);
767                 break;
768         }
769 }
770
771
772 extern void Mathlib_Init(void);
773 extern void FS_Init (void);
774
775 /*
776 ================
777 COM_Init
778 ================
779 */
780 void COM_Init (void)
781 {
782 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
783         qbyte swaptest[2] = {1,0};
784
785 // set the byte swapping variables in a portable manner
786         if ( *(short *)swaptest == 1)
787         {
788                 BigShort = ShortSwap;
789                 LittleShort = ShortNoSwap;
790                 BigLong = LongSwap;
791                 LittleLong = LongNoSwap;
792                 BigFloat = FloatSwap;
793                 LittleFloat = FloatNoSwap;
794         }
795         else
796         {
797                 BigShort = ShortNoSwap;
798                 LittleShort = ShortSwap;
799                 BigLong = LongNoSwap;
800                 LittleLong = LongSwap;
801                 BigFloat = FloatNoSwap;
802                 LittleFloat = FloatSwap;
803         }
804 #endif
805
806         Cvar_RegisterVariable (&registered);
807         Cvar_RegisterVariable (&cmdline);
808
809         Mathlib_Init();
810
811         FS_Init ();
812         Con_InitLogging();
813         COM_CheckRegistered ();
814
815         COM_InitGameType();
816 }
817
818
819 /*
820 ============
821 va
822
823 does a varargs printf into a temp buffer, so I don't need to have
824 varargs versions of all text functions.
825 FIXME: make this buffer size safe someday
826 ============
827 */
828 char *va(const char *format, ...)
829 {
830         va_list argptr;
831         // LordHavoc: now cycles through 8 buffers to avoid problems in most cases
832         static char string[8][1024], *s;
833         static int stringindex = 0;
834
835         s = string[stringindex];
836         stringindex = (stringindex + 1) & 7;
837         va_start (argptr, format);
838         vsnprintf (s, sizeof (string[0]), format,argptr);
839         va_end (argptr);
840
841         return s;
842 }
843
844
845 //======================================
846 // LordHavoc: added these because they are useful
847
848 void COM_ToLowerString(const char *in, char *out)
849 {
850         while (*in)
851         {
852                 if (*in >= 'A' && *in <= 'Z')
853                         *out++ = *in++ + 'a' - 'A';
854                 else
855                         *out++ = *in++;
856         }
857 }
858
859 void COM_ToUpperString(const char *in, char *out)
860 {
861         while (*in)
862         {
863                 if (*in >= 'a' && *in <= 'z')
864                         *out++ = *in++ + 'A' - 'a';
865                 else
866                         *out++ = *in++;
867         }
868 }
869
870 int COM_StringBeginsWith(const char *s, const char *match)
871 {
872         for (;*s && *match;s++, match++)
873                 if (*s != *match)
874                         return false;
875         return true;
876 }
877
878 // written by Elric, thanks Elric!
879 char *SearchInfostring(const char *infostring, const char *key)
880 {
881         static char value [256];
882         char crt_key [256];
883         size_t value_ind, key_ind;
884         char c;
885
886         if (*infostring++ != '\\')
887                 return NULL;
888
889         value_ind = 0;
890         for (;;)
891         {
892                 key_ind = 0;
893
894                 // Get the key name
895                 for (;;)
896                 {
897                         c = *infostring++;
898
899                         if (c == '\0')
900                                 return NULL;
901                         if (c == '\\' || key_ind == sizeof (crt_key) - 1)
902                         {
903                                 crt_key[key_ind] = '\0';
904                                 break;
905                         }
906
907                         crt_key[key_ind++] = c;
908                 }
909
910                 // If it's the key we are looking for, save it in "value"
911                 if (!strcmp(crt_key, key))
912                 {
913                         for (;;)
914                         {
915                                 c = *infostring++;
916
917                                 if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1)
918                                 {
919                                         value[value_ind] = '\0';
920                                         return value;
921                                 }
922
923                                 value[value_ind++] = c;
924                         }
925                 }
926
927                 // Else, skip the value
928                 for (;;)
929                 {
930                         c = *infostring++;
931
932                         if (c == '\0')
933                                 return NULL;
934                         if (c == '\\')
935                                 break;
936                 }
937         }
938 }
939
940
941 //========================================================
942 // strlcat and strlcpy, from OpenBSD
943
944 /*
945  * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
946  *
947  * Permission to use, copy, modify, and distribute this software for any
948  * purpose with or without fee is hereby granted, provided that the above
949  * copyright notice and this permission notice appear in all copies.
950  *
951  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
952  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
953  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
954  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
955  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
956  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
957  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
958  */
959
960 /*      $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $    */
961 /*      $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $     */
962
963
964 // Most (all?) BSDs already have them
965 #if !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__FreeBSD__)
966
967 size_t
968 strlcat(char *dst, const char *src, size_t siz)
969 {
970         register char *d = dst;
971         register const char *s = src;
972         register size_t n = siz;
973         size_t dlen;
974
975         /* Find the end of dst and adjust bytes left but don't go past end */
976         while (n-- != 0 && *d != '\0')
977                 d++;
978         dlen = d - dst;
979         n = siz - dlen;
980
981         if (n == 0)
982                 return(dlen + strlen(s));
983         while (*s != '\0') {
984                 if (n != 1) {
985                         *d++ = *s;
986                         n--;
987                 }
988                 s++;
989         }
990         *d = '\0';
991
992         return(dlen + (s - src));       /* count does not include NUL */
993 }
994
995 size_t
996 strlcpy(char *dst, const char *src, size_t siz)
997 {
998         register char *d = dst;
999         register const char *s = src;
1000         register size_t n = siz;
1001
1002         /* Copy as many bytes as will fit */
1003         if (n != 0 && --n != 0) {
1004                 do {
1005                         if ((*d++ = *s++) == 0)
1006                                 break;
1007                 } while (--n != 0);
1008         }
1009
1010         /* Not enough room in dst, add NUL and traverse rest of src */
1011         if (n == 0) {
1012                 if (siz != 0)
1013                         *d = '\0';              /* NUL-terminate dst */
1014                 while (*s++)
1015                         ;
1016         }
1017
1018         return(s - src - 1);    /* count does not include NUL */
1019 }
1020
1021 #endif  // #if !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__FreeBSD__)