1 /*
2 ===========================================================================
3 Copyright (C) 2005 - 2015, ioquake3 contributors
4 Copyright (C) 2013 - 2015, OpenJK contributors
5 
6 This file is part of the OpenJK source code.
7 
8 OpenJK is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License version 2 as
10 published by the Free Software Foundation.
11 
12 This program is distributed in the hope that it will be useful,
13 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 this program; if not, see <http://www.gnu.org/licenses/>.
19 ===========================================================================
20 */
21 
22 #include <dirent.h>
23 #include <stdarg.h>
24 #include <stdlib.h>
25 #include <sys/time.h>
26 #include <errno.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <pwd.h>
32 #include <libgen.h>
33 #include <sched.h>
34 #include <signal.h>
35 
36 #include "qcommon/qcommon.h"
37 #include "qcommon/q_shared.h"
38 #include "sys_local.h"
39 
40 qboolean stdinIsATTY = qfalse;
41 
42 // Used to determine where to store user-specific files
43 static char homePath[ MAX_OSPATH ] = { 0 };
44 
Sys_PlatformInit(void)45 void Sys_PlatformInit( void )
46 {
47 	const char* term = getenv( "TERM" );
48 
49 	signal( SIGHUP, Sys_SigHandler );
50 	signal( SIGQUIT, Sys_SigHandler );
51 	signal( SIGTRAP, Sys_SigHandler );
52 	signal( SIGABRT, Sys_SigHandler );
53 	signal( SIGBUS, Sys_SigHandler );
54 
55 	if (isatty( STDIN_FILENO ) && !( term && ( !strcmp( term, "raw" ) || !strcmp( term, "dumb" ) ) ))
56 		stdinIsATTY = qtrue;
57 	else
58 		stdinIsATTY = qfalse;
59 }
60 
Sys_PlatformExit(void)61 void Sys_PlatformExit( void )
62 {
63 }
64 
65 /*
66 ================
67 Sys_Milliseconds
68 ================
69 */
70 /* base time in seconds, that's our origin
71    timeval:tv_sec is an int:
72    assuming this wraps every 0x7fffffff - ~68 years since the Epoch (1970) - we're safe till 2038 */
73 unsigned long sys_timeBase = 0;
74 /* current time in ms, using sys_timeBase as origin
75    NOTE: sys_timeBase*1000 + curtime -> ms since the Epoch
76      0x7fffffff ms - ~24 days
77    although timeval:tv_usec is an int, I'm not sure wether it is actually used as an unsigned int
78      (which would affect the wrap period) */
79 int curtime;
Sys_Milliseconds(bool baseTime)80 int Sys_Milliseconds (bool baseTime)
81 {
82 	struct timeval tp;
83 
84 	gettimeofday(&tp, NULL);
85 
86 	if (!sys_timeBase)
87 	{
88 		sys_timeBase = tp.tv_sec;
89 		return tp.tv_usec/1000;
90 	}
91 
92 	curtime = (tp.tv_sec - sys_timeBase)*1000 + tp.tv_usec/1000;
93 
94     static int sys_timeBase = curtime;
95 	if (!baseTime)
96 	{
97 		curtime -= sys_timeBase;
98 	}
99 
100 	return curtime;
101 }
102 
Sys_Milliseconds2(void)103 int Sys_Milliseconds2( void )
104 {
105     return Sys_Milliseconds(false);
106 }
107 
108 /*
109 ==================
110 Sys_RandomBytes
111 ==================
112 */
Sys_RandomBytes(byte * string,int len)113 bool Sys_RandomBytes( byte *string, int len )
114 {
115 	FILE *fp;
116 
117 	fp = fopen( "/dev/urandom", "r" );
118 	if( !fp )
119 		return false;
120 
121 	setvbuf( fp, NULL, _IONBF, 0 ); // don't buffer reads from /dev/urandom
122 
123 	if( !fread( string, sizeof( byte ), len, fp ) )
124 	{
125 		fclose( fp );
126 		return false;
127 	}
128 
129 	fclose( fp );
130 	return true;
131 }
132 
133 /*
134  ==================
135  Sys_GetCurrentUser
136  ==================
137  */
Sys_GetCurrentUser(void)138 char *Sys_GetCurrentUser( void )
139 {
140 	struct passwd *p;
141 
142 	if ( (p = getpwuid( getuid() )) == NULL ) {
143 		return "player";
144 	}
145 	return p->pw_name;
146 }
147 
148 #define MEM_THRESHOLD 96*1024*1024
149 
150 /*
151 ==================
152 Sys_LowPhysicalMemory
153 
154 TODO
155 ==================
156 */
Sys_LowPhysicalMemory(void)157 qboolean Sys_LowPhysicalMemory( void )
158 {
159 	return qfalse;
160 }
161 
162 /*
163 ==================
164 Sys_Basename
165 ==================
166 */
Sys_Basename(char * path)167 const char *Sys_Basename( char *path )
168 {
169 	return basename( path );
170 }
171 
172 /*
173 ==================
174 Sys_Dirname
175 ==================
176 */
Sys_Dirname(char * path)177 const char *Sys_Dirname( char *path )
178 {
179 	return dirname( path );
180 }
181 
182 /*
183 ==============================================================
184 
185 DIRECTORY SCANNING
186 
187 ==============================================================
188 */
189 
190 #define MAX_FOUND_FILES 0x1000
191 
192 /*
193 ==================
194 Sys_ListFiles
195 ==================
196 */
Sys_ListFilteredFiles(const char * basedir,char * subdirs,char * filter,char ** list,int * numfiles)197 void Sys_ListFilteredFiles( const char *basedir, char *subdirs, char *filter, char **list, int *numfiles ) {
198 	char		search[MAX_OSPATH], newsubdirs[MAX_OSPATH];
199 	char		filename[MAX_OSPATH];
200 	DIR			*fdir;
201 	struct dirent *d;
202 	struct stat st;
203 
204 	if ( *numfiles >= MAX_FOUND_FILES - 1 ) {
205 		return;
206 	}
207 
208 	if (strlen(subdirs)) {
209 		Com_sprintf( search, sizeof(search), "%s/%s", basedir, subdirs );
210 	}
211 	else {
212 		Com_sprintf( search, sizeof(search), "%s", basedir );
213 	}
214 
215 	if ((fdir = opendir(search)) == NULL) {
216 		return;
217 	}
218 
219 	while ((d = readdir(fdir)) != NULL) {
220 		Com_sprintf(filename, sizeof(filename), "%s/%s", search, d->d_name);
221 		if (stat(filename, &st) == -1)
222 			continue;
223 
224 		if (st.st_mode & S_IFDIR) {
225 			if (Q_stricmp(d->d_name, ".") && Q_stricmp(d->d_name, "..")) {
226 				if (strlen(subdirs)) {
227 					Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s/%s", subdirs, d->d_name);
228 				}
229 				else {
230 					Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s", d->d_name);
231 				}
232 				Sys_ListFilteredFiles( basedir, newsubdirs, filter, list, numfiles );
233 			}
234 		}
235 		if ( *numfiles >= MAX_FOUND_FILES - 1 ) {
236 			break;
237 		}
238 		Com_sprintf( filename, sizeof(filename), "%s/%s", subdirs, d->d_name );
239 		if (!Com_FilterPath( filter, filename, qfalse ))
240 			continue;
241 		list[ *numfiles ] = CopyString( filename );
242 		(*numfiles)++;
243 	}
244 
245 	closedir(fdir);
246 }
247 
Sys_ListFiles(const char * directory,const char * extension,char * filter,int * numfiles,qboolean wantsubs)248 char **Sys_ListFiles( const char *directory, const char *extension, char *filter, int *numfiles, qboolean wantsubs )
249 {
250 	struct dirent *d;
251 	DIR		*fdir;
252 	qboolean dironly = wantsubs;
253 	char		search[MAX_OSPATH];
254 	int			nfiles;
255 	char		**listCopy;
256 	char		*list[MAX_FOUND_FILES];
257 	int			i;
258 	struct stat st;
259 
260 	if (filter) {
261 
262 		nfiles = 0;
263 		Sys_ListFilteredFiles( directory, "", filter, list, &nfiles );
264 
265 		list[ nfiles ] = 0;
266 		*numfiles = nfiles;
267 
268 		if (!nfiles)
269 			return NULL;
270 
271 		listCopy = (char **)Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ), TAG_LISTFILES, qfalse );
272 		for ( i = 0 ; i < nfiles ; i++ ) {
273 			listCopy[i] = list[i];
274 		}
275 		listCopy[i] = NULL;
276 
277 		return listCopy;
278 	}
279 
280 	if ( !extension)
281 		extension = "";
282 
283 	if ( extension[0] == '/' && extension[1] == 0 ) {
284 		extension = "";
285 		dironly = qtrue;
286 	}
287 
288 	size_t extLen = strlen( extension );
289 
290 	// search
291 	nfiles = 0;
292 
293 	if ((fdir = opendir(directory)) == NULL) {
294 		*numfiles = 0;
295 		return NULL;
296 	}
297 
298 	while ((d = readdir(fdir)) != NULL) {
299 		Com_sprintf(search, sizeof(search), "%s/%s", directory, d->d_name);
300 		if (stat(search, &st) == -1)
301 			continue;
302 		if ((dironly && !(st.st_mode & S_IFDIR)) ||
303 			(!dironly && (st.st_mode & S_IFDIR)))
304 			continue;
305 
306 		if (*extension) {
307 			if ( strlen( d->d_name ) < extLen ||
308 				Q_stricmp(
309 					d->d_name + strlen( d->d_name ) - extLen,
310 					extension ) ) {
311 				continue; // didn't match
312 			}
313 		}
314 
315 		if ( nfiles == MAX_FOUND_FILES - 1 )
316 			break;
317 		list[ nfiles ] = CopyString( d->d_name );
318 		nfiles++;
319 	}
320 
321 	list[ nfiles ] = 0;
322 
323 	closedir(fdir);
324 
325 	// return a copy of the list
326 	*numfiles = nfiles;
327 
328 	if ( !nfiles ) {
329 		return NULL;
330 	}
331 
332 	listCopy = (char **)Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ), TAG_LISTFILES, qfalse );
333 	for ( i = 0 ; i < nfiles ; i++ ) {
334 		listCopy[i] = list[i];
335 	}
336 	listCopy[i] = NULL;
337 
338 	return listCopy;
339 }
340 
Sys_FreeFileList(char ** fileList)341 void	Sys_FreeFileList( char **fileList ) {
342 	int		i;
343 
344 	if ( !fileList ) {
345 		return;
346 	}
347 
348 	for ( i = 0 ; fileList[i] ; i++ ) {
349 		Z_Free( fileList[i] );
350 	}
351 
352 	Z_Free( fileList );
353 }
354 
355 /*
356 ==================
357 Sys_Sleep
358 
359 Block execution for msec or until input is recieved.
360 ==================
361 */
Sys_Sleep(int msec)362 void Sys_Sleep( int msec )
363 {
364 	if( msec == 0 )
365 		return;
366 
367 	if( stdinIsATTY )
368 	{
369 		fd_set fdset;
370 
371 		FD_ZERO(&fdset);
372 		FD_SET(STDIN_FILENO, &fdset);
373 		if( msec < 0 )
374 		{
375 			select(STDIN_FILENO + 1, &fdset, NULL, NULL, NULL);
376 		}
377 		else
378 		{
379 			struct timeval timeout;
380 
381 			timeout.tv_sec = msec/1000;
382 			timeout.tv_usec = (msec%1000)*1000;
383 			select(STDIN_FILENO + 1, &fdset, NULL, NULL, &timeout);
384 		}
385 	}
386 	else
387 	{
388 		// With nothing to select() on, we can't wait indefinitely
389 		if( msec < 0 )
390 			msec = 10;
391 
392 		usleep( msec * 1000 );
393 	}
394 }
395 
396 /*
397 ==================
398 Sys_Mkdir
399 ==================
400 */
Sys_Mkdir(const char * path)401 qboolean Sys_Mkdir( const char *path )
402 {
403 	int result = mkdir( path, 0750 );
404 
405 	if( result != 0 )
406 		return (qboolean)(errno == EEXIST);
407 
408 	return qtrue;
409 }
410 
Sys_Cwd(void)411 char *Sys_Cwd( void )
412 {
413 	static char cwd[MAX_OSPATH];
414 
415 	if ( getcwd( cwd, sizeof( cwd ) - 1 ) == NULL )
416 		cwd[0] = '\0';
417 	else
418 		cwd[MAX_OSPATH-1] = '\0';
419 
420 	return cwd;
421 }
422 
423 /* Resolves path names and determines if they are the same */
424 /* For use with full OS paths not quake paths */
425 /* Returns true if resulting paths are valid and the same, otherwise false */
Sys_PathCmp(const char * path1,const char * path2)426 bool Sys_PathCmp( const char *path1, const char *path2 )
427 {
428 	char *r1, *r2;
429 
430 	r1 = realpath(path1, NULL);
431 	r2 = realpath(path2, NULL);
432 
433 	if(r1 && r2 && !Q_stricmp(r1, r2))
434 	{
435 		free(r1);
436 		free(r2);
437 		return true;
438 	}
439 
440 	free(r1);
441 	free(r2);
442 	return false;
443 }
444 
445 /*
446 ==================
447 Sys_DefaultHomePath
448 ==================
449 */
450 #ifdef MACOS_X
Sys_DefaultHomePath(void)451 char *Sys_DefaultHomePath(void)
452 {
453 	char *p;
454 
455 	if ( !homePath[0] )
456 	{
457 		if ( (p = getenv( "HOME" )) != NULL )
458 		{
459 			Com_sprintf( homePath, sizeof( homePath ), "%s%c", p, PATH_SEP );
460 			Q_strcat( homePath, sizeof( homePath ), "Library/Application Support/" );
461 
462 			if ( com_homepath && com_homepath->string[0] )
463 				Q_strcat( homePath, sizeof( homePath ), com_homepath->string );
464 			else
465 				Q_strcat( homePath, sizeof( homePath ), HOMEPATH_NAME_MACOSX );
466 		}
467 	}
468 
469 	return homePath;
470 }
471 #else
Sys_DefaultHomePath(void)472 char *Sys_DefaultHomePath(void)
473 {
474 	char *p;
475 
476 	if ( !homePath[0] )
477 	{
478 		if ( (p = getenv( "XDG_DATA_HOME" )) != NULL )
479 		{
480 			Com_sprintf( homePath, sizeof( homePath ), "%s%c", p, PATH_SEP );
481 			if ( com_homepath && com_homepath->string[0] )
482 				Q_strcat( homePath, sizeof( homePath ), com_homepath->string );
483 			else
484 				Q_strcat( homePath, sizeof( homePath ), HOMEPATH_NAME_UNIX );
485 
486 			return homePath;
487 		}
488 
489 		if ( (p = getenv( "HOME" )) != NULL )
490 		{
491 			Com_sprintf( homePath, sizeof( homePath ), "%s%c.local%cshare%c", p, PATH_SEP, PATH_SEP, PATH_SEP );
492 			if ( com_homepath && com_homepath->string[0] )
493 				Q_strcat( homePath, sizeof( homePath ), com_homepath->string );
494 			else
495 				Q_strcat( homePath, sizeof( homePath ), HOMEPATH_NAME_UNIX );
496 
497 			return homePath;
498 		}
499 	}
500 
501 	return homePath;
502 }
503 #endif
504 
Sys_SetProcessorAffinity(void)505 void Sys_SetProcessorAffinity( void ) {
506 #if defined(__linux__)
507 	uint32_t cores;
508 
509 	if ( sscanf( com_affinity->string, "%X", &cores ) != 1 )
510 		cores = 1; // set to first core only
511 
512 	const long numCores = sysconf( _SC_NPROCESSORS_ONLN );
513 	if ( !cores )
514 		cores = (1 << numCores) - 1; // use all cores
515 
516 	cpu_set_t set;
517 	CPU_ZERO( &set );
518 	for ( int i = 0; i < numCores; i++ ) {
519 		if ( cores & (1<<i) ) {
520 			CPU_SET( i, &set );
521 		}
522 	}
523 
524 	sched_setaffinity( 0, sizeof( set ), &set );
525 #elif defined(MACOS_X)
526 	//TODO: Apple's APIs for this are weird but exist on a per-thread level. Good enough for us.
527 #endif
528 }
529 
Sys_UnpackDLL(const char * name)530 UnpackDLLResult Sys_UnpackDLL(const char *name)
531 {
532 	return UnpackDLLResult();
533 }
534 
Sys_DLLNeedsUnpacking()535 bool Sys_DLLNeedsUnpacking()
536 {
537 	return false;
538 }
539 
540 
541 /*
542 =================
543 Sys_AnsiColorPrint
544 
545 Transform Q3 colour codes to ANSI escape sequences
546 =================
547 */
Sys_AnsiColorPrint(const char * msg)548 void Sys_AnsiColorPrint( const char *msg )
549 {
550 	static char buffer[MAXPRINTMSG];
551 	int         length = 0;
552 	static int  q3ToAnsi[Q_COLOR_BITS+1] =
553 	{
554 		30, // COLOR_BLACK
555 		31, // COLOR_RED
556 		32, // COLOR_GREEN
557 		33, // COLOR_YELLOW
558 		34, // COLOR_BLUE
559 		36, // COLOR_CYAN
560 		35, // COLOR_MAGENTA
561 		0,  // COLOR_WHITE
562 		33, // COLOR_ORANGE // FIXME
563 		30, // COLOR_GREY
564 	};
565 
566 	while ( *msg )
567 	{
568 		if ( Q_IsColorStringExt( msg ) || *msg == '\n' )
569 		{
570 			// First empty the buffer
571 			if ( length > 0 )
572 			{
573 				buffer[length] = '\0';
574 				fputs( buffer, stderr );
575 				length = 0;
576 			}
577 
578 			if ( *msg == '\n' )
579 			{
580 				// Issue a reset and then the newline
581 				fputs( "\033[0m\n", stderr );
582 				msg++;
583 			}
584 			else
585 			{
586 				// Print the color code
587 				Com_sprintf( buffer, sizeof( buffer ), "\033[%dm",
588 					q3ToAnsi[ColorIndex( *(msg + 1) )] );
589 				fputs( buffer, stderr );
590 				msg += 2;
591 			}
592 		}
593 		else
594 		{
595 			if ( length >= MAXPRINTMSG - 1 )
596 				break;
597 
598 			buffer[length] = *msg;
599 			length++;
600 			msg++;
601 		}
602 	}
603 
604 	// Empty anything still left in the buffer
605 	if ( length > 0 )
606 	{
607 		buffer[length] = '\0';
608 		fputs( buffer, stderr );
609 	}
610 }
611