From 425decdf7e9284d15aa726e3ae96b9942fb0e3ea Mon Sep 17 00:00:00 2001 From: IronClawTrem Date: Sun, 16 Feb 2020 03:40:06 +0000 Subject: create tremded branch --- src/sys/sys_unix.cpp | 1006 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1006 insertions(+) create mode 100644 src/sys/sys_unix.cpp (limited to 'src/sys/sys_unix.cpp') diff --git a/src/sys/sys_unix.cpp b/src/sys/sys_unix.cpp new file mode 100644 index 0000000..e55655c --- /dev/null +++ b/src/sys/sys_unix.cpp @@ -0,0 +1,1006 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2013 Darklegion Development +Copyright (C) 2015-2019 GrangerHub + +This file is part of Tremulous. + +Tremulous 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 3 of the License, +or (at your option) any later version. + +Tremulous 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 Tremulous; if not, see + +=========================================================================== +*/ + +#include "qcommon/cvar.h" +#include "qcommon/files.h" +#include "qcommon/q_shared.h" +#include "qcommon/qcommon.h" +#include "dialog.h" +#include "sys_local.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +bool 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 && com_homepath != NULL ) + { + if( ( p = getenv( "HOME" ) ) != NULL ) + { + Com_sprintf(homePath, sizeof(homePath), "%s%c", p, PATH_SEP); +#ifdef __APPLE__ + Q_strcat(homePath, sizeof(homePath), + "Library/Application Support/"); + + if(com_homepath->string[0]) + Q_strcat(homePath, sizeof(homePath), com_homepath->string); + else + Q_strcat(homePath, sizeof(homePath), HOMEPATH_NAME_MACOSX); +#else + if(com_homepath->string[0]) + Q_strcat(homePath, sizeof(homePath), com_homepath->string); + else + Q_strcat(homePath, sizeof(homePath), HOMEPATH_NAME_UNIX); +#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 */ +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; +} + +/* +================== +Sys_RandomBytes +================== +*/ +bool Sys_RandomBytes( byte *string, int len ) +{ + FILE *fp; + + fp = fopen( "/dev/urandom", "r" ); + if( !fp ) + return false; + + setvbuf( fp, NULL, _IONBF, 0 ); // don't buffer reads from /dev/urandom + + if( fread( string, sizeof( byte ), len, fp ) != len ) + { + fclose( fp ); + return false; + } + + fclose( fp ); + return true; +} + +/* +================== +Sys_GetCurrentUser +================== +*/ +const char *Sys_GetCurrentUser( void ) +{ + struct passwd *p; + + if ( (p = getpwuid( getuid() )) == NULL ) { + return "player"; + } + return p->pw_name; +} + +/* +================== +Sys_CryptoRandomBytes +================== +*/ +void Sys_CryptoRandomBytes( byte *string, int len ) +{ + if ( !Sys_RandomBytes( string, len ) ) + Com_Error( ERR_FATAL, "Sys_CryptoRandomBytes: error reading /dev/urandom" ); +} + +#define MEM_THRESHOLD 96*1024*1024 + +/* +================== +Sys_LowPhysicalMemory + +TODO +================== +*/ +bool Sys_LowPhysicalMemory( void ) +{ + return false; +} + +/* +================== +Sys_Basename +================== +*/ +const char *Sys_Basename( char *path ) +{ + return basename( path ); +} + +/* +================== +Sys_Dirname +================== +*/ +const char *Sys_Dirname( char* path ) +{ + return dirname( path ); +} + +/* +============== +Sys_FOpen +============== +*/ +FILE *Sys_FOpen( const char *ospath, const char *mode ) { + struct stat buf; + + // check if path exists and is a directory + if ( !stat( ospath, &buf ) && S_ISDIR( buf.st_mode ) ) + return NULL; + + return fopen( ospath, mode ); +} + +/* +================== +Sys_Mkdir +================== +*/ +bool Sys_Mkdir( const char *path ) +{ + int result = mkdir( path, 0750 ); + + if( result != 0 ) + return (bool)(errno == EEXIST); + + return true; +} + +/* +================== +Sys_Mkfifo +================== +*/ +FILE *Sys_Mkfifo( const char *ospath ) +{ + FILE *fifo; + int result; + int fn; + struct stat buf; + + // if file already exists AND is a pipefile, remove it + if( !stat( ospath, &buf ) && S_ISFIFO( buf.st_mode ) ) + FS_Remove( ospath ); + + result = mkfifo( ospath, 0600 ); + if( result != 0 ) + return NULL; + + fifo = fopen( ospath, "w+" ); + if( fifo ) + { + fn = fileno( fifo ); + fcntl( fn, F_SETFL, O_NONBLOCK ); + } + + return fifo; +} + +/* +============== +Sys_OpenWithDefault + +Opens a path with the default application +============== +*/ +bool Sys_OpenWithDefault( const char *path ) +{ + int status; + int exitNum; + pid_t pid; + + Com_Printf( S_COLOR_WHITE "Sys_OpenWithDefault: opening %s .....\n", + path ); + + // attempt to start child process + pid = fork(); + + if( pid < 0 ) + { + // failed to start the child process + Com_Printf( S_COLOR_RED "Sys_OpenWithDefault: %s\n" S_COLOR_WHITE, + strerror( exitNum ) ); + return false; + } + else if ( pid == 0 ) + { + //child proccess + char *argv[3]; + char tempPath[MAX_OSPATH]; + char openCmd[MAX_OSPATH]; + + ::memset( tempPath, 0, sizeof( tempPath ) ); + ::memset( openCmd, 0, sizeof( openCmd ) ); + + Q_strcat( tempPath, sizeof(tempPath), path ); + + argv[1] = tempPath; + argv[2] = NULL; + +#ifdef __APPLE__ + Q_strcat( openCmd, sizeof(openCmd), "open"); +#else + Q_strcat( openCmd, sizeof(openCmd), "xdg-open"); +#endif + + argv[0] = openCmd; + + // attempt to open the path + if( execvp( argv[0], argv ) < 0 ) + { + //failure + exit( errno ); + } + + //success + exit(0); + } + + wait( &status ); + exitNum = WEXITSTATUS( status ); + + if( !exitNum ) + { + return true; + } + else + { + Com_Printf( S_COLOR_RED "Sys_OpenWithDefault: %s\n" S_COLOR_WHITE, strerror( exitNum ) ); + return false; + } +} + +/* +================== +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, const char *subdirs, + const 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, false )) + continue; + list[ *numfiles ] = CopyString( filename ); + (*numfiles)++; + } + + closedir(fdir); +} + +/* +================== +Sys_ListFiles +================== +*/ +char **Sys_ListFiles( const char *directory, const char *extension, + const char *filter, int *numfiles, bool wantsubs ) +{ + struct dirent *d; + DIR *fdir; + bool 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 = (char**)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 = true; + } + + 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 ) < extLen || + Q_stricmp( + d->d_name + strlen( d->d_name ) - extLen, + 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 = (char**)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 ); +} + +/* +================== +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_game" ); + const char *fileName = "crashlog.txt"; + char *dirpath = FS_BuildOSPath( homepath, gamedir, ""); + char *ospath = FS_BuildOSPath( homepath, gamedir, fileName ); + + Sys_Print( va( "%s\n", error ) ); + +#ifndef DEDICATED + Sys_Dialog( DT_ERROR, va( "%s. See \"%s\" for details.", error, ospath ), "Error" ); +#endif + + // Make sure the write path for the crashlog exists... + + if(!Sys_Mkdir(homepath)) + { + Com_Printf("ERROR: couldn't create path '%s' for crash log.\n", homepath); + return; + } + + if(!Sys_Mkdir(dirpath)) + { + Com_Printf("ERROR: couldn't create path '%s' for crash log.\n", dirpath); + 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 ); +} + +#ifndef __APPLE__ +static char execBuffer[ 1024 ]; +static char *execBufferPointer; +static char *execArgv[ 16 ]; +static int execArgc; + +/* +============== +Sys_ClearExecBuffer +============== +*/ +static void Sys_ClearExecBuffer( void ) +{ + execBufferPointer = execBuffer; + ::memset( execArgv, 0, sizeof( execArgv ) ); + execArgc = 0; +} + +/* +============== +Sys_AppendToExecBuffer +============== +*/ +static void Sys_AppendToExecBuffer( const char *text ) +{ + size_t size = sizeof( execBuffer ) - ( execBufferPointer - execBuffer ); + int length = strlen( text ) + 1; + + if( length > size || execArgc >= ARRAY_LEN( execArgv ) ) + return; + + Q_strncpyz( execBufferPointer, text, size ); + execArgv[ execArgc++ ] = execBufferPointer; + + execBufferPointer += length; +} + +/* +============== +Sys_Exec +============== +*/ +static int Sys_Exec( void ) +{ + pid_t pid = fork( ); + + if( pid < 0 ) + return -1; + + if( pid ) + { + // Parent + int exitCode; + + wait( &exitCode ); + + return WEXITSTATUS( exitCode ); + } + else + { + // Child + execvp( execArgv[ 0 ], execArgv ); + + // Failed to execute + exit( -1 ); + + return -1; + } +} + +/* +============== +Sys_ZenityCommand +============== +*/ +static void Sys_ZenityCommand( dialogType_t type, const char *message, const char *title ) +{ + Sys_ClearExecBuffer( ); + Sys_AppendToExecBuffer( "zenity" ); + + switch( type ) + { + default: + case DT_INFO: Sys_AppendToExecBuffer( "--info" ); break; + case DT_WARNING: Sys_AppendToExecBuffer( "--warning" ); break; + case DT_ERROR: Sys_AppendToExecBuffer( "--error" ); break; + case DT_YES_NO: + Sys_AppendToExecBuffer( "--question" ); + Sys_AppendToExecBuffer( "--ok-label=Yes" ); + Sys_AppendToExecBuffer( "--cancel-label=No" ); + break; + + case DT_OK_CANCEL: + Sys_AppendToExecBuffer( "--question" ); + Sys_AppendToExecBuffer( "--ok-label=OK" ); + Sys_AppendToExecBuffer( "--cancel-label=Cancel" ); + break; + } + + Sys_AppendToExecBuffer( va( "--text=%s", message ) ); + Sys_AppendToExecBuffer( va( "--title=%s", title ) ); +} + +/* +============== +Sys_KdialogCommand +============== +*/ +static void Sys_KdialogCommand( dialogType_t type, const char *message, const char *title ) +{ + Sys_ClearExecBuffer( ); + Sys_AppendToExecBuffer( "kdialog" ); + + switch( type ) + { + default: + case DT_INFO: Sys_AppendToExecBuffer( "--msgbox" ); break; + case DT_WARNING: Sys_AppendToExecBuffer( "--sorry" ); break; + case DT_ERROR: Sys_AppendToExecBuffer( "--error" ); break; + case DT_YES_NO: Sys_AppendToExecBuffer( "--warningyesno" ); break; + case DT_OK_CANCEL: Sys_AppendToExecBuffer( "--warningcontinuecancel" ); break; + } + + Sys_AppendToExecBuffer( message ); + Sys_AppendToExecBuffer( va( "--title=%s", title ) ); +} + +/* +============== +Sys_XmessageCommand +============== +*/ +static void Sys_XmessageCommand( dialogType_t type, const char *message, const char *title ) +{ + Sys_ClearExecBuffer( ); + Sys_AppendToExecBuffer( "xmessage" ); + Sys_AppendToExecBuffer( "-buttons" ); + + switch( type ) + { + default: Sys_AppendToExecBuffer( "OK:0" ); break; + case DT_YES_NO: Sys_AppendToExecBuffer( "Yes:0,No:1" ); break; + case DT_OK_CANCEL: Sys_AppendToExecBuffer( "OK:0,Cancel:1" ); break; + } + + Sys_AppendToExecBuffer( "-center" ); + Sys_AppendToExecBuffer( message ); +} + +/* +============== +Sys_Dialog + +Display a *nix dialog box +============== +*/ +dialogResult_t Sys_Dialog( dialogType_t type, const char *message, const char *title ) +{ + typedef enum + { + NONE = 0, + ZENITY, + KDIALOG, + XMESSAGE, + NUM_DIALOG_PROGRAMS + } dialogCommandType_t; + typedef void (*dialogCommandBuilder_t)( dialogType_t, const char *, const char * ); + + const char *session = getenv( "DESKTOP_SESSION" ); + bool tried[ NUM_DIALOG_PROGRAMS ] = { false }; + dialogCommandBuilder_t commands[ NUM_DIALOG_PROGRAMS ] = { NULL }; + dialogCommandType_t preferredCommandType = NONE; + int i; + + commands[ ZENITY ] = &Sys_ZenityCommand; + commands[ KDIALOG ] = &Sys_KdialogCommand; + commands[ XMESSAGE ] = &Sys_XmessageCommand; + + // This may not be the best way + if( !Q_stricmp( session, "gnome" ) ) + preferredCommandType = ZENITY; + else if( !Q_stricmp( session, "kde" ) ) + preferredCommandType = KDIALOG; + + for( i = NONE + 1; i < NUM_DIALOG_PROGRAMS; i++ ) + { + if( preferredCommandType != NONE && preferredCommandType != i ) + continue; + + if( !tried[ i ] ) + { + int exitCode; + + commands[ i ]( type, message, title ); + exitCode = Sys_Exec( ); + + if( exitCode >= 0 ) + { + switch( type ) + { + case DT_YES_NO: return exitCode ? DR_NO : DR_YES; + case DT_OK_CANCEL: return exitCode ? DR_CANCEL : DR_OK; + default: return DR_OK; + } + } + + tried[ i ] = true; + + // The preference failed, so start again in order + if( preferredCommandType != NONE ) + { + preferredCommandType = NONE; + i = NONE + 1; + } + } + } + + Com_DPrintf( S_COLOR_YELLOW "WARNING: failed to show a dialog\n" ); + return DR_OK; +} +#endif + +/* +============== +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 +} + +void Sys_SetFloatEnv(void) +{ + // rounding toward nearest + fesetround(FE_TONEAREST); +} + +/* +============== +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( SIGABRT, Sys_SigHandler ); + signal( SIGBUS, Sys_SigHandler ); + + Sys_SetFloatEnv(); + + stdinIsATTY = isatty( STDIN_FILENO ) && + !( term && ( !strcmp( term, "raw" ) || !strcmp( term, "dumb" ) ) ); +} + +/* +============== +Sys_PlatformExit + +Unix specific deinitialisation +============== +*/ +void Sys_PlatformExit( void ) +{ +} + +/* +============== +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); +} + +/* +============== +Sys_PID +============== +*/ +int Sys_PID( void ) +{ + return getpid( ); +} + +/* +============== +Sys_PIDIsRunning +============== +*/ +bool Sys_PIDIsRunning( int pid ) +{ + return kill( pid, 0 ) == 0; +} + + +/* +================= +Sys_DllExtension + +Check if filename should be allowed to be loaded as a DLL. +================= +*/ +bool Sys_DllExtension( const char *name ) +{ + const char *p; + char c = 0; + + if ( COM_CompareExtension(name, DLL_EXT) ) + return true; + + // Check for format of filename.so.1.2.3 + p = strstr( name, DLL_EXT "." ); + + if ( p ) + { + p += strlen( DLL_EXT ); + + // Check if .so is only followed for periods and numbers. + while ( *p ) + { + c = *p; + + if ( !isdigit( c ) && c != '.' ) + return false; + + p++; + } + + // Don't allow filename to end in a period. file.so., file.so.0., etc + if ( c != '.' ) + return true; + } + + return false; +} -- cgit