1 /*
2  * common.c -- misc utility functions used in client and server
3  * $Id: common.c 5530 2016-03-15 14:00:06Z sezero $
4  *
5  * Copyright (C) 1996-1997  Id Software, Inc.
6  * Copyright (C) 2008-2012  O.Sezer <sezero@users.sourceforge.net>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or (at
11  * your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16  *
17  * See the GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22  */
23 
24 #include "quakedef.h"
25 #include "q_ctype.h"
26 #include "filenames.h"
27 
28 
29 int		safemode;
30 
31 
32 /*
33 ============================================================================
34 
35 REPLACEMENT FUNCTIONS
36 
37 ============================================================================
38 */
39 
q_strcasecmp(const char * s1,const char * s2)40 int q_strcasecmp(const char * s1, const char * s2)
41 {
42 	const char * p1 = s1;
43 	const char * p2 = s2;
44 	char c1, c2;
45 
46 	if (p1 == p2)
47 		return 0;
48 
49 	do
50 	{
51 		c1 = q_tolower (*p1++);
52 		c2 = q_tolower (*p2++);
53 		if (c1 == '\0')
54 			break;
55 	} while (c1 == c2);
56 
57 	return (int)(c1 - c2);
58 }
59 
q_strncasecmp(const char * s1,const char * s2,size_t n)60 int q_strncasecmp(const char *s1, const char *s2, size_t n)
61 {
62 	const char * p1 = s1;
63 	const char * p2 = s2;
64 	char c1, c2;
65 
66 	if (p1 == p2 || n == 0)
67 		return 0;
68 
69 	do
70 	{
71 		c1 = q_tolower (*p1++);
72 		c2 = q_tolower (*p2++);
73 		if (c1 == '\0' || c1 != c2)
74 			break;
75 	} while (--n > 0);
76 
77 	return (int)(c1 - c2);
78 }
79 
q_strlwr(char * str)80 char *q_strlwr (char *str)
81 {
82 	char	*c;
83 	c = str;
84 	while (*c)
85 	{
86 		*c = q_tolower(*c);
87 		c++;
88 	}
89 	return str;
90 }
91 
q_strupr(char * str)92 char *q_strupr (char *str)
93 {
94 	char	*c;
95 	c = str;
96 	while (*c)
97 	{
98 		*c = q_toupper(*c);
99 		c++;
100 	}
101 	return str;
102 }
103 
104 #ifdef __DJGPP__ /* override stock DJGPP versions of str[n]icmp by our q_str[n]casecmp: */
105 #ifdef __cplusplus
106 extern "C" {
107 #endif
108 int __stricmp(const char *, const char *) __attribute__((alias("q_strcasecmp")));
109 int stricmp(const char *, const char *) __attribute__((alias("q_strcasecmp")));
110 int strcasecmp(const char *, const char *) __attribute__((alias("q_strcasecmp")));
111 int __strnicmp(const char *, const char *, size_t) __attribute__((alias("q_strncasecmp")));
112 int strnicmp(const char *, const char *, size_t) __attribute__((alias("q_strncasecmp")));
113 int strncasecmp(const char *, const char *, size_t) __attribute__((alias("q_strncasecmp")));
114 char *strlwr(char *) __attribute__((alias("q_strlwr")));
115 char *strupr(char *) __attribute__((alias("q_strlwr")));
116 #ifdef __cplusplus
117 }
118 #endif
119 #endif
120 
qerr_strlcat(const char * caller,int linenum,char * dst,const char * src,size_t size)121 size_t qerr_strlcat (const char *caller, int linenum,
122 		     char *dst, const char *src, size_t size)
123 {
124 	size_t	ret = q_strlcat (dst, src, size);
125 	if (ret >= size)
126 		Sys_Error("%s: %d: string buffer overflow!", caller, linenum);
127 	return ret;
128 }
129 
qerr_strlcpy(const char * caller,int linenum,char * dst,const char * src,size_t size)130 size_t qerr_strlcpy (const char *caller, int linenum,
131 		     char *dst, const char *src, size_t size)
132 {
133 	size_t	ret = q_strlcpy (dst, src, size);
134 	if (ret >= size)
135 		Sys_Error("%s: %d: string buffer overflow!", caller, linenum);
136 	return ret;
137 }
138 
qerr_snprintf(const char * caller,int linenum,char * str,size_t size,const char * format,...)139 int qerr_snprintf (const char *caller, int linenum,
140 		   char *str, size_t size, const char *format, ...)
141 {
142 	int		ret;
143 	va_list		argptr;
144 
145 	va_start (argptr, format);
146 	ret = q_vsnprintf (str, size, format, argptr);
147 	va_end (argptr);
148 
149 	if ((size_t)ret >= size)
150 		Sys_Error("%s: %d: string buffer overflow!", caller, linenum);
151 	return ret;
152 }
153 
154 
155 /*
156 ============================================================================
157 
158 MISC UTILITY FUNCTIONS
159 
160 ============================================================================
161 */
162 
163 /*
164 ============
165 va
166 
167 does a varargs printf into a temp buffer. cycles between
168 4 different static buffers. the number of buffers cycled
169 is defined in VA_NUM_BUFFS.
170 ============
171 */
172 #define	VA_NUM_BUFFS	4
173 #define	VA_BUFFERLEN	1024
174 
get_va_buffer(void)175 static char *get_va_buffer(void)
176 {
177 	static char va_buffers[VA_NUM_BUFFS][VA_BUFFERLEN];
178 	static int buffer_idx = 0;
179 	buffer_idx = (buffer_idx + 1) & (VA_NUM_BUFFS - 1);
180 	return va_buffers[buffer_idx];
181 }
182 
va(const char * format,...)183 char *va (const char *format, ...)
184 {
185 	va_list		argptr;
186 	char		*va_buf;
187 
188 	va_buf = get_va_buffer ();
189 	va_start (argptr, format);
190 	if (q_vsnprintf(va_buf, VA_BUFFERLEN, format, argptr) >= VA_BUFFERLEN)
191 		Con_DPrintf("%s: overflow (string truncated)\n", __thisfunc__);
192 	va_end (argptr);
193 
194 	return va_buf;
195 }
196 
197 
198 /*============================================================================
199   quick'n'dirty string comparison function for use with qsort
200   ============================================================================*/
201 
COM_StrCompare(const void * arg1,const void * arg2)202 int COM_StrCompare (const void *arg1, const void *arg2)
203 {
204 	return q_strcasecmp ( *(char **) arg1, *(char **) arg2);
205 }
206 
207 
208 /*============================================================================
209   FileName Processing utilities
210   ============================================================================*/
211 
212 /*
213 ============
214 COM_SkipPath
215 ============
216 */
COM_SkipPath(const char * pathname)217 const char *COM_SkipPath (const char *pathname)
218 {
219 	const char	*last;
220 
221 	last = pathname;
222 	while (*pathname)
223 	{
224 		if (IS_DIR_SEPARATOR(*pathname))
225 			last = pathname + 1;
226 		pathname++;
227 	}
228 	return last;
229 }
230 
231 /*
232 ============
233 COM_StripExtension
234 ============
235 */
COM_StripExtension(const char * in,char * out,size_t outsize)236 void COM_StripExtension (const char *in, char *out, size_t outsize)
237 {
238 	int	length;
239 
240 	if (!*in)
241 	{
242 		*out = '\0';
243 		return;
244 	}
245 	if (in != out)	/* copy when not in-place editing */
246 		q_strlcpy (out, in, outsize);
247 	length = (int)strlen(out) - 1;
248 	while (length > 0 && out[length] != '.')
249 	{
250 		--length;
251 		if (IS_DIR_SEPARATOR(out[length]))
252 			return;	/* no extension */
253 	}
254 	if (length > 0)
255 		out[length] = '\0';
256 }
257 
258 /*
259 ============
260 COM_FileGetExtension - doesn't return NULL
261 ============
262 */
COM_FileGetExtension(const char * in)263 const char *COM_FileGetExtension (const char *in)
264 {
265 	const char	*src;
266 	size_t		len;
267 
268 	len = strlen(in);
269 	if (len < 2)	/* nothing meaningful */
270 		return "";
271 
272 	src = in + len - 1;
273 	while (src != in && src[-1] != '.')
274 		src--;
275 	if (src == in || FIND_FIRST_DIRSEP(src) != NULL || HAS_DRIVE_SPEC(src))
276 		return "";	/* no extension, or parent directory has a dot */
277 
278 	return src;
279 }
280 
281 /*
282 ============
283 COM_ExtractExtension
284 ============
285 */
COM_ExtractExtension(const char * in,char * out,size_t outsize)286 void COM_ExtractExtension (const char *in, char *out, size_t outsize)
287 {
288 	const char *ext = COM_FileGetExtension (in);
289 	if (! *ext)
290 		*out = '\0';
291 	else
292 		q_strlcpy (out, ext, outsize);
293 }
294 
295 /*
296 ============
297 COM_FileBase
298 take 'somedir/otherdir/filename.ext',
299 write only 'filename' to the output
300 ============
301 */
COM_FileBase(const char * in,char * out,size_t outsize)302 void COM_FileBase (const char *in, char *out, size_t outsize)
303 {
304 	COM_StripExtension (COM_SkipPath(in), out, outsize);
305 }
306 
307 /*
308 ==================
309 COM_DefaultExtension
310 if path doesn't have a .EXT, append extension
311 (extension should include the leading ".")
312 ==================
313 */
314 #if 0 /* can be dangerous */
315 void COM_DefaultExtension (char *path, const char *extension, size_t len)
316 {
317 	char	*src;
318 
319 	if (!*path) return;
320 	src = path + strlen(path) - 1;
321 
322 	while (!IS_DIR_SEPARATOR(*src) && src != path)
323 	{
324 		if (*src == '.')
325 			return; // it has an extension
326 		src--;
327 	}
328 
329 	qerr_strlcat(__thisfunc__, __LINE__, path, extension, len);
330 }
331 #endif
332 
333 /*
334 ==================
335 COM_AddExtension
336 if path extension doesn't match .EXT, append it
337 (extension should include the leading ".")
338 ==================
339 */
COM_AddExtension(char * path,const char * extension,size_t len)340 void COM_AddExtension (char *path, const char *extension, size_t len)
341 {
342 	if (strcmp(COM_FileGetExtension(path), extension + 1) != 0)
343 		qerr_strlcat(__thisfunc__, __LINE__, path, extension, len);
344 }
345 
346 
347 /*
348 ============================================================================
349 
350 STRING PARSING FUNCTIONS
351 
352 ============================================================================
353 */
354 
355 char		com_token[1024];
356 
357 /*
358 ==============
359 COM_Parse
360 
361 Parse a token out of a string
362 ==============
363 */
COM_Parse(const char * data)364 const char *COM_Parse (const char *data)
365 {
366 	int		c;
367 	int		len;
368 
369 	len = 0;
370 	com_token[0] = 0;
371 
372 	if (!data)
373 		return NULL;
374 
375 // skip whitespace
376 skipwhite:
377 	while ((c = *data) <= ' ')
378 	{
379 		if (c == 0)
380 			return NULL;	// end of file
381 		data++;
382 	}
383 
384 // skip // comments
385 	if (c == '/' && data[1] == '/')
386 	{
387 		while (*data && *data != '\n')
388 			data++;
389 		goto skipwhite;
390 	}
391 
392 // skip /*..*/ comments
393 	if (c == '/' && data[1] == '*')
394 	{
395 		data += 2;
396 		while (*data && !(*data == '*' && data[1] == '/'))
397 			data++;
398 		if (*data)
399 			data += 2;
400 		goto skipwhite;
401 	}
402 
403 // handle quoted strings specially
404 	if (c == '\"')
405 	{
406 		data++;
407 		while (1)
408 		{
409 			if ((c = *data) != 0)
410 				++data;
411 			if (c == '\"' || !c)
412 			{
413 				com_token[len] = 0;
414 				return data;
415 			}
416 			com_token[len] = c;
417 			len++;
418 		}
419 	}
420 
421 #if 0
422 // parse single characters
423 	if (c == '{' || c == '}' || c == '(' || c == ')' || c == '\'' || c == ':')
424 	{
425 		com_token[len] = c;
426 		len++;
427 		com_token[len] = 0;
428 		return data+1;
429 	}
430 #endif
431 
432 // parse a regular word
433 	do
434 	{
435 		com_token[len] = c;
436 		data++;
437 		len++;
438 		c = *data;
439 #if 0
440 		if (c == '{' || c == '}' || c == '(' || c == ')' || c == '\'' || c == ':')
441 			break;
442 #endif
443 	} while (c > 32);
444 
445 	com_token[len] = 0;
446 	return data;
447 }
448 
449 
450 /*
451 ============================================================================
452 
453 COMMAND LINE PROCESSING FUNCTIONS
454 
455 ============================================================================
456 */
457 
458 /*
459 ================
460 COM_CheckParm
461 
462 Returns the position (1 to argc-1) in the program's argument list
463 where the given parameter apears, or 0 if not present
464 ================
465 */
COM_CheckParm(const char * parm)466 int COM_CheckParm (const char *parm)
467 {
468 	int		i;
469 
470 	for (i = 1; i < com_argc; i++)
471 	{
472 		if (!com_argv[i])
473 			continue;		// NEXTSTEP sometimes clears appkit vars.
474 		if (!strcmp (parm,com_argv[i]))
475 			return i;
476 	}
477 
478 	return 0;
479 }
480 
COM_Cmdline_f(void)481 static void COM_Cmdline_f (void)
482 {
483 	int			i;
484 
485 	Con_Printf ("cmdline was:");
486 	for (i = 0; (i < MAX_NUM_ARGVS) && (i < com_argc); i++)
487 	{
488 		if (com_argv[i])
489 			Con_Printf (" %s", com_argv[i]);
490 	}
491 	Con_Printf ("\n");
492 }
493 
494 /*
495 ============================================================================
496 
497 INIT, ETC..
498 
499 ============================================================================
500 */
501 
COM_ValidateByteorder(void)502 void COM_ValidateByteorder (void)
503 {
504 	const char	*endianism[] = { "BE", "LE", "PDP", "Unknown" };
505 	const char	*tmp;
506 
507 	ByteOrder_Init ();
508 	switch (host_byteorder)
509 	{
510 	case BIG_ENDIAN:
511 		tmp = endianism[0];
512 		break;
513 	case LITTLE_ENDIAN:
514 		tmp = endianism[1];
515 		break;
516 	case PDP_ENDIAN:
517 		tmp = endianism[2];
518 		host_byteorder = -1;	/* not supported */
519 		break;
520 	default:
521 		tmp = endianism[3];
522 		break;
523 	}
524 	if (host_byteorder < 0)
525 		Sys_Error ("%s: Unsupported byte order [%s]", __thisfunc__, tmp);
526 	Sys_Printf("Detected byte order: %s\n", tmp);
527 #if !ENDIAN_RUNTIME_DETECT
528 	if (host_byteorder != BYTE_ORDER)
529 	{
530 		const char	*tmp2;
531 
532 		switch (BYTE_ORDER)
533 		{
534 		case BIG_ENDIAN:
535 			tmp2 = endianism[0];
536 			break;
537 		case LITTLE_ENDIAN:
538 			tmp2 = endianism[1];
539 			break;
540 		case PDP_ENDIAN:
541 			tmp2 = endianism[2];
542 			break;
543 		default:
544 			tmp2 = endianism[3];
545 			break;
546 		}
547 		Sys_Error ("Detected byte order %s doesn't match compiled %s order!", tmp, tmp2);
548 	}
549 #endif	/* ENDIAN_RUNTIME_DETECT */
550 }
551 
552 /*
553 ================
554 COM_Init
555 ================
556 */
COM_Init(void)557 void COM_Init (void)
558 {
559 	Cmd_AddCommand ("cmdline", COM_Cmdline_f);
560 
561 	safemode = COM_CheckParm ("-safe");
562 }
563 
564