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