Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

unix_main.c (28793B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, 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 Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 #include <unistd.h>
     23 #include <signal.h>
     24 #include <stdlib.h>
     25 #include <limits.h>
     26 #include <sys/time.h>
     27 #include <sys/types.h>
     28 #include <unistd.h>
     29 #include <fcntl.h>
     30 #include <stdarg.h>
     31 #include <stdio.h>
     32 #include <sys/ipc.h>
     33 #include <sys/shm.h>
     34 #include <sys/stat.h>
     35 #include <string.h>
     36 #include <ctype.h>
     37 #include <sys/wait.h>
     38 #include <sys/mman.h>
     39 #include <errno.h>
     40 #ifdef __linux__ // rb010123
     41   #include <mntent.h>
     42 #endif
     43 #include <dlfcn.h>
     44 
     45 #ifdef __linux__
     46   #include <fpu_control.h> // bk001213 - force dumps on divide by zero
     47 #endif
     48 
     49 // FIXME TTimo should we gard this? most *nix system should comply?
     50 #include <termios.h>
     51 
     52 #include "../game/q_shared.h"
     53 #include "../qcommon/qcommon.h"
     54 #include "../renderer/tr_public.h"
     55 
     56 #include "linux_local.h" // bk001204
     57 
     58 // Structure containing functions exported from refresh DLL
     59 refexport_t re;
     60 
     61 unsigned  sys_frame_time;
     62 
     63 uid_t saved_euid;
     64 qboolean stdin_active = qtrue;
     65 
     66 // =============================================================
     67 // tty console variables
     68 // =============================================================
     69 
     70 // enable/disabled tty input mode
     71 // NOTE TTimo this is used during startup, cannot be changed during run
     72 static cvar_t *ttycon = NULL;
     73 // general flag to tell about tty console mode
     74 static qboolean ttycon_on = qfalse;
     75 // when printing general stuff to stdout stderr (Sys_Printf)
     76 //   we need to disable the tty console stuff
     77 // this increments so we can recursively disable
     78 static int ttycon_hide = 0;
     79 // some key codes that the terminal may be using
     80 // TTimo NOTE: I'm not sure how relevant this is
     81 static int tty_erase;
     82 static int tty_eof;
     83 
     84 static struct termios tty_tc;
     85 
     86 static field_t tty_con;
     87 
     88 // history
     89 // NOTE TTimo this is a bit duplicate of the graphical console history
     90 //   but it's safer and faster to write our own here
     91 #define TTY_HISTORY 32
     92 static field_t ttyEditLines[TTY_HISTORY];
     93 static int hist_current = -1, hist_count = 0;
     94 
     95 // =======================================================================
     96 // General routines
     97 // =======================================================================
     98 
     99 // bk001207 
    100 #define MEM_THRESHOLD 96*1024*1024
    101 
    102 /*
    103 ==================
    104 Sys_LowPhysicalMemory()
    105 ==================
    106 */
    107 qboolean Sys_LowPhysicalMemory() {
    108   //MEMORYSTATUS stat;
    109   //GlobalMemoryStatus (&stat);
    110   //return (stat.dwTotalPhys <= MEM_THRESHOLD) ? qtrue : qfalse;
    111   return qfalse; // bk001207 - FIXME
    112 }
    113 
    114 /*
    115 ==================
    116 Sys_FunctionCmp
    117 ==================
    118 */
    119 int Sys_FunctionCmp(void *f1, void *f2) {
    120   return qtrue;
    121 }
    122 
    123 /*
    124 ==================
    125 Sys_FunctionCheckSum
    126 ==================
    127 */
    128 int Sys_FunctionCheckSum(void *f1) {
    129   return 0;
    130 }
    131 
    132 /*
    133 ==================
    134 Sys_MonkeyShouldBeSpanked
    135 ==================
    136 */
    137 int Sys_MonkeyShouldBeSpanked( void ) {
    138   return 0;
    139 }
    140 
    141 void Sys_BeginProfiling( void ) {
    142 }
    143 
    144 /*
    145 =================
    146 Sys_In_Restart_f
    147 
    148 Restart the input subsystem
    149 =================
    150 */
    151 void Sys_In_Restart_f( void ) 
    152 {
    153   IN_Shutdown();
    154   IN_Init();
    155 }
    156 
    157 // =============================================================
    158 // tty console routines
    159 // NOTE: if the user is editing a line when something gets printed to the early console then it won't look good
    160 //   so we provide tty_Clear and tty_Show to be called before and after a stdout or stderr output
    161 // =============================================================
    162 
    163 // flush stdin, I suspect some terminals are sending a LOT of shit
    164 // FIXME TTimo relevant?
    165 void tty_FlushIn()
    166 {
    167   char key;
    168   while (read(0, &key, 1)!=-1);
    169 }
    170 
    171 // do a backspace
    172 // TTimo NOTE: it seems on some terminals just sending '\b' is not enough
    173 //   so for now, in any case we send "\b \b" .. yeah well ..
    174 //   (there may be a way to find out if '\b' alone would work though)
    175 void tty_Back()
    176 {
    177   char key;
    178   key = '\b';
    179   write(1, &key, 1);
    180   key = ' ';
    181   write(1, &key, 1);
    182   key = '\b';
    183   write(1, &key, 1);
    184 }
    185 
    186 // clear the display of the line currently edited
    187 // bring cursor back to beginning of line
    188 void tty_Hide()
    189 {
    190   int i;
    191   assert(ttycon_on);
    192   if (ttycon_hide)
    193   {
    194     ttycon_hide++;
    195     return;
    196   }
    197   if (tty_con.cursor>0)
    198   {
    199     for (i=0; i<tty_con.cursor; i++)
    200     {
    201       tty_Back();
    202     }
    203   }
    204   ttycon_hide++;
    205 }
    206 
    207 // show the current line
    208 // FIXME TTimo need to position the cursor if needed??
    209 void tty_Show()
    210 {
    211   int i;
    212   assert(ttycon_on);
    213   assert(ttycon_hide>0);
    214   ttycon_hide--;
    215   if (ttycon_hide == 0)
    216   {
    217     if (tty_con.cursor)
    218     {
    219       for (i=0; i<tty_con.cursor; i++)
    220       {
    221         write(1, tty_con.buffer+i, 1);
    222       }
    223     }
    224   }
    225 }
    226 
    227 // never exit without calling this, or your terminal will be left in a pretty bad state
    228 void Sys_ConsoleInputShutdown()
    229 {
    230   if (ttycon_on)
    231   {
    232     Com_Printf("Shutdown tty console\n");
    233     tcsetattr (0, TCSADRAIN, &tty_tc);
    234   }
    235 }
    236 
    237 void Hist_Add(field_t *field)
    238 {
    239   int i;
    240   assert(hist_count <= TTY_HISTORY);
    241   assert(hist_count >= 0);
    242   assert(hist_current >= -1);
    243   assert(hist_current <= hist_count);
    244   // make some room
    245   for (i=TTY_HISTORY-1; i>0; i--)
    246   {
    247     ttyEditLines[i] = ttyEditLines[i-1];
    248   }
    249   ttyEditLines[0] = *field;
    250   if (hist_count<TTY_HISTORY)
    251   {
    252     hist_count++;
    253   }
    254   hist_current = -1; // re-init
    255 }
    256 
    257 field_t *Hist_Prev()
    258 {
    259   int hist_prev;
    260   assert(hist_count <= TTY_HISTORY);
    261   assert(hist_count >= 0);
    262   assert(hist_current >= -1);
    263   assert(hist_current <= hist_count);
    264   hist_prev = hist_current + 1;
    265   if (hist_prev >= hist_count)
    266   {
    267     return NULL;
    268   }
    269   hist_current++;
    270   return &(ttyEditLines[hist_current]);
    271 }
    272 
    273 field_t *Hist_Next()
    274 {
    275   assert(hist_count <= TTY_HISTORY);
    276   assert(hist_count >= 0);
    277   assert(hist_current >= -1);
    278   assert(hist_current <= hist_count);
    279   if (hist_current >= 0)
    280   {
    281     hist_current--;
    282   }
    283   if (hist_current == -1)
    284   {
    285     return NULL;
    286   }
    287   return &(ttyEditLines[hist_current]);
    288 }
    289 
    290 // =============================================================
    291 // general sys routines
    292 // =============================================================
    293 
    294 #if 0
    295 // NOTE TTimo this is not used .. looks interesting though? protection against buffer overflow kind of stuff?
    296 void Sys_Printf (char *fmt, ...)
    297 {
    298   va_list   argptr;
    299   char    text[1024];
    300   unsigned char   *p;
    301 
    302   va_start (argptr,fmt);
    303   vsprintf (text,fmt,argptr);
    304   va_end (argptr);
    305 
    306   if (strlen(text) > sizeof(text))
    307     Sys_Error("memory overwrite in Sys_Printf");
    308 
    309   for (p = (unsigned char *)text; *p; p++)
    310   {
    311     *p &= 0x7f;
    312     if ((*p > 128 || *p < 32) && *p != 10 && *p != 13 && *p != 9)
    313       printf("[%02x]", *p);
    314     else
    315       putc(*p, stdout);
    316   }
    317 }
    318 #endif
    319 
    320 // single exit point (regular exit or in case of signal fault)
    321 void Sys_Exit( int ex ) {
    322   Sys_ConsoleInputShutdown();
    323 
    324 #ifdef NDEBUG // regular behavior
    325 
    326   // We can't do this 
    327   //  as long as GL DLL's keep installing with atexit...
    328   //exit(ex);
    329   _exit(ex);
    330 #else
    331 
    332   // Give me a backtrace on error exits.
    333   assert( ex == 0 );
    334   exit(ex);
    335 #endif
    336 }
    337 
    338 
    339 void Sys_Quit (void) {
    340   CL_Shutdown ();
    341   fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY);
    342   Sys_Exit(0);
    343 }
    344 
    345 void Sys_Init(void)
    346 {
    347   Cmd_AddCommand ("in_restart", Sys_In_Restart_f);
    348 
    349 #if defined __linux__
    350 #if defined __i386__
    351   Cvar_Set( "arch", "linux i386" );
    352 #elif defined __alpha__
    353   Cvar_Set( "arch", "linux alpha" );
    354 #elif defined __sparc__
    355   Cvar_Set( "arch", "linux sparc" );
    356 #elif defined __FreeBSD__
    357 
    358 #if defined __i386__ // FreeBSD
    359   Cvar_Set( "arch", "freebsd i386" );
    360 #elif defined __alpha__
    361   Cvar_Set( "arch", "freebsd alpha" );
    362 #else
    363   Cvar_Set( "arch", "freebsd unknown" );
    364 #endif // FreeBSD
    365 
    366 #else
    367   Cvar_Set( "arch", "linux unknown" );
    368 #endif
    369 #elif defined __sun__
    370 #if defined __i386__
    371   Cvar_Set( "arch", "solaris x86" );
    372 #elif defined __sparc__
    373   Cvar_Set( "arch", "solaris sparc" );
    374 #else
    375   Cvar_Set( "arch", "solaris unknown" );
    376 #endif
    377 #elif defined __sgi__
    378 #if defined __mips__
    379   Cvar_Set( "arch", "sgi mips" );
    380 #else
    381   Cvar_Set( "arch", "sgi unknown" );
    382 #endif
    383 #else
    384   Cvar_Set( "arch", "unknown" );
    385 #endif
    386 
    387   Cvar_Set( "username", Sys_GetCurrentUser() );
    388 
    389   IN_Init();
    390 
    391 }
    392 
    393 void  Sys_Error( const char *error, ...)
    394 { 
    395   va_list     argptr;
    396   char        string[1024];
    397 
    398   // change stdin to non blocking
    399   // NOTE TTimo not sure how well that goes with tty console mode
    400   fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY);
    401 
    402   // don't bother do a show on this one heh
    403   if (ttycon_on)
    404   {
    405     tty_Hide();
    406   }
    407 
    408   CL_Shutdown ();
    409 
    410   va_start (argptr,error);
    411   vsprintf (string,error,argptr);
    412   va_end (argptr);
    413   fprintf(stderr, "Sys_Error: %s\n", string);
    414 
    415   Sys_Exit( 1 ); // bk010104 - use single exit point.
    416 } 
    417 
    418 void Sys_Warn (char *warning, ...)
    419 { 
    420   va_list     argptr;
    421   char        string[1024];
    422 
    423   va_start (argptr,warning);
    424   vsprintf (string,warning,argptr);
    425   va_end (argptr);
    426 
    427   if (ttycon_on)
    428   {
    429     tty_Hide();
    430   }
    431 
    432   fprintf(stderr, "Warning: %s", string);
    433 
    434   if (ttycon_on)
    435   {
    436     tty_Show();
    437   }
    438 } 
    439 
    440 /*
    441 ============
    442 Sys_FileTime
    443 
    444 returns -1 if not present
    445 ============
    446 */
    447 int Sys_FileTime (char *path)
    448 {
    449   struct  stat  buf;
    450 
    451   if (stat (path,&buf) == -1)
    452     return -1;
    453 
    454   return buf.st_mtime;
    455 }
    456 
    457 void floating_point_exception_handler(int whatever)
    458 {
    459   signal(SIGFPE, floating_point_exception_handler);
    460 }
    461 
    462 // initialize the console input (tty mode if wanted and possible)
    463 void Sys_ConsoleInputInit()
    464 {
    465   struct termios tc;
    466 
    467   // TTimo 
    468   // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=390
    469   // ttycon 0 or 1, if the process is backgrounded (running non interactively)
    470   // then SIGTTIN or SIGTOU is emitted, if not catched, turns into a SIGSTP
    471   signal(SIGTTIN, SIG_IGN);
    472   signal(SIGTTOU, SIG_IGN);
    473 
    474   // FIXME TTimo initialize this in Sys_Init or something?
    475   ttycon = Cvar_Get("ttycon", "1", 0);
    476   if (ttycon && ttycon->value)
    477   {
    478     if (isatty(STDIN_FILENO)!=1)
    479     {
    480       Com_Printf("stdin is not a tty, tty console mode failed\n");
    481       Cvar_Set("ttycon", "0");
    482       ttycon_on = qfalse;
    483       return;
    484     }
    485     Com_Printf("Started tty console (use +set ttycon 0 to disable)\n");
    486     Field_Clear(&tty_con);
    487     tcgetattr (0, &tty_tc);
    488     tty_erase = tty_tc.c_cc[VERASE];
    489     tty_eof = tty_tc.c_cc[VEOF];
    490     tc = tty_tc;
    491     /*
    492      ECHO: don't echo input characters
    493      ICANON: enable canonical mode.  This  enables  the  special
    494               characters  EOF,  EOL,  EOL2, ERASE, KILL, REPRINT,
    495               STATUS, and WERASE, and buffers by lines.
    496      ISIG: when any of the characters  INTR,  QUIT,  SUSP,  or
    497               DSUSP are received, generate the corresponding sig­
    498               nal
    499     */              
    500     tc.c_lflag &= ~(ECHO | ICANON);
    501     /*
    502      ISTRIP strip off bit 8
    503      INPCK enable input parity checking
    504      */
    505     tc.c_iflag &= ~(ISTRIP | INPCK);
    506     tc.c_cc[VMIN] = 1;
    507     tc.c_cc[VTIME] = 0;
    508     tcsetattr (0, TCSADRAIN, &tc);    
    509     ttycon_on = qtrue;
    510   } else
    511     ttycon_on = qfalse;
    512 }
    513 
    514 char *Sys_ConsoleInput(void)
    515 {
    516   // we use this when sending back commands
    517   static char text[256];
    518   int i;
    519   int avail;
    520   char key;
    521   field_t *history;
    522 
    523   if (ttycon && ttycon->value)
    524   {
    525     avail = read(0, &key, 1);
    526     if (avail != -1)
    527     {
    528       // we have something
    529       // backspace?
    530       // NOTE TTimo testing a lot of values .. seems it's the only way to get it to work everywhere
    531       if ((key == tty_erase) || (key == 127) || (key == 8))
    532       {
    533         if (tty_con.cursor > 0)
    534         {
    535           tty_con.cursor--;
    536           tty_con.buffer[tty_con.cursor] = '\0';
    537           tty_Back();
    538         }
    539         return NULL;
    540       }
    541       // check if this is a control char
    542       if ((key) && (key) < ' ')
    543       {
    544         if (key == '\n')
    545         {
    546           // push it in history
    547           Hist_Add(&tty_con);
    548           strcpy(text, tty_con.buffer);
    549           Field_Clear(&tty_con);
    550           key = '\n';
    551           write(1, &key, 1);
    552           return text;
    553         }
    554         if (key == '\t')
    555         {
    556           tty_Hide();
    557           Field_CompleteCommand( &tty_con );
    558           // Field_CompleteCommand does weird things to the string, do a cleanup
    559           //   it adds a '\' at the beginning of the string
    560           //   cursor doesn't reflect actual length of the string that's sent back
    561           tty_con.cursor = strlen(tty_con.buffer);
    562           if (tty_con.cursor>0)
    563           {
    564             if (tty_con.buffer[0] == '\\')
    565             {
    566               for (i=0; i<=tty_con.cursor; i++)
    567               {
    568                 tty_con.buffer[i] = tty_con.buffer[i+1];
    569               }
    570               tty_con.cursor--;
    571             }
    572           }
    573           tty_Show();
    574           return NULL;
    575         }
    576         avail = read(0, &key, 1);
    577         if (avail != -1)
    578         {
    579           // VT 100 keys
    580           if (key == '[' || key == 'O')
    581           {
    582             avail = read(0, &key, 1);
    583             if (avail != -1)
    584             {
    585               switch (key)
    586               {
    587               case 'A':
    588                 history = Hist_Prev();
    589                 if (history)
    590                 {
    591                   tty_Hide();
    592                   tty_con = *history;
    593                   tty_Show();
    594                 }
    595                 tty_FlushIn();
    596                 return NULL;
    597                 break;
    598               case 'B':
    599                 history = Hist_Next();
    600                 tty_Hide();
    601                 if (history)
    602                 {
    603                   tty_con = *history;
    604                 } else
    605                 {
    606                   Field_Clear(&tty_con);
    607                 }
    608                 tty_Show();
    609                 tty_FlushIn();
    610                 return NULL;
    611                 break;
    612               case 'C':
    613                 return NULL;
    614               case 'D':
    615                 return NULL;
    616               }
    617             }
    618           }
    619         }
    620         Com_DPrintf("droping ISCTL sequence: %d, tty_erase: %d\n", key, tty_erase);
    621         tty_FlushIn();
    622         return NULL;
    623       }
    624       // push regular character
    625       tty_con.buffer[tty_con.cursor] = key;
    626       tty_con.cursor++;
    627       // print the current line (this is differential)
    628       write(1, &key, 1);
    629     }
    630     return NULL;
    631   } else
    632   {
    633     int     len;
    634     fd_set  fdset;
    635     struct timeval timeout;
    636 
    637     if (!com_dedicated || !com_dedicated->value)
    638       return NULL;
    639 
    640     if (!stdin_active)
    641       return NULL;
    642 
    643     FD_ZERO(&fdset);
    644     FD_SET(0, &fdset); // stdin
    645     timeout.tv_sec = 0;
    646     timeout.tv_usec = 0;
    647     if (select (1, &fdset, NULL, NULL, &timeout) == -1 || !FD_ISSET(0, &fdset))
    648     {
    649       return NULL;
    650     }
    651 
    652     len = read (0, text, sizeof(text));
    653     if (len == 0)
    654     { // eof!
    655       stdin_active = qfalse;
    656       return NULL;
    657     }
    658 
    659     if (len < 1)
    660       return NULL;
    661     text[len-1] = 0;    // rip off the /n and terminate
    662 
    663     return text;
    664   }
    665 }
    666 
    667 /*****************************************************************************/
    668 
    669 /*
    670 =================
    671 Sys_UnloadDll
    672 
    673 =================
    674 */
    675 void Sys_UnloadDll( void *dllHandle ) {
    676   // bk001206 - verbose error reporting
    677   const char* err; // rb010123 - now const
    678   if ( !dllHandle )
    679   {
    680     Com_Printf("Sys_UnloadDll(NULL)\n");
    681     return;
    682   }
    683   dlclose( dllHandle );
    684   err = dlerror();
    685   if ( err != NULL )
    686     Com_Printf ( "Sys_UnloadGame failed on dlclose: \"%s\"!\n", err );
    687 }
    688 
    689 
    690 /*
    691 =================
    692 Sys_LoadDll
    693 
    694 Used to load a development dll instead of a virtual machine
    695 TTimo:
    696 changed the load procedure to match VFS logic, and allow developer use
    697 #1 look down current path
    698 #2 look in fs_homepath
    699 #3 look in fs_basepath
    700 =================
    701 */
    702 extern char   *FS_BuildOSPath( const char *base, const char *game, const char *qpath );
    703 
    704 void *Sys_LoadDll( const char *name, char *fqpath ,
    705                    int (**entryPoint)(int, ...),
    706                    int (*systemcalls)(int, ...) ) 
    707 {
    708   void *libHandle;
    709   void  (*dllEntry)( int (*syscallptr)(int, ...) );
    710   char  curpath[MAX_OSPATH];
    711   char  fname[MAX_OSPATH];
    712   char  *basepath;
    713   char  *homepath;
    714   char  *pwdpath;
    715   char  *gamedir;
    716   char  *fn;
    717   const char*  err = NULL;
    718 	
    719 	*fqpath = 0;
    720 
    721   // bk001206 - let's have some paranoia
    722   assert( name );
    723 
    724   getcwd(curpath, sizeof(curpath));
    725 #if defined __i386__
    726   snprintf (fname, sizeof(fname), "%si386.so", name);
    727 #elif defined __powerpc__   //rcg010207 - PPC support.
    728   snprintf (fname, sizeof(fname), "%sppc.so", name);
    729 #elif defined __axp__
    730   snprintf (fname, sizeof(fname), "%saxp.so", name);
    731 #elif defined __mips__
    732   snprintf (fname, sizeof(fname), "%smips.so", name);
    733 #else
    734 #error Unknown arch
    735 #endif
    736 
    737 // bk001129 - was RTLD_LAZY 
    738 #define Q_RTLD    RTLD_NOW
    739 
    740   pwdpath = Sys_Cwd();
    741   basepath = Cvar_VariableString( "fs_basepath" );
    742   homepath = Cvar_VariableString( "fs_homepath" );
    743   gamedir = Cvar_VariableString( "fs_game" );
    744 
    745   // pwdpath
    746   fn = FS_BuildOSPath( pwdpath, gamedir, fname );
    747   Com_Printf( "Sys_LoadDll(%s)... \n", fn );
    748   libHandle = dlopen( fn, Q_RTLD );
    749 
    750   if ( !libHandle )
    751   {
    752     Com_Printf( "Sys_LoadDll(%s) failed:\n\"%s\"\n", fn, dlerror() );
    753     // fs_homepath
    754     fn = FS_BuildOSPath( homepath, gamedir, fname );
    755     Com_Printf( "Sys_LoadDll(%s)... \n", fn );
    756     libHandle = dlopen( fn, Q_RTLD );
    757 
    758     if ( !libHandle )
    759     {
    760       Com_Printf( "Sys_LoadDll(%s) failed:\n\"%s\"\n", fn, dlerror() );
    761       // fs_basepath
    762       fn = FS_BuildOSPath( basepath, gamedir, fname );
    763       Com_Printf( "Sys_LoadDll(%s)... \n", fn );
    764       libHandle = dlopen( fn, Q_RTLD );
    765 
    766       if ( !libHandle )
    767       {
    768 #ifndef NDEBUG // bk001206 - in debug abort on failure
    769         Com_Error ( ERR_FATAL, "Sys_LoadDll(%s) failed dlopen() completely!\n", name  );
    770 #else
    771         Com_Printf ( "Sys_LoadDll(%s) failed dlopen() completely!\n", name );
    772 #endif
    773         return NULL;
    774       } else
    775         Com_Printf ( "Sys_LoadDll(%s): succeeded ...\n", fn );
    776     } else
    777       Com_Printf ( "Sys_LoadDll(%s): succeeded ...\n", fn );
    778   } else
    779     Com_Printf ( "Sys_LoadDll(%s): succeeded ...\n", fn ); 
    780 
    781   dllEntry = dlsym( libHandle, "dllEntry" ); 
    782   *entryPoint = dlsym( libHandle, "vmMain" );
    783   if ( !*entryPoint || !dllEntry )
    784   {
    785     err = dlerror();
    786 #ifndef NDEBUG // bk001206 - in debug abort on failure
    787     Com_Error ( ERR_FATAL, "Sys_LoadDll(%s) failed dlsym(vmMain):\n\"%s\" !\n", name, err );
    788 #else
    789     Com_Printf ( "Sys_LoadDll(%s) failed dlsym(vmMain):\n\"%s\" !\n", name, err );
    790 #endif
    791     dlclose( libHandle );
    792     err = dlerror();
    793     if ( err != NULL )
    794       Com_Printf ( "Sys_LoadDll(%s) failed dlcose:\n\"%s\"\n", name, err );
    795     return NULL;
    796   }
    797   Com_Printf ( "Sys_LoadDll(%s) found **vmMain** at  %p  \n", name, *entryPoint ); // bk001212
    798   dllEntry( systemcalls );
    799   Com_Printf ( "Sys_LoadDll(%s) succeeded!\n", name );
    800   if ( libHandle ) Q_strncpyz ( fqpath , fn , MAX_QPATH ) ;		// added 7/20/02 by T.Ray
    801   return libHandle;
    802 }
    803 
    804 /*
    805 ========================================================================
    806 
    807 BACKGROUND FILE STREAMING
    808 
    809 ========================================================================
    810 */
    811 
    812 #if 1
    813 
    814 void Sys_InitStreamThread( void ) {
    815 }
    816 
    817 void Sys_ShutdownStreamThread( void ) {
    818 }
    819 
    820 void Sys_BeginStreamedFile( fileHandle_t f, int readAhead ) {
    821 }
    822 
    823 void Sys_EndStreamedFile( fileHandle_t f ) {
    824 }
    825 
    826 int Sys_StreamedRead( void *buffer, int size, int count, fileHandle_t f ) {
    827   return FS_Read( buffer, size * count, f );
    828 }
    829 
    830 void Sys_StreamSeek( fileHandle_t f, int offset, int origin ) {
    831   FS_Seek( f, offset, origin );
    832 }
    833 
    834 #else
    835 
    836 typedef struct
    837 {
    838   fileHandle_t file;
    839   byte  *buffer;
    840   qboolean  eof;
    841   int   bufferSize;
    842   int   streamPosition; // next byte to be returned by Sys_StreamRead
    843   int   threadPosition; // next byte to be read from file
    844 } streamState_t;
    845 
    846 streamState_t stream;
    847 
    848 /*
    849 ===============
    850 Sys_StreamThread
    851 
    852 A thread will be sitting in this loop forever
    853 ================
    854 */
    855 void Sys_StreamThread( void ) 
    856 {
    857   int   buffer;
    858   int   count;
    859   int   readCount;
    860   int   bufferPoint;
    861   int   r;
    862 
    863   // if there is any space left in the buffer, fill it up
    864   if ( !stream.eof )
    865   {
    866     count = stream.bufferSize - (stream.threadPosition - stream.streamPosition);
    867     if ( count )
    868     {
    869       bufferPoint = stream.threadPosition % stream.bufferSize;
    870       buffer = stream.bufferSize - bufferPoint;
    871       readCount = buffer < count ? buffer : count;
    872       r = FS_Read ( stream.buffer + bufferPoint, readCount, stream.file );
    873       stream.threadPosition += r;
    874 
    875       if ( r != readCount )
    876         stream.eof = qtrue;
    877     }
    878   }
    879 }
    880 
    881 /*
    882 ===============
    883 Sys_InitStreamThread
    884 
    885 ================
    886 */
    887 void Sys_InitStreamThread( void ) 
    888 {
    889 }
    890 
    891 /*
    892 ===============
    893 Sys_ShutdownStreamThread
    894 
    895 ================
    896 */
    897 void Sys_ShutdownStreamThread( void ) 
    898 {
    899 }
    900 
    901 
    902 /*
    903 ===============
    904 Sys_BeginStreamedFile
    905 
    906 ================
    907 */
    908 void Sys_BeginStreamedFile( fileHandle_t f, int readAhead ) 
    909 {
    910   if ( stream.file )
    911   {
    912     Com_Error( ERR_FATAL, "Sys_BeginStreamedFile: unclosed stream");
    913   }
    914 
    915   stream.file = f;
    916   stream.buffer = Z_Malloc( readAhead );
    917   stream.bufferSize = readAhead;
    918   stream.streamPosition = 0;
    919   stream.threadPosition = 0;
    920   stream.eof = qfalse;
    921 }
    922 
    923 /*
    924 ===============
    925 Sys_EndStreamedFile
    926 
    927 ================
    928 */
    929 void Sys_EndStreamedFile( fileHandle_t f ) 
    930 {
    931   if ( f != stream.file )
    932   {
    933     Com_Error( ERR_FATAL, "Sys_EndStreamedFile: wrong file");
    934   }
    935 
    936   stream.file = 0;
    937   Z_Free( stream.buffer );
    938 }
    939 
    940 
    941 /*
    942 ===============
    943 Sys_StreamedRead
    944 
    945 ================
    946 */
    947 int Sys_StreamedRead( void *buffer, int size, int count, fileHandle_t f ) 
    948 {
    949   int   available;
    950   int   remaining;
    951   int   sleepCount;
    952   int   copy;
    953   int   bufferCount;
    954   int   bufferPoint;
    955   byte  *dest;
    956 
    957   dest = (byte *)buffer;
    958   remaining = size * count;
    959 
    960   if ( remaining <= 0 )
    961   {
    962     Com_Error( ERR_FATAL, "Streamed read with non-positive size" );
    963   }
    964 
    965   sleepCount = 0;
    966   while ( remaining > 0 )
    967   {
    968     available = stream.threadPosition - stream.streamPosition;
    969     if ( !available )
    970     {
    971       if (stream.eof)
    972         break;
    973       Sys_StreamThread();
    974       continue;
    975     }
    976 
    977     bufferPoint = stream.streamPosition % stream.bufferSize;
    978     bufferCount = stream.bufferSize - bufferPoint;
    979 
    980     copy = available < bufferCount ? available : bufferCount;
    981     if ( copy > remaining )
    982     {
    983       copy = remaining;
    984     }
    985     memcpy( dest, stream.buffer + bufferPoint, copy );
    986     stream.streamPosition += copy;
    987     dest += copy;
    988     remaining -= copy;
    989   }
    990 
    991   return(count * size - remaining) / size;
    992 }
    993 
    994 /*
    995 ===============
    996 Sys_StreamSeek
    997 
    998 ================
    999 */
   1000 void Sys_StreamSeek( fileHandle_t f, int offset, int origin ) {
   1001   // clear to that point
   1002   FS_Seek( f, offset, origin );
   1003   stream.streamPosition = 0;
   1004   stream.threadPosition = 0;
   1005   stream.eof = qfalse;
   1006 }
   1007 
   1008 #endif
   1009 
   1010 /*
   1011 ========================================================================
   1012 
   1013 EVENT LOOP
   1014 
   1015 ========================================================================
   1016 */
   1017 
   1018 // bk000306: upped this from 64
   1019 #define	MAX_QUED_EVENTS		256
   1020 #define	MASK_QUED_EVENTS	( MAX_QUED_EVENTS - 1 )
   1021 
   1022 sysEvent_t  eventQue[MAX_QUED_EVENTS];
   1023 // bk000306: initialize
   1024 int   eventHead = 0;
   1025 int             eventTail = 0;
   1026 byte    sys_packetReceived[MAX_MSGLEN];
   1027 
   1028 /*
   1029 ================
   1030 Sys_QueEvent
   1031 
   1032 A time of 0 will get the current time
   1033 Ptr should either be null, or point to a block of data that can
   1034 be freed by the game later.
   1035 ================
   1036 */
   1037 void Sys_QueEvent( int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr ) {
   1038   sysEvent_t  *ev;
   1039 
   1040   ev = &eventQue[ eventHead & MASK_QUED_EVENTS ];
   1041 
   1042   // bk000305 - was missing
   1043   if ( eventHead - eventTail >= MAX_QUED_EVENTS )
   1044   {
   1045     Com_Printf("Sys_QueEvent: overflow\n");
   1046     // we are discarding an event, but don't leak memory
   1047     if ( ev->evPtr )
   1048     {
   1049       Z_Free( ev->evPtr );
   1050     }
   1051     eventTail++;
   1052   }
   1053 
   1054   eventHead++;
   1055 
   1056   if ( time == 0 )
   1057   {
   1058     time = Sys_Milliseconds();
   1059   }
   1060 
   1061   ev->evTime = time;
   1062   ev->evType = type;
   1063   ev->evValue = value;
   1064   ev->evValue2 = value2;
   1065   ev->evPtrLength = ptrLength;
   1066   ev->evPtr = ptr;
   1067 }
   1068 
   1069 /*
   1070 ================
   1071 Sys_GetEvent
   1072 
   1073 ================
   1074 */
   1075 sysEvent_t Sys_GetEvent( void ) {
   1076   sysEvent_t  ev;
   1077   char    *s;
   1078   msg_t   netmsg;
   1079   netadr_t  adr;
   1080 
   1081   // return if we have data
   1082   if ( eventHead > eventTail )
   1083   {
   1084     eventTail++;
   1085     return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ];
   1086   }
   1087 
   1088   // pump the message loop
   1089   // in vga this calls KBD_Update, under X, it calls GetEvent
   1090   Sys_SendKeyEvents ();
   1091 
   1092   // check for console commands
   1093   s = Sys_ConsoleInput();
   1094   if ( s )
   1095   {
   1096     char  *b;
   1097     int   len;
   1098 
   1099     len = strlen( s ) + 1;
   1100     b = Z_Malloc( len );
   1101     strcpy( b, s );
   1102     Sys_QueEvent( 0, SE_CONSOLE, 0, 0, len, b );
   1103   }
   1104 
   1105   // check for other input devices
   1106   IN_Frame();
   1107 
   1108   // check for network packets
   1109   MSG_Init( &netmsg, sys_packetReceived, sizeof( sys_packetReceived ) );
   1110   if ( Sys_GetPacket ( &adr, &netmsg ) )
   1111   {
   1112     netadr_t    *buf;
   1113     int       len;
   1114 
   1115     // copy out to a seperate buffer for qeueing
   1116     len = sizeof( netadr_t ) + netmsg.cursize;
   1117     buf = Z_Malloc( len );
   1118     *buf = adr;
   1119     memcpy( buf+1, netmsg.data, netmsg.cursize );
   1120     Sys_QueEvent( 0, SE_PACKET, 0, 0, len, buf );
   1121   }
   1122 
   1123   // return if we have data
   1124   if ( eventHead > eventTail )
   1125   {
   1126     eventTail++;
   1127     return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ];
   1128   }
   1129 
   1130   // create an empty event to return
   1131 
   1132   memset( &ev, 0, sizeof( ev ) );
   1133   ev.evTime = Sys_Milliseconds();
   1134 
   1135   return ev;
   1136 }
   1137 
   1138 /*****************************************************************************/
   1139 
   1140 qboolean Sys_CheckCD( void ) {
   1141   return qtrue;
   1142 }
   1143 
   1144 void Sys_AppActivate (void)
   1145 {
   1146 }
   1147 
   1148 char *Sys_GetClipboardData(void)
   1149 {
   1150   return NULL;
   1151 }
   1152 
   1153 void  Sys_Print( const char *msg )
   1154 {
   1155   if (ttycon_on)
   1156   {
   1157     tty_Hide();
   1158   }
   1159   fputs(msg, stderr);
   1160   if (ttycon_on)
   1161   {
   1162     tty_Show();
   1163   }
   1164 }
   1165 
   1166 
   1167 void    Sys_ConfigureFPU() { // bk001213 - divide by zero
   1168 #ifdef __linux__
   1169 #ifdef __i386
   1170 #ifndef NDEBUG
   1171 
   1172   // bk0101022 - enable FPE's in debug mode
   1173   static int fpu_word = _FPU_DEFAULT & ~(_FPU_MASK_ZM | _FPU_MASK_IM);
   1174   int current = 0;
   1175   _FPU_GETCW(current);
   1176   if ( current!=fpu_word)
   1177   {
   1178 #if 0
   1179     Com_Printf("FPU Control 0x%x (was 0x%x)\n", fpu_word, current );
   1180     _FPU_SETCW( fpu_word );
   1181     _FPU_GETCW( current );
   1182     assert(fpu_word==current);
   1183 #endif
   1184   }
   1185 #else // NDEBUG
   1186   static int fpu_word = _FPU_DEFAULT;
   1187   _FPU_SETCW( fpu_word );
   1188 #endif // NDEBUG
   1189 #endif // __i386 
   1190 #endif // __linux
   1191 }
   1192 
   1193 void Sys_PrintBinVersion( const char* name ) {
   1194   char* date = __DATE__;
   1195   char* time = __TIME__;
   1196   char* sep = "==============================================================";
   1197   fprintf( stdout, "\n\n%s\n", sep );
   1198 #ifdef DEDICATED
   1199   fprintf( stdout, "Linux Quake3 Dedicated Server [%s %s]\n", date, time );  
   1200 #else
   1201   fprintf( stdout, "Linux Quake3 Full Executable  [%s %s]\n", date, time );  
   1202 #endif
   1203   fprintf( stdout, " local install: %s\n", name );
   1204   fprintf( stdout, "%s\n\n", sep );
   1205 }
   1206 
   1207 void Sys_ParseArgs( int argc, char* argv[] ) {
   1208 
   1209   if ( argc==2 )
   1210   {
   1211     if ( (!strcmp( argv[1], "--version" ))
   1212          || ( !strcmp( argv[1], "-v" )) )
   1213     {
   1214       Sys_PrintBinVersion( argv[0] );
   1215       Sys_Exit(0);
   1216     }
   1217   }
   1218 }
   1219 
   1220 #include "../client/client.h"
   1221 extern clientStatic_t cls;
   1222 
   1223 int main ( int argc, char* argv[] )
   1224 {
   1225   // int 	oldtime, newtime; // bk001204 - unused
   1226   int   len, i;
   1227   char  *cmdline;
   1228   void Sys_SetDefaultCDPath(const char *path);
   1229 
   1230   // go back to real user for config loads
   1231   saved_euid = geteuid();
   1232   seteuid(getuid());
   1233 
   1234   Sys_ParseArgs( argc, argv );  // bk010104 - added this for support
   1235 
   1236   Sys_SetDefaultCDPath(argv[0]);
   1237 
   1238   // merge the command line, this is kinda silly
   1239   for (len = 1, i = 1; i < argc; i++)
   1240     len += strlen(argv[i]) + 1;
   1241   cmdline = malloc(len);
   1242   *cmdline = 0;
   1243   for (i = 1; i < argc; i++)
   1244   {
   1245     if (i > 1)
   1246       strcat(cmdline, " ");
   1247     strcat(cmdline, argv[i]);
   1248   }
   1249 
   1250   // bk000306 - clear queues
   1251   memset( &eventQue[0], 0, MAX_QUED_EVENTS*sizeof(sysEvent_t) ); 
   1252   memset( &sys_packetReceived[0], 0, MAX_MSGLEN*sizeof(byte) );
   1253 
   1254   Com_Init(cmdline);
   1255   NET_Init();
   1256 
   1257   Sys_ConsoleInputInit();
   1258 
   1259   fcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | FNDELAY);
   1260 	
   1261 #ifdef DEDICATED
   1262 	// init here for dedicated, as we don't have GLimp_Init
   1263 	InitSig();
   1264 #endif
   1265 
   1266   while (1)
   1267   {
   1268 #ifdef __linux__
   1269     Sys_ConfigureFPU();
   1270 #endif
   1271     Com_Frame ();
   1272   }
   1273 }