/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "../qcommon/q_shared.h" #include "../qcommon/qcommon.h" #include "sys_local.h" #include #include #include #include #include #include #include #include #include #include #include #include qboolean stdinIsATTY; // Used to determine where to store user-specific files static char homePath[ MAX_OSPATH ] = { 0 }; /* ================== Sys_DefaultHomePath ================== */ char *Sys_DefaultHomePath(void) { char *p; if( !*homePath ) { if( ( p = getenv( "HOME" ) ) != NULL ) { Q_strncpyz( homePath, p, sizeof( homePath ) ); #ifdef MACOS_X Q_strcat( homePath, sizeof( homePath ), "/Library/Application Support/Quake3" ); #else Q_strcat( homePath, sizeof( homePath ), "/.q3a" ); #endif } } return homePath; } /* ================ Sys_Milliseconds ================ */ /* base time in seconds, that's our origin timeval:tv_sec is an int: assuming this wraps every 0x7fffffff - ~68 years since the Epoch (1970) - we're safe till 2038 using unsigned long data type to work right with Sys_XTimeToSysTime */ unsigned long sys_timeBase = 0; /* current time in ms, using sys_timeBase as origin NOTE: sys_timeBase*1000 + curtime -> ms since the Epoch 0x7fffffff ms - ~24 days although timeval:tv_usec is an int, I'm not sure wether it is actually used as an unsigned int (which would affect the wrap period) */ int curtime; int Sys_Milliseconds (void) { struct timeval tp; gettimeofday(&tp, NULL); if (!sys_timeBase) { sys_timeBase = tp.tv_sec; return tp.tv_usec/1000; } curtime = (tp.tv_sec - sys_timeBase)*1000 + tp.tv_usec/1000; return curtime; } #if !id386 /* ================== fastftol ================== */ long fastftol( float f ) { return (long)f; } /* ================== Sys_SnapVector ================== */ void Sys_SnapVector( float *v ) { v[0] = rint(v[0]); v[1] = rint(v[1]); v[2] = rint(v[2]); } #endif /* ================== Sys_RandomBytes ================== */ qboolean Sys_RandomBytes( byte *string, int len ) { FILE *fp; fp = fopen( "/dev/urandom", "r" ); if( !fp ) return qfalse; if( !fread( string, sizeof( byte ), len, fp ) ) { fclose( fp ); return qfalse; } fclose( fp ); return qtrue; } /* ================== Sys_GetCurrentUser ================== */ char *Sys_GetCurrentUser( void ) { struct passwd *p; if ( (p = getpwuid( getuid() )) == NULL ) { return "player"; } return p->pw_name; } /* ================== Sys_GetClipboardData ================== */ char *Sys_GetClipboardData(void) { return NULL; } #define MEM_THRESHOLD 96*1024*1024 /* ================== Sys_LowPhysicalMemory TODO ================== */ qboolean Sys_LowPhysicalMemory( void ) { return qfalse; } /* ================== Sys_Basename ================== */ const char *Sys_Basename( char *path ) { return basename( path ); } /* ================== Sys_Dirname ================== */ const char *Sys_Dirname( char *path ) { return dirname( path ); } /* ================== Sys_Mkdir ================== */ qboolean Sys_Mkdir( const char *path ) { int result = mkdir( path, 0750 ); if( result != 0 ) return errno == EEXIST; return qtrue; } /* ================== Sys_Cwd ================== */ char *Sys_Cwd( void ) { static char cwd[MAX_OSPATH]; char *result = getcwd( cwd, sizeof( cwd ) - 1 ); if( result != cwd ) return NULL; cwd[MAX_OSPATH-1] = 0; return cwd; } /* ============================================================== DIRECTORY SCANNING ============================================================== */ #define MAX_FOUND_FILES 0x1000 /* ================== Sys_ListFilteredFiles ================== */ void Sys_ListFilteredFiles( const char *basedir, char *subdirs, char *filter, char **list, int *numfiles ) { char search[MAX_OSPATH], newsubdirs[MAX_OSPATH]; char filename[MAX_OSPATH]; DIR *fdir; struct dirent *d; struct stat st; if ( *numfiles >= MAX_FOUND_FILES - 1 ) { return; } if (strlen(subdirs)) { Com_sprintf( search, sizeof(search), "%s/%s", basedir, subdirs ); } else { Com_sprintf( search, sizeof(search), "%s", basedir ); } if ((fdir = opendir(search)) == NULL) { return; } while ((d = readdir(fdir)) != NULL) { Com_sprintf(filename, sizeof(filename), "%s/%s", search, d->d_name); if (stat(filename, &st) == -1) continue; if (st.st_mode & S_IFDIR) { if (Q_stricmp(d->d_name, ".") && Q_stricmp(d->d_name, "..")) { if (strlen(subdirs)) { Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s/%s", subdirs, d->d_name); } else { Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s", d->d_name); } Sys_ListFilteredFiles( basedir, newsubdirs, filter, list, numfiles ); } } if ( *numfiles >= MAX_FOUND_FILES - 1 ) { break; } Com_sprintf( filename, sizeof(filename), "%s/%s", subdirs, d->d_name ); if (!Com_FilterPath( filter, filename, qfalse )) continue; list[ *numfiles ] = CopyString( filename ); (*numfiles)++; } closedir(fdir); } /* ================== Sys_ListFiles ================== */ char **Sys_ListFiles( const char *directory, const char *extension, char *filter, int *numfiles, qboolean wantsubs ) { struct dirent *d; DIR *fdir; qboolean dironly = wantsubs; char search[MAX_OSPATH]; int nfiles; char **listCopy; char *list[MAX_FOUND_FILES]; int i; struct stat st; int extLen; if (filter) { nfiles = 0; Sys_ListFilteredFiles( directory, "", filter, list, &nfiles ); list[ nfiles ] = NULL; *numfiles = nfiles; if (!nfiles) return NULL; listCopy = Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) ); for ( i = 0 ; i < nfiles ; i++ ) { listCopy[i] = list[i]; } listCopy[i] = NULL; return listCopy; } if ( !extension) extension = ""; if ( extension[0] == '/' && extension[1] == 0 ) { extension = ""; dironly = qtrue; } extLen = strlen( extension ); // search nfiles = 0; if ((fdir = opendir(directory)) == NULL) { *numfiles = 0; return NULL; } while ((d = readdir(fdir)) != NULL) { Com_sprintf(search, sizeof(search), "%s/%s", directory, d->d_name); if (stat(search, &st) == -1) continue; if ((dironly && !(st.st_mode & S_IFDIR)) || (!dironly && (st.st_mode & S_IFDIR))) continue; if (*extension) { if ( strlen( d->d_name ) < strlen( extension ) || Q_stricmp( d->d_name + strlen( d->d_name ) - strlen( extension ), extension ) ) { continue; // didn't match } } if ( nfiles == MAX_FOUND_FILES - 1 ) break; list[ nfiles ] = CopyString( d->d_name ); nfiles++; } list[ nfiles ] = NULL; closedir(fdir); // return a copy of the list *numfiles = nfiles; if ( !nfiles ) { return NULL; } listCopy = Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) ); for ( i = 0 ; i < nfiles ; i++ ) { listCopy[i] = list[i]; } listCopy[i] = NULL; return listCopy; } /* ================== Sys_FreeFileList ================== */ void Sys_FreeFileList( char **list ) { int i; if ( !list ) { return; } for ( i = 0 ; list[i] ; i++ ) { Z_Free( list[i] ); } Z_Free( list ); } #ifdef MACOS_X /* ================= Sys_StripAppBundle Discovers if passed dir is suffixed with the directory structure of a Mac OS X .app bundle. If it is, the .app directory structure is stripped off the end and the result is returned. If not, dir is returned untouched. ================= */ char *Sys_StripAppBundle( char *dir ) { static char cwd[MAX_OSPATH]; Q_strncpyz(cwd, dir, sizeof(cwd)); if(strcmp(Sys_Basename(cwd), "MacOS")) return dir; Q_strncpyz(cwd, Sys_Dirname(cwd), sizeof(cwd)); if(strcmp(Sys_Basename(cwd), "Contents")) return dir; Q_strncpyz(cwd, Sys_Dirname(cwd), sizeof(cwd)); if(!strstr(Sys_Basename(cwd), ".app")) return dir; Q_strncpyz(cwd, Sys_Dirname(cwd), sizeof(cwd)); return cwd; } #endif // MACOS_X /* ================== Sys_Sleep Block execution for msec or until input is recieved. ================== */ void Sys_Sleep( int msec ) { if( msec == 0 ) return; if( stdinIsATTY ) { fd_set fdset; FD_ZERO(&fdset); FD_SET(STDIN_FILENO, &fdset); if( msec < 0 ) { select(STDIN_FILENO + 1, &fdset, NULL, NULL, NULL); } else { struct timeval timeout; timeout.tv_sec = msec/1000; timeout.tv_usec = (msec%1000)*1000; select(STDIN_FILENO + 1, &fdset, NULL, NULL, &timeout); } } else { // With nothing to select() on, we can't wait indefinitely if( msec < 0 ) msec = 10; usleep( msec * 1000 ); } } /* ============== Sys_ErrorDialog Display an error message ============== */ void Sys_ErrorDialog( const char *error ) { char buffer[ 1024 ]; unsigned int size; int f = -1; const char *homepath = Cvar_VariableString( "fs_homepath" ); const char *gamedir = Cvar_VariableString( "fs_gamedir" ); const char *fileName = "crashlog.txt"; char *ospath = FS_BuildOSPath( homepath, gamedir, fileName ); Sys_Print( va( "%s\n", error ) ); #if defined(MACOS_X) && !DEDICATED /* This function has to be in a separate file, compiled as Objective-C. */ extern void Cocoa_MsgBox( const char *text ); if (!com_dedicated || !com_dedicated->integer) Cocoa_MsgBox(error); #endif /* make sure the write path for the crashlog exists... */ if( FS_CreatePath( ospath ) ) { Com_Printf( "ERROR: couldn't create path '%s' for crash log.\n", ospath ); return; } /* we might be crashing because we maxed out the Quake MAX_FILE_HANDLES, which will come through here, so we don't want to recurse forever by calling FS_FOpenFileWrite()...use the Unix system APIs instead. */ f = open(ospath, O_CREAT | O_TRUNC | O_WRONLY, 0640); if( f == -1 ) { Com_Printf( "ERROR: couldn't open %s\n", fileName ); return; } /* We're crashing, so we don't care much if write() or close() fails. */ while( ( size = CON_LogRead( buffer, sizeof( buffer ) ) ) > 0 ) { if (write( f, buffer, size ) != size) { Com_Printf( "ERROR: couldn't fully write to %s\n", fileName ); break; } } close(f); } /* ============== Sys_GLimpSafeInit Unix specific "safe" GL implementation initialisation ============== */ void Sys_GLimpSafeInit( void ) { // NOP } /* ============== Sys_GLimpInit Unix specific GL implementation initialisation ============== */ void Sys_GLimpInit( void ) { // NOP } /* ============== Sys_PlatformInit Unix specific initialisation ============== */ void Sys_PlatformInit( void ) { const char* term = getenv( "TERM" ); signal( SIGHUP, Sys_SigHandler ); signal( SIGQUIT, Sys_SigHandler ); signal( SIGTRAP, Sys_SigHandler ); signal( SIGIOT, Sys_SigHandler ); signal( SIGBUS, Sys_SigHandler ); stdinIsATTY = isatty( STDIN_FILENO ) && !( term && ( !strcmp( term, "raw" ) || !strcmp( term, "dumb" ) ) ); } /* ============== Sys_SetEnv set/unset environment variables (empty value removes it) ============== */ void Sys_SetEnv(const char *name, const char *value) { if(value && *value) setenv(name, value, 1); else unsetenv(name); }