diff options
Diffstat (limited to 'src/sys_unix.c')
-rw-r--r-- | src/sys_unix.c | 1046 |
1 files changed, 1046 insertions, 0 deletions
diff --git a/src/sys_unix.c b/src/sys_unix.c new file mode 100644 index 0000000..885a97b --- /dev/null +++ b/src/sys_unix.c @@ -0,0 +1,1046 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program 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. + +This program 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 this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include <unistd.h> +#include <signal.h> +#include <stdlib.h> +#include <limits.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdio.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#include <sys/stat.h> +#include <string.h> +#include <ctype.h> +#include <sys/wait.h> +#include <sys/mman.h> +#include <dirent.h> +#include <dlfcn.h> +#include <termios.h> +#include <sys/ioctl.h> +#ifndef __linux__ +#include <machine/param.h> +#endif + +#include "com_local.h" +#include "q_list.h" +#include "q_field.h" +#include "prompt.h" +#include "files.h" +#if USE_REF +#include "vid_public.h" +#endif +#include "sys_public.h" +#include "io_sleep.h" + +cvar_t *sys_basedir; +cvar_t *sys_libdir; +cvar_t *sys_homedir; +cvar_t *sys_parachute; + + +/* +=============================================================================== + +TERMINAL SUPPORT + +=============================================================================== +*/ + +#if USE_SYSCON + +cvar_t *sys_console; + +static qboolean tty_enabled; +static struct termios tty_orig; +static commandPrompt_t tty_prompt; +static int tty_hidden; +static ioentry_t *tty_io; + + +static void tty_hide_input( void ) { + int i; + + if( !tty_hidden ) { + for( i = 0; i <= tty_prompt.inputLine.cursorPos; i++ ) { + write( 1, "\b \b", 3 ); + } + } + tty_hidden++; +} + +static void tty_show_input( void ) { + if( !tty_hidden ) { + return; + } + + tty_hidden--; + if( !tty_hidden ) { + write( 1, "]", 1 ); + write( 1, tty_prompt.inputLine.text, + tty_prompt.inputLine.cursorPos ); + } +} + +static void tty_init_input( void ) { + struct termios tty; +#ifdef TIOCGWINSZ + struct winsize ws; +#endif + int width; + + if( !isatty( 0 ) || !isatty( 1 ) ) { + Com_Printf( "stdin/stdout don't both refer to a TTY\n" ); + Cvar_Set( "sys_console", "1" ); + return; + } + + tcgetattr( 0, &tty_orig ); + tty = tty_orig; + tty.c_lflag &= ~( ECHO | ICANON | INPCK | ISTRIP ); + tty.c_cc[VMIN] = 1; + tty.c_cc[VTIME] = 0; + tcsetattr( 0, TCSADRAIN, &tty ); + + // determine terminal width + width = 80; +#ifdef TIOCGWINSZ + if( ioctl( 0, TIOCGWINSZ, &ws ) == 0 ) { + if( ws.ws_col ) { + width = ws.ws_col; + } + } +#endif + tty_prompt.widthInChars = width; + tty_prompt.printf = Sys_Printf; + tty_enabled = qtrue; + + // figure out input line width + width--; + if( width > MAX_FIELD_TEXT - 1 ) { + width = MAX_FIELD_TEXT - 1; + } + IF_Init( &tty_prompt.inputLine, width, width ); + + // display command prompt + write( 1, "]", 1 ); +} + +static void tty_shutdown_input( void ) { + if( tty_io ) { + IO_Remove( 0 ); + tty_io = NULL; + } + if( tty_enabled ) { + tty_hide_input(); + tcsetattr( 0, TCSADRAIN, &tty_orig ); + tty_enabled = qfalse; + } +} + +void Sys_SetConsoleColor( color_index_t color ) { + static const char color_to_ansi[8] = + { '0', '1', '2', '3', '4', '6', '5', '7' }; + char buf[5]; + size_t len; + + if( !sys_console || !sys_console->integer ) { + return; + } + + if( !tty_enabled ) { + return; + } + + buf[0] = '\033'; + buf[1] = '['; + switch( color ) { + case COLOR_NONE: + buf[2] = '0'; + buf[3] = 'm'; + len = 4; + break; + case COLOR_ALT: + buf[2] = '3'; + buf[3] = '2'; + buf[4] = 'm'; + len = 5; + break; + default: + buf[2] = '3'; + buf[3] = color_to_ansi[color]; + buf[4] = 'm'; + len = 5; + break; + } + + write( 1, buf, len ); +} + +static void tty_write_output( const char *text ) { + char buf[MAXPRINTMSG]; + size_t len; + + for( len = 0; len < MAXPRINTMSG; len++ ) { + int c = *text++; + if( !c ) { + break; + } + buf[len] = Q_charascii( c ); + } + + write( 1, buf, len ); +} + +/* +================= +Sys_ConsoleOutput +================= +*/ +void Sys_ConsoleOutput( const char *text ) { + if( !sys_console || !sys_console->integer ) { + return; + } + + if( !tty_enabled ) { + tty_write_output( text ); + } else { + tty_hide_input(); + tty_write_output( text ); + tty_show_input(); + } +} + +void Sys_SetConsoleTitle( const char *title ) { + char buffer[MAX_STRING_CHARS]; + size_t len; + + if( !sys_console || !sys_console->integer ) { + return; + } + + if( !tty_enabled ) { + return; + } + + len = Q_snprintf( buffer, sizeof( buffer ), "\033]0;%s\007", title ); + if( len < sizeof( buffer ) ) { + write( 1, buffer, len ); + } +} + +static void tty_parse_input( const char *text ) { + inputField_t *f; + char *s; + int i, key; + + f = &tty_prompt.inputLine; + while( *text ) { + key = *text++; + + if( key == tty_orig.c_cc[VERASE] || key == 127 || key == 8 ) { + if( f->cursorPos ) { + f->text[--f->cursorPos] = 0; + write( 1, "\b \b", 3 ); + } + continue; + } + + if( key == tty_orig.c_cc[VKILL] ) { + for( i = 0; i < f->cursorPos; i++ ) { + write( 1, "\b \b", 3 ); + } + f->cursorPos = 0; + continue; + } + + if( key >= 32 ) { + if( f->cursorPos == f->maxChars - 1 ) { + write( 1, va( "\b \b%c", key ), 4 ); + f->text[f->cursorPos+0] = key; + f->text[f->cursorPos+1] = 0; + } else { + write( 1, va( "%c", key ), 1 ); + f->text[f->cursorPos+0] = key; + f->text[f->cursorPos+1] = 0; + f->cursorPos++; + } + continue; + } + + if( key == '\n' ) { + tty_hide_input(); + s = Prompt_Action( &tty_prompt ); + if( s ) { + if( *s == '\\' || *s == '/' ) { + s++; + } + Sys_Printf( "]%s\n", s ); + Cbuf_AddText( &cmd_buffer, s ); + } else { + write( 1, "]\n", 2 ); + } + tty_show_input(); + continue; + } + + if( key == '\t' ) { + tty_hide_input(); + Prompt_CompleteCommand( &tty_prompt, qfalse ); + f->cursorPos = strlen( f->text ); // FIXME + tty_show_input(); + continue; + } + + if( *text ) { + key = *text++; + if( key == '[' || key == 'O' ) { + if( *text ) { + key = *text++; + switch( key ) { + case 'A': + tty_hide_input(); + Prompt_HistoryUp( &tty_prompt ); + tty_show_input(); + break; + case 'B': + tty_hide_input(); + Prompt_HistoryDown( &tty_prompt ); + tty_show_input(); + break; +#if 0 + case 'C': + if( f->text[f->cursorPos] ) { + Sys_ConsoleWrite( "\033[C", 3 ); + f->cursorPos++; + } + break; + case 'D': + if( f->cursorPos ) { + Sys_ConsoleWrite( "\033[D", 3 ); + f->cursorPos--; + } + break; +#endif + } + } + } + } + } +} + +void Sys_RunConsole( void ) { + char text[MAX_STRING_CHARS]; + int ret; + + if( !sys_console || !sys_console->integer ) { + return; + } + + if( !tty_io || !tty_io->canread ) { + return; + } + + ret = read( 0, text, sizeof( text ) - 1 ); + if( !ret ) { + Com_DPrintf( "Read EOF from stdin.\n" ); + tty_shutdown_input(); + Cvar_Set( "sys_console", "0" ); + return; + } + if( ret < 0 ) { + if( errno == EINTR ) { + return; + } + Com_Error( ERR_FATAL, "%s: read() failed: %s", + __func__, strerror( errno ) ); + } + text[ret] = 0; + + if( !tty_enabled ) { + Cbuf_AddText( &cmd_buffer, text ); + return; + } + + tty_parse_input( text ); +} + +#endif // USE_SYSCON + +/* +=============================================================================== + +HUNK + +=============================================================================== +*/ + +void Hunk_Begin( mempool_t *pool, size_t maxsize ) { + void *buf; + + // reserve a huge chunk of memory, but don't commit any yet + pool->maxsize = ( maxsize + 4095 ) & ~4095; + pool->cursize = 0; + buf = mmap( NULL, pool->maxsize, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANON, -1, 0 ); + if( buf == NULL || buf == ( void * )-1 ) { + Com_Error( ERR_FATAL, "%s: unable to reserve %"PRIz" bytes: %s", + __func__, pool->maxsize, strerror( errno ) ); + } + pool->base = buf; + pool->mapped = pool->maxsize; +} + +void *Hunk_Alloc( mempool_t *pool, size_t size ) { + void *buf; + + // round to cacheline + size = ( size + 63 ) & ~63; + if( pool->cursize + size > pool->maxsize ) { + Com_Error( ERR_FATAL, "%s: unable to allocate %"PRIz" bytes out of %"PRIz, + __func__, size, pool->maxsize ); + } + buf = ( byte * )pool->base + pool->cursize; + pool->cursize += size; + return buf; +} + +void Hunk_End( mempool_t *pool ) { + size_t newsize = ( pool->cursize + 4095 ) & ~4095; + + if( newsize > pool->maxsize ) { + Com_Error( ERR_FATAL, "%s: newsize > maxsize", __func__ ); + } + + if( newsize < pool->maxsize ) { +#ifdef _GNU_SOURCE + void *buf = mremap( pool->base, pool->maxsize, newsize, 0 ); +#else + void *unmap_base = ( byte * )pool->base + newsize; + size_t unmap_len = pool->maxsize - newsize; + void *buf = munmap( unmap_base, unmap_len ) + pool->base; +#endif + if( buf != pool->base ) { + Com_Error( ERR_FATAL, "%s: could not remap virtual block: %s", + __func__, strerror( errno ) ); + } + } + pool->mapped = newsize; +} + +void Hunk_Free( mempool_t *pool ) { + if( pool->base ) { + if( munmap( pool->base, pool->mapped ) ) { + Com_Error( ERR_FATAL, "%s: munmap failed: %s", + __func__, strerror( errno ) ); + } + } + memset( pool, 0, sizeof( *pool ) ); +} + +/* +=============================================================================== + +GENERAL ROUTINES + +=============================================================================== +*/ + +void Sys_DebugBreak( void ) { + raise( SIGTRAP ); +} + +unsigned Sys_Milliseconds( void ) { + struct timeval tp; + unsigned time; + + gettimeofday( &tp, NULL ); + time = tp.tv_sec * 1000 + tp.tv_usec / 1000; + return time; +} + +/* +================ +Sys_GetPathInfo +================ +*/ +qerror_t Sys_GetPathInfo( const char *path, file_info_t *info ) { + struct stat st; + + if( stat( path, &st ) == -1 ) { + return Q_ERR(errno); + } + + if( !S_ISREG( st.st_mode ) ) { + return Q_ERR_ISDIR; + } + + if( info ) { + info->size = st.st_size; + info->ctime = st.st_ctime; + info->mtime = st.st_mtime; + } + + return Q_ERR_SUCCESS; +} + +qerror_t Sys_GetFileInfo( FILE *fp, file_info_t *info ) { + struct stat st; + + if( fstat( fileno( fp ), &st ) == -1 ) { + return Q_ERR(errno); + } + + if( !S_ISREG( st.st_mode ) ) { + return Q_ERR_ISDIR; + } + + if( info ) { + info->size = st.st_size; + info->ctime = st.st_ctime; + info->mtime = st.st_mtime; + } + + return Q_ERR_SUCCESS; +} + +/* +================= +Sys_Quit + +This function never returns. +================= +*/ +void Sys_Quit( void ) { +#if USE_SYSCON + tty_shutdown_input(); +#endif + exit( 0 ); +} + +/* +================= +Sys_GetCurrentDirectory +================= +*/ +char *Sys_GetCurrentDirectory( void ) { + static char curpath[MAX_OSPATH]; + + getcwd( curpath, sizeof( curpath ) ); + + return curpath; +} + +void Sys_AddDefaultConfig( void ) { + FILE *fp; + struct stat st; + size_t len, r; + + fp = fopen( SYS_SITECFG_NAME, "r" ); + if( !fp ) { + return; + } + + if( fstat( fileno( fp ), &st ) == 0 ) { + Com_Printf( "Execing " SYS_SITECFG_NAME "\n" ); + + len = st.st_size; + if( len >= cmd_buffer.maxsize ) { + len = cmd_buffer.maxsize - 1; + } + + r = fread( cmd_buffer.text, 1, len, fp ); + cmd_buffer.text[r] = 0; + + cmd_buffer.cursize = COM_Compress( cmd_buffer.text ); + } + + fclose( fp ); +} + +void Sys_Sleep( int msec ) { + struct timespec req; + + req.tv_sec = msec / 1000; + req.tv_nsec = ( msec % 1000 ) * 1000000; + nanosleep( &req, NULL ); +} + +#if USE_AC_CLIENT +qboolean Sys_GetAntiCheatAPI( void ) { + Sys_Sleep( 1500 ); + return qfalse; +} +#endif + +void Sys_FixFPCW( void ) { +#ifdef __i386__ + uint16_t cw; + + __asm__ __volatile__( "fnstcw %0" : "=m" (cw) ); + + Com_DPrintf( "FPU control word: %x\n", cw ); + + if( cw & 0x300 ) { + Com_DPrintf( "Setting FPU to single precision mode\n" ); + cw &= ~0x300; + } + if( cw & 0xC00 ) { + Com_DPrintf( "Setting FPU to round to nearest mode\n" ); + cw &= ~0xC00; + } + + __asm__ __volatile__( "fldcw %0" : : "m" (cw) ); +#endif +} + +static void hup_handler( int signum ) { + Com_FlushLogs(); +} + +static void term_handler( int signum ) { +#ifdef _GNU_SOURCE + Com_Printf( "%s\n", strsignal( signum ) ); +#else + Com_Printf( "Received signal %d, exiting\n", signum ); +#endif + Com_Quit( NULL, KILL_DROP ); +} + +static void kill_handler( int signum ) { +#if USE_SYSCON + tty_shutdown_input(); +#endif + +#if USE_CLIENT && USE_REF + VID_FatalShutdown(); +#endif + +#ifdef _GNU_SOURCE + fprintf( stderr, "%s\n", strsignal( signum ) ); +#else + fprintf( stderr, "Received signal %d, aborting\n", signum ); +#endif + + exit( 1 ); +} + +/* +================= +Sys_Init +================= +*/ +void Sys_Init( void ) { + char *homedir; + + signal( SIGTERM, term_handler ); + signal( SIGINT, term_handler ); + signal( SIGTTIN, SIG_IGN ); + signal( SIGTTOU, SIG_IGN ); + signal( SIGUSR1, hup_handler ); + + // basedir <path> + // allows the game to run from outside the data tree + sys_basedir = Cvar_Get( "basedir", DATADIR, CVAR_NOSET ); + + // homedir <path> + // specifies per-user writable directory for demos, screenshots, etc + if( HOMEDIR[0] == '~' ) { + char *s = getenv( "HOME" ); + if( s && *s ) { + homedir = va( "%s%s", s, HOMEDIR + 1 ); + } else { + homedir = ""; + } + } else { + homedir = HOMEDIR; + } + sys_homedir = Cvar_Get( "homedir", homedir, CVAR_NOSET ); + sys_libdir = Cvar_Get( "libdir", LIBDIR, CVAR_NOSET ); + +#if USE_SYSCON + // we want TTY support enabled if started from terminal, + // but don't want any output by default if launched without one + // (from X session for example) + sys_console = Cvar_Get( "sys_console", isatty( 0 ) && + isatty( 1 ) ? "2" : "0", CVAR_NOSET ); + + if( sys_console->integer > 0 ) { + // change stdin to non-blocking and stdout to blocking + fcntl( 0, F_SETFL, fcntl( 0, F_GETFL, 0 ) | FNDELAY ); + fcntl( 1, F_SETFL, fcntl( 1, F_GETFL, 0 ) & ~FNDELAY ); + + // add stdin to the list of descriptors to wait on + tty_io = IO_Add( 0 ); + tty_io->wantread = qtrue; + + // init optional TTY support + if( sys_console->integer > 1 ) { + tty_init_input(); + } + signal( SIGHUP, term_handler ); + } else +#endif + if( Com_IsDedicated() ) { + signal( SIGHUP, hup_handler ); + } + + sys_parachute = Cvar_Get( "sys_parachute", "1", CVAR_NOSET ); + + if( sys_parachute->integer ) { + // perform some cleanup when crashing + signal( SIGSEGV, kill_handler ); + signal( SIGILL, kill_handler ); + signal( SIGFPE, kill_handler ); + signal( SIGTRAP, kill_handler ); + } + + Sys_FixFPCW(); +} + +#if USE_SYSCON +/* +================ +Sys_Printf +================ +*/ +void Sys_Printf( const char *fmt, ... ) { + va_list argptr; + char msg[MAXPRINTMSG]; + + va_start( argptr, fmt ); + Q_vsnprintf( msg, sizeof( msg ), fmt, argptr ); + va_end( argptr ); + + Sys_ConsoleOutput( msg ); +} +#endif + +/* +================= +Sys_Error +================= +*/ +void Sys_Error( const char *error, ... ) { + va_list argptr; + char text[MAXPRINTMSG]; + +#if USE_SYSCON + tty_shutdown_input(); +#endif + +#if USE_CLIENT && USE_REF + VID_FatalShutdown(); +#endif + + va_start( argptr, error ); + Q_vsnprintf( text, sizeof( text ), error, argptr ); + va_end( argptr ); + + fprintf( stderr, "********************\n" + "FATAL: %s\n" + "********************\n", text ); + exit( 1 ); +} + +/* +======================================================================== + +DLL LOADING + +======================================================================== +*/ + +/* +================= +Sys_FreeLibrary +================= +*/ +void Sys_FreeLibrary( void *handle ) { + if( handle && dlclose( handle ) ) { + Com_Error( ERR_FATAL, "dlclose failed on %p: %s", handle, dlerror() ); + } +} + +/* +================= +Sys_LoadLibrary +================= +*/ +void *Sys_LoadLibrary( const char *path, const char *sym, void **handle ) { + void *module, *entry; + + *handle = NULL; + + module = dlopen( path, RTLD_LAZY ); + if( !module ) { + Com_DPrintf( "%s failed: %s\n", __func__, dlerror() ); + return NULL; + } + + if( sym ) { + entry = dlsym( module, sym ); + if( !entry ) { + Com_DPrintf( "%s failed: %s\n", __func__, dlerror() ); + dlclose( module ); + return NULL; + } + } else { + entry = NULL; + } + + Com_DPrintf( "%s succeeded: %s\n", __func__, path ); + + *handle = module; + + return entry; +} + +void *Sys_GetProcAddress( void *handle, const char *sym ) { + return dlsym( handle, sym ); +} + +/* +=============================================================================== + +MISC + +=============================================================================== +*/ + +/* +================= +Sys_ListFilteredFiles +================= +*/ +static void Sys_ListFilteredFiles( void **listedFiles, + int *count, + const char *path, + const char *filter, + int flags, + size_t baselen, + int depth ) +{ + struct dirent *findInfo; + DIR *findHandle; + struct stat st; + char findPath[MAX_OSPATH]; + char *name; + size_t len; + + if( depth >= 32 ) { + return; + } + + if( *count >= MAX_LISTED_FILES ) { + return; + } + + if( ( findHandle = opendir( path ) ) == NULL ) { + return; + } + + while( ( findInfo = readdir( findHandle ) ) != NULL ) { + if( !strcmp( findInfo->d_name, "." ) ) { + continue; + } + if( !strcmp( findInfo->d_name, ".." ) ) { + continue; + } + len = Q_concat( findPath, sizeof( findPath ), + path, "/", findInfo->d_name, NULL ); + if( len >= sizeof( findPath ) ) { + continue; + } + + if( stat( findPath, &st ) == -1 ) { + continue; + } + + if( st.st_mode & S_IFDIR ) { + Sys_ListFilteredFiles( listedFiles, count, findPath, + filter, flags, baselen, depth + 1 ); + } + + if( ( flags & FS_SEARCHDIRS_MASK ) == FS_SEARCHDIRS_ONLY ) { + if( !( st.st_mode & S_IFDIR ) ) { + continue; + } + } else if( ( flags & FS_SEARCHDIRS_MASK ) == FS_SEARCHDIRS_NO ) { + if( st.st_mode & S_IFDIR ) { + continue; + } + } + + if( !FS_WildCmp( filter, findPath + baselen ) ) { + continue; + } + if( flags & FS_SEARCH_SAVEPATH ) { + name = findPath + baselen; + } else { + name = findInfo->d_name; + } + + if( flags & FS_SEARCH_EXTRAINFO ) { + listedFiles[( *count )++] = FS_CopyInfo( name, + st.st_size, st.st_ctime, st.st_mtime ); + } else { + listedFiles[( *count )++] = FS_CopyString( name ); + } + if( *count >= MAX_LISTED_FILES ) { + break; + } + + } + + closedir( findHandle ); +} + + +/* +================= +Sys_ListFiles +================= +*/ +void **Sys_ListFiles( const char *path, + const char *extension, + int flags, + size_t baselen, + int *numFiles ) +{ + struct dirent *findInfo; + DIR *findHandle; + struct stat st; + char findPath[MAX_OSPATH]; + void *listedFiles[MAX_LISTED_FILES]; + int count = 0; + char *s; + size_t len; + + if( numFiles ) { + *numFiles = 0; + } + + if( flags & FS_SEARCH_BYFILTER ) { + Sys_ListFilteredFiles( listedFiles, &count, path, + extension, flags, baselen, 0 ); + } else { + if( ( findHandle = opendir( path ) ) == NULL ) { + return NULL; + } + + while( ( findInfo = readdir( findHandle ) ) != NULL ) { + if( !strcmp( findInfo->d_name, "." ) ) { + continue; + } + if( !strcmp( findInfo->d_name, ".." ) ) { + continue; + } + + len = Q_concat( findPath, sizeof( findPath ), + path, "/", findInfo->d_name, NULL ); + if( len >= sizeof( findPath ) ) { + continue; + } + + if( stat( findPath, &st ) == -1 ) { + continue; + } + if( ( flags & FS_SEARCHDIRS_MASK ) == FS_SEARCHDIRS_ONLY ) { + if( !( st.st_mode & S_IFDIR ) ) { + continue; + } + } else if( ( flags & FS_SEARCHDIRS_MASK ) == FS_SEARCHDIRS_NO ) { + if( st.st_mode & S_IFDIR ) { + continue; + } + } + + if( extension && !FS_ExtCmp( extension, findInfo->d_name ) ) { + continue; + } + + if( flags & FS_SEARCH_SAVEPATH ) { + s = findPath + baselen; + } else { + s = findInfo->d_name; + } + if( flags & FS_SEARCH_EXTRAINFO ) { + listedFiles[count++] = FS_CopyInfo( s, st.st_size, + st.st_ctime, st.st_mtime ); + } else { + listedFiles[count++] = FS_CopyString( s ); + } + + if( count >= MAX_LISTED_FILES ) { + break; + } + } + + closedir( findHandle ); + } + + if( !count ) { + return NULL; + } + + if( numFiles ) { + *numFiles = count; + } + + return FS_CopyList( listedFiles, count ); +} + +/* +================= +main +================= +*/ +int main( int argc, char **argv ) { + if( argc > 1 ) { + if( !strcmp( argv[1], "-v" ) || !strcmp( argv[1], "--version" ) ) { + printf( APPLICATION " " VERSION " " __DATE__ " " BUILDSTRING " " + CPUSTRING "\n" ); + return 0; + } + if( !strcmp( argv[1], "-h" ) || !strcmp( argv[1], "--help" ) ) { + printf( "Usage: %s [+command arguments] [...]\n", argv[0] ); + return 0; + } + } + + if( !getuid() || !geteuid() ) { + printf( "You can not run " APPLICATION " as superuser " + "for security reasons!\n" ); + return 1; + } + + Qcommon_Init( argc, argv ); + while( 1 ) { + Qcommon_Frame(); + } + + return 1; // never gets here +} + |